diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 26af4ee9..7460c5f0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -99,6 +99,10 @@ jobs: - name: Compiler, CLI and declaration integrations if: matrix.os != 'ubuntu-latest' || matrix.node != 22 run: npm run test:run -- --selectProjects build types --ci --maxWorkers=2 + - name: Override builds in isolated sessions + run: npm run test:run -- --selectProjects build --testPathPatterns=tests/integration/build/override --ci --maxWorkers=2 + env: + ADNBN_OVERRIDE_BUILD_MODE: session - name: Upload build coverage if: matrix.os == 'ubuntu-latest' && matrix.node == 22 uses: actions/upload-artifact@v4 @@ -126,7 +130,7 @@ jobs: - name: Install dependencies run: npm ci - name: Build package, prepare and typecheck every application - run: npm run typecheck:integration + run: npm run typecheck:fixtures browser: name: Browser (${{ matrix.browser }}, Node ${{ matrix.node }}) @@ -168,6 +172,15 @@ jobs: env: ADNBN_CHROME_BIN: ${{ steps.chrome.outputs.chrome-path }} ADNBN_FIREFOX_BIN: ${{ steps.firefox.outputs.firefox-path }} + - name: Upload browser startup timings + if: always() + uses: actions/upload-artifact@v4 + with: + name: browser-startup-${{ matrix.browser }}-node-${{ matrix.node }} + path: .cache/integration/browser-startup.json + include-hidden-files: true + if-no-files-found: ignore + retention-days: 7 coverage: name: Coverage (ubuntu-latest, Node 22) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d77d553d..785f6b91 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -111,12 +111,13 @@ jobs: run: node .github/scripts/releaseAutomation.cjs run - name: Sync main → develop - uses: devmasx/merge-branch@v1.4.0 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: - type: now - from_branch: main - target_branch: develop - message: "chore(sync): merge main into develop" - github_token: ${{ secrets.GITHUB_TOKEN }} - env: - HUSKY: 0 + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + await github.rest.repos.merge({ + ...context.repo, + base: "develop", + head: "main", + commit_message: "chore(sync): merge main into develop", + }); diff --git a/.husky/pre-push b/.husky/pre-push index ec7e6fe2..76299cc2 100644 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1,4 +1,4 @@ #!/usr/bin/env sh # Browser scenarios and coverage run in CI; build the package only once here. -npm run test:pre-push +npm run check diff --git a/.prettierignore b/.prettierignore index fb92500f..418e7b6e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -4,4 +4,6 @@ dist/ /build/ addon/ src/cli/entrypoint/file/tests/fixtures/ +src/cli/plugins/style/tests/fixtures/ +tests/integration/build/style/fixtures/ CHANGELOG.md diff --git a/AGENTS.md b/AGENTS.md index 1a1cc1aa..4d60b31e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,6 +4,14 @@ - `CHANGELOG.md` is generated by the release pipeline during publication. Do not edit it manually; document behavior and migration guidance in the relevant documentation instead. +## Production code changes during test work + +- Authorization to add, migrate, refactor, or accelerate tests does not authorize changes to production code, including CLI, build configuration, resolvers, plugins, public contracts, or resource lifecycles. Do not change working production behavior merely to accommodate a test runner, fixture layout, parallel execution, or an experimental test backend. +- Distinguish a product defect from a limitation of the test setup. Before proposing a production fix, demonstrate how supported consumer usage violates the intended contract, independently of test-only assumptions. A failing experiment or a faster test run alone is not evidence of a product defect. Keep a confirmed fix within an already authorized scope; otherwise present the evidence and obtain agreement before changing production code. +- First use the existing public boundaries and realistic execution model in tests. Prefer properly isolated fixtures, processes, and resources owned by the test session. Preserve scenario coverage and observable behavior; do not hide failures with private implementation access, hidden global state, arbitrary retries, or special production branches for tests. +- If a sound test-layer solution is unavailable, stop the affected implementation and propose alternatives before editing production code. Explain the ownership boundary, consumer impact, lifecycle and cleanup, concurrency and platform constraints, tradeoffs, and validation. Choose established mechanisms that fit this codebase; neither novelty nor passing tests justifies an architectural workaround. +- Treat newly discovered production changes as a separate scope decision, even inside an approved test optimization pilot. Agreement on a product fix does not authorize unrelated infrastructure redesign. Keep product fixes and test-only changes separately reviewable. + ## Cross-platform tests - The full CI matrix in `.github/workflows/ci.yml` runs on Linux and Windows with Node.js 22 and 24. New and changed non-browser tests must work on both operating systems. Chrome and Firefox browser integrations currently run on Linux; consult the workflow for the current matrix. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 309caaa1..1beaed45 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -73,6 +73,13 @@ Releases and versioning: - We use SemVer. Commit types help determine version bumps (`feat` → minor, `fix` → patch, `BREAKING CHANGE` → major). - Package version is updated by maintainers within release/hotfix branches. +Run `npm run release:preview` to inspect the proposed version and changelog from a feature branch, +even with a dirty working tree. This is a dry run: it does not update files, create commits or tags, push, +or publish to GitHub/npm. Only committed changes contribute to the proposed release notes and version. +The preview uses the read-only `--release-version` and `--changelog` modes, which exit before the version +update stage. `--dry-run` alone is insufficient with the installed release-it version: its npm plugin still +executes `npm version`. The preview's relaxed checks do not change the actual release restrictions. + ## How to contribute 1. Fork the repository and create a branch from `develop` (or `main` for `hotfix`): @@ -83,16 +90,10 @@ Releases and versioning: 3. Make changes and ensure commits follow Conventional Commits. 4. Before opening a PR, run local checks: - Formatting: `npm run format` - - Tests: `npm test` (the full suite includes a Chrome MV3 integration test; use `ADNBN_CHROME_BIN=/path/to/chrome` to override automatic Chrome discovery) - - By area (if needed): - - `npm run test:relay` - - `npm run test:service` - - `npm run test:storage` - - `npm run test:message` - - `npm run test:locale` - - `npm run test:manifest` - - `npm run test:entrypoint` - - `npm run test:plugins` + - Non-browser checks: `npm run check` (framework types, package build, non-browser tests and fixture typechecks). + - Full tests: `npm test` (includes Chrome and Firefox; use `ADNBN_CHROME_BIN` and `ADNBN_FIREFOX_BIN` to override discovery). + - Focused units: `npm run test:unit -- --testPathPatterns=src/locale` or `npm run test:unit -- --runTestsByPath src/sandbox/SandboxMessage.test.ts`. + - See [tests/README.md](tests/README.md) for test groups, watch mode, coverage and fixture commands. 5. Open a PR to the appropriate branch (usually `develop`) and include: - motivation and solution; - alternatives and trade-offs (if any); diff --git a/jest.config.ts b/jest.config.ts index 1e515f0f..02896a3b 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -4,8 +4,12 @@ import {availableParallelism} from "node:os"; const shared: Config = { rootDir: import.meta.dirname, testEnvironment: "node", + // This function is serialized into browser/VM realms; Istanbul closures cannot cross that boundary. + coveragePathIgnorePatterns: ["/node_modules/", "/src/relay/adapters/invoke-relay\\.ts$"], + globals: {ADNBN_TEST_ROOT: import.meta.dirname}, - setupFiles: ["/tests/jest.setup.ts"], + setupFilesAfterEnv: ["/tests/jest.setup.ts"], + transformIgnorePatterns: ["/node_modules/(?!(@addon-core/storage|nanoid)/)"], modulePathIgnorePatterns: ["/.cache/"], resolver: "/tests/raw-module-resolver.cjs", moduleNameMapper: { @@ -24,6 +28,7 @@ const shared: Config = { "^@transport/(.*)$": "/src/transport/$1", "^@main/(.*)$": "/src/main/$1", "^@typing/(.*)$": "/src/types/$1", + "^@tests/(.*)$": "/tests/$1", }, extensionsToTreatAsEsm: [".ts", ".tsx"], transform: { @@ -45,7 +50,7 @@ const shared: Config = { const buildTests = [ "/src/cli/bundler/plugins/**/*.test.ts", - "/src/cli/index.test.ts", + "/src/cli/builders/app/**/*.test.ts", "/src/cli/virtual/virtual.test.ts", "/src/cli/plugins/content/RelayDeclaration.test.ts", "/src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts", @@ -53,6 +58,7 @@ const buildTests = [ "/tests/integration/build/**/*.test.ts", ]; const domTests = [ + "/tests/browser-harness-dom.test.ts", "/src/entry/**/*.test.ts", "/src/frame/**/*.test.ts", "/src/sandbox/providers/**/*.test.ts", @@ -65,6 +71,7 @@ const exclude = (patterns: string[]) => const config: Config = { verbose: false, + reporters: ["default", "/tests/BrowserStartupReporter.cjs"], maxWorkers: Math.max(1, Math.min(8, availableParallelism() - 1)), coverageProvider: "babel", projects: [ @@ -91,7 +98,7 @@ const config: Config = { { ...shared, displayName: "chrome", - setupFiles: [], + setupFilesAfterEnv: [], testMatch: [ "/tests/integration/browser/**/*.integration.test.ts", ...exclude(["/tests/integration/browser/**/*.firefox.integration.test.ts"]), @@ -100,7 +107,7 @@ const config: Config = { { ...shared, displayName: "firefox", - setupFiles: [], + setupFilesAfterEnv: [], testMatch: ["/tests/integration/browser/**/*.firefox.integration.test.ts"], }, ], diff --git a/package-lock.json b/package-lock.json index c12c83c7..76ca3b62 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,9 +10,9 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "@addon-core/browser": "^0.7.2", + "@addon-core/browser": "^0.9.0", "@addon-core/inject-script": "^0.5.0", - "@addon-core/storage": "^0.7.0", + "@addon-core/storage": "^0.8.0", "@rsdoctor/rspack-plugin": "^1.5.1", "@rspack/cli": "^1.7.5", "@rspack/core": "^1.7.5", @@ -38,7 +38,6 @@ "sass": "^1.83.4", "sass-loader": "^16.0.4", "semver": "^7.7.3", - "source-modifier-loader": "^1.0.2", "tsconfig-paths": "^4.2.0", "type-fest": "^4.37.0", "typescript": "^5.7.3", @@ -81,7 +80,6 @@ "istanbul-reports": "^3.2.0", "jest": "^30.2.0", "jest-environment-jsdom": "^30.0.0", - "jest-webextension-mock": "^4.0.0", "prettier": "^3.6.2", "react": "^19.2.8", "react-dom": "^19.2.8", @@ -129,18 +127,18 @@ "license": "MIT" }, "node_modules/@addon-core/browser": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@addon-core/browser/-/browser-0.7.2.tgz", - "integrity": "sha512-noDIPQktJOl7HVFUBZONL1iAVK4Spq1K+JgtN3OTsNPSvkgogrWUvpjq00p99R02y+YWORR70gu3tR+NDh09vw==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@addon-core/browser/-/browser-0.9.0.tgz", + "integrity": "sha512-EiZnYVOZoYi49+RqOQKBPYnYSNtUtjyVDn3rrKZD3/PF0hU2vGShioP7VbeqP1pL4eX1ZrpWkbBkaNHwwf9s+A==", "license": "MIT", "dependencies": { - "@types/chrome": "^0.2.2" + "@types/chrome": "^0.3.0" } }, "node_modules/@addon-core/browser/node_modules/@types/chrome": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.2.7.tgz", - "integrity": "sha512-9kjBozQ+jyDVt1eai3VZqjHDTN95JCuRmbRHOryOltBJmzrTmUw/9r/DznmjRaQ8WlyIfeCA4WNzoj0IvormJA==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.3.0.tgz", + "integrity": "sha512-HPAMKyJwUnNJdVsxgypwvIStlwRhhaLnxgEwKIB99oOcffJrC4UMeD5xseVoBwDinSkr690DCBaAtSYdK6M1qw==", "license": "MIT", "dependencies": { "@types/filesystem": "*", @@ -156,21 +154,41 @@ "@addon-core/browser": "^0.7.2" } }, + "node_modules/@addon-core/inject-script/node_modules/@addon-core/browser": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@addon-core/browser/-/browser-0.7.2.tgz", + "integrity": "sha512-noDIPQktJOl7HVFUBZONL1iAVK4Spq1K+JgtN3OTsNPSvkgogrWUvpjq00p99R02y+YWORR70gu3tR+NDh09vw==", + "license": "MIT", + "dependencies": { + "@types/chrome": "^0.2.2" + } + }, + "node_modules/@addon-core/inject-script/node_modules/@types/chrome": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.2.9.tgz", + "integrity": "sha512-6rXrDPkkWv4D2Q23HqT+iED4voODU5CpOSc2VFgKyecSFItuEsIBvz9CY6jUQ3dd+Q7zo1bnCE9pKRMtkYut5g==", + "license": "MIT", + "dependencies": { + "@types/filesystem": "*", + "@types/har-format": "*" + } + }, "node_modules/@addon-core/storage": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@addon-core/storage/-/storage-0.7.0.tgz", - "integrity": "sha512-MINCKg6sMKKeqRUnhmfPLveYBUwrvDrEvSmZgo+7QMxD0bWrbdmbO8Eb15DJHzNF/dX3M/CBwW4dxSzZXr03Fg==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@addon-core/storage/-/storage-0.8.0.tgz", + "integrity": "sha512-2luHeyty/r64XJh7Jh81kEs3wKUq3VdRGK8Aw2oO4OpBd2YI3AZSZIdrG1+6fEyaYaQTsdaGr0D+Cve8FiT/yQ==", "license": "MIT", "dependencies": { - "@addon-core/browser": "^0.5.0", + "@addon-core/browser": "^0.9.0", "dequal": "^2.0.3" }, "peerDependencies": { "@types/chrome": "*", - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0", + "typescript": ">=5.4" }, "peerDependenciesMeta": { "@types/react": { @@ -178,18 +196,12 @@ }, "@types/react-dom": { "optional": true + }, + "typescript": { + "optional": true } } }, - "node_modules/@addon-core/storage/node_modules/@addon-core/browser": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@addon-core/browser/-/browser-0.5.0.tgz", - "integrity": "sha512-xFi/dFcJYNblW+esBkJ2D5ind6Yydcj3yJnwVocILjDuV1oyxD9Qlh+K3FJnRprwdlqhxeo/E06KFoS6lWSO+g==", - "license": "MIT", - "peerDependencies": { - "@types/chrome": "*" - } - }, "node_modules/@adobe/css-tools": { "version": "4.4.4", "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", @@ -14729,13 +14741,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/jest-webextension-mock": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/jest-webextension-mock/-/jest-webextension-mock-4.1.1.tgz", - "integrity": "sha512-smCGBtl+5MpjKnJhyjCeaL/ZtdUa0T41tbHsEdb1nY3UEVogbYvehVw2MuMSvysFD3wNv4iYSIEgAwnR3H0YIw==", - "dev": true, - "license": "ISC" - }, "node_modules/jest-worker": { "version": "30.3.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.3.0.tgz", @@ -18074,12 +18079,6 @@ "node": ">=0.10.0" } }, - "node_modules/source-modifier-loader": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-modifier-loader/-/source-modifier-loader-1.0.2.tgz", - "integrity": "sha512-XrNpNvB8+gACt1k4bIX5MFuwWZkPesnhoS+vsVhn4XoMD2luvvJMiMkHPkF2/qmCAbWw5vjKqo0pq0GoYXhPcA==", - "license": "MIT" - }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", diff --git a/package.json b/package.json index 75c1a695..cc43deab 100644 --- a/package.json +++ b/package.json @@ -31,9 +31,9 @@ "module": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ - "bin", + "bin/adnbn.js", "dist", - "scripts", + "scripts/check-node-version.js", "types" ], "engines": { @@ -114,39 +114,28 @@ "prepublishOnly": "npm run build", "build": "node ./scripts/clean-dist.js && tsup && tsc -p tsconfig.build.json && node ./scripts/copy-dts.js && tsc-alias -p tsconfig.build.json -f -fe .js", "format": "prettier --write .", - "typecheck": "tsc -p tsconfig.json --noEmit", - "prepare:integration": "npm run build && tsx tests/integration/prepare.ts", - "typecheck:integration": "npm run build && tsx tests/integration/typecheck.ts --prepare", - "test:integration": "npm run build && node scripts/test-run.mjs --selectProjects build types chrome firefox", - "test:integration:build": "npm run test:build --", + "check": "tsx tests/check.ts", "check:node-version": "node ./scripts/check-node-version.js", + "typecheck": "tsc -p tsconfig.json --noEmit", + "typecheck:fixtures": "npm run build && tsx tests/integration/typecheck.ts --prepare", + "fixtures:prepare": "npm run build && tsx tests/integration/prepare.ts", "test": "npm run build && node scripts/test-run.mjs", - "test:ci": "npm run test -- --ci --coverage", - "test:ci:non-browser": "npm run test -- --ci --coverage --selectProjects unit-node unit-dom build types", - "test:watch": "node scripts/test-run.mjs --selectProjects unit-node unit-dom --watch", - "test:offscreen": "node scripts/test-run.mjs --testPathPatterns=Offscreen", - "test:chrome": "npm run build && node scripts/test-run.mjs --selectProjects chrome", - "test:firefox": "npm run build && node scripts/test-run.mjs --selectProjects firefox", - "test:message": "node scripts/test-run.mjs --testPathPatterns=src/message", - "test:service": "node scripts/test-run.mjs --testPathPatterns=Service", - "test:relay": "npm run test -- --testPathPatterns=Relay", - "test:locale": "npm run test -- --testPathPatterns=Locale", - "test:manifest": "node scripts/test-run.mjs --testPathPatterns=src/cli/builders/manifest", - "test:entrypoint": "node scripts/test-run.mjs --testPathPatterns=src/cli/entrypoint", - "test:plugins": "npm run test -- --testPathPatterns=src/cli/plugins", - "release": "release-it", - "release:preview": "release-it --no-github.release --no-npm.publish --no-git.tag --ci", "test:unit": "node scripts/test-run.mjs --selectProjects unit-node unit-dom --verbose=false", - "test:build": "npm run build && node scripts/test-run.mjs --selectProjects build", - "test:types": "npm run typecheck:integration && node scripts/test-run.mjs --selectProjects types", + "test:watch": "node scripts/test-run.mjs --selectProjects unit-node unit-dom --watch", + "test:build": "npm run build && node scripts/test-run.mjs --selectProjects build --verbose=false", + "test:types": "npm run build && node scripts/test-run.mjs --selectProjects types --verbose=false", + "test:browser": "npm run build && node scripts/test-run.mjs --selectProjects chrome firefox --verbose=false", + "test:browser:chrome": "npm run build && node scripts/test-run.mjs --selectProjects chrome --verbose=false", + "test:browser:firefox": "npm run build && node scripts/test-run.mjs --selectProjects firefox --verbose=false", "test:inventory": "node scripts/test-inventory.mjs", - "test:pre-push": "tsx tests/pre-push.ts", - "test:run": "node scripts/test-run.mjs" + "test:run": "node scripts/test-run.mjs", + "release": "release-it", + "release:preview": "release-it --release-version --dry-run --no-git.requireBranch --no-git.requireCleanWorkingDir --no-git.push --no-github.release --no-npm.publish --ci && release-it --changelog --dry-run --no-git.requireBranch --no-git.requireCleanWorkingDir --no-git.push --no-github.release --no-npm.publish --ci" }, "dependencies": { - "@addon-core/browser": "^0.7.2", + "@addon-core/browser": "^0.9.0", "@addon-core/inject-script": "^0.5.0", - "@addon-core/storage": "^0.7.0", + "@addon-core/storage": "^0.8.0", "@rsdoctor/rspack-plugin": "^1.5.1", "@rspack/cli": "^1.7.5", "@rspack/core": "^1.7.5", @@ -172,7 +161,6 @@ "sass": "^1.83.4", "sass-loader": "^16.0.4", "semver": "^7.7.3", - "source-modifier-loader": "^1.0.2", "tsconfig-paths": "^4.2.0", "type-fest": "^4.37.0", "typescript": "^5.7.3", @@ -212,7 +200,6 @@ "istanbul-reports": "^3.2.0", "jest": "^30.2.0", "jest-environment-jsdom": "^30.0.0", - "jest-webextension-mock": "^4.0.0", "prettier": "^3.6.2", "react": "^19.2.8", "react-dom": "^19.2.8", diff --git a/scripts/test-inventory-utils.mjs b/scripts/test-inventory-utils.mjs new file mode 100644 index 00000000..be7350f6 --- /dev/null +++ b/scripts/test-inventory-utils.mjs @@ -0,0 +1,173 @@ +import assert from "node:assert/strict"; +import {readFileSync} from "node:fs"; +import path from "node:path"; +import ts from "typescript"; + +const parse = (file, source) => ts.createSourceFile(file, source, ts.ScriptTarget.Latest, true); +const walk = (node, visitor) => { + visitor(node); + ts.forEachChild(node, child => walk(child, visitor)); +}; +const sorted = values => [...values].sort(); + +export function findModuleMocks(source, file = "test.ts") { + const modules = new Set(); + + walk(parse(file, source), node => { + if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression)) { + return; + } + + const {expression, name} = node.expression; + const argument = node.arguments[0]; + + if ( + ts.isIdentifier(expression) && + expression.text === "jest" && + ["mock", "doMock", "setMock", "unstable_mockModule"].includes(name.text) && + argument && + ts.isStringLiteralLike(argument) && + (argument.text.startsWith("@addon-core/") || argument.text === "@main/env") + ) { + modules.add(argument.text); + } + }); + + return sorted(modules); +} + +export function findStaticState(source, file) { + const ast = parse(file, source); + const classes = {}; + + walk(ast, node => { + if (!ts.isClassDeclaration(node) || !node.name) { + return; + } + + const members = node.members.filter(member => + member.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.StaticKeyword) + ); + const fields = members.filter(ts.isPropertyDeclaration).map(member => member.name.getText(ast)); + const getInstance = members.some(member => member.name?.getText(ast) === "getInstance"); + + if (fields.length || getInstance) { + classes[node.name.text] = {file, fields, getInstance}; + } + }); + + return classes; +} + +export function verifyTestInfrastructure(root, testFiles, sourceFiles) { + const read = file => readFileSync(path.join(root, file), "utf8"); + const {localMocks} = JSON.parse(read("tests/browser-harness/local-mocks.json")); + + for (const file of Object.keys(localMocks)) { + assert(testFiles.has(file), `Stale local-mock entry: ${file}`); + } + + const actualMocks = {}; + + for (const file of testFiles) { + const modules = findModuleMocks(read(file), file); + + if (modules.length) { + actualMocks[file] = modules; + } + } + + assert.deepEqual( + actualMocks, + localMocks, + "Module-mock inventory changed; migrate mocks or explicitly review exceptions" + ); + console.info(`Local module mocks: ${Object.keys(actualMocks).length} files`); + + for (const file of sorted(Object.keys(actualMocks))) { + console.info(` ${file}: ${actualMocks[file].join(", ")}`); + } + + const declared = JSON.parse(read("tests/browser-harness/framework-state-inventory.json")); + const discovered = {}; + const globalKeys = new Set(); + + for (const file of sourceFiles.filter( + file => + file.startsWith("src/") && + /\.tsx?$/.test(file) && + !file.endsWith(".d.ts") && + !/\.test\.tsx?$/.test(file) && + !file.includes("/tests/") + )) { + const source = read(file); + + for (const [name, owner] of Object.entries(findStaticState(source, file))) { + assert(!discovered[name], `Duplicate state-owner class name: ${name}`); + discovered[name] = owner; + } + + walk(parse(file, source), node => { + if ( + ts.isVariableStatement(node) && + node.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword) + ) { + for (const declaration of node.declarationList.declarations) { + if (ts.isIdentifier(declaration.name) && declaration.name.text.endsWith("GlobalKey")) { + globalKeys.add(declaration.name.text); + } + } + } + }); + } + + const expected = Object.fromEntries( + Object.entries(declared).map(([name, {retained, ...owner}]) => { + if (retained !== undefined) { + assert(typeof retained === "string" && retained.trim(), `Missing retention reason: ${name}`); + } + + return [name, owner]; + }) + ); + + assert.deepEqual( + discovered, + expected, + "Static state inventory changed; register its reset policy or explain retention" + ); + const resetSource = parse("framework-state.ts", read("tests/browser-harness/framework-state.ts")); + const resetOwners = new Set(); + const resetKeys = new Set(); + + walk(resetSource, node => { + if ( + ts.isVariableDeclaration(node) && + node.name.getText(resetSource) === "frameworkStateResetters" && + node.initializer && + ts.isObjectLiteralExpression(node.initializer) + ) { + for (const property of node.initializer.properties) { + resetOwners.add(property.name.getText(resetSource)); + } + } + + if (ts.isCallExpression(node)) { + for (const argument of node.arguments) { + if (ts.isIdentifier(argument) && argument.text.endsWith("GlobalKey")) { + resetKeys.add(argument.text); + } + } + } + }); + + assert.deepEqual( + sorted(resetOwners), + sorted(Object.keys(declared).filter(name => !declared[name].retained)), + "Every session-state owner must have a resetter" + ); + assert.deepEqual(sorted(resetKeys), sorted(globalKeys), "Every exported GlobalKey must be covered by reset"); + console.info( + `Framework state: ${resetOwners.size} reset owners, ${globalKeys.size} global keys, ${Object.keys(declared).length - resetOwners.size} documented retained owners` + ); +} diff --git a/scripts/test-inventory.mjs b/scripts/test-inventory.mjs index aa59fbc5..ac317a6f 100644 --- a/scripts/test-inventory.mjs +++ b/scripts/test-inventory.mjs @@ -3,6 +3,7 @@ import {execFile} from "node:child_process"; import {existsSync} from "node:fs"; import path from "node:path"; import {promisify} from "node:util"; +import {verifyTestInfrastructure} from "./test-inventory-utils.mjs"; const execute = promisify(execFile); const root = path.resolve(import.meta.dirname, ".."); @@ -36,3 +37,8 @@ for (const project of projects) { } assert.deepEqual(new Set(owners.keys()), expected, "Every test file must belong to exactly one Jest project"); console.info(`${expected.size} test files, each in exactly one project`); +verifyTestInfrastructure( + root, + expected, + tracked.split("\0").filter(file => file && existsSync(path.join(root, file))) +); diff --git a/src/cli/builders/app/build.test.ts b/src/cli/builders/app/build.test.ts new file mode 100644 index 00000000..c16a8a6e --- /dev/null +++ b/src/cli/builders/app/build.test.ts @@ -0,0 +1,179 @@ +import {rspack, Stats, type Compiler} from "@rspack/core"; +import {mkdtemp, readFile, rm} from "fs/promises"; +import os from "os"; +import path from "path"; + +import {BuildError, build} from "./build"; + +const fixtures = path.join(__dirname, "tests", "fixtures"); +let output: string; + +beforeEach(async () => { + output = await mkdtemp(path.join(os.tmpdir(), "adnbn-build-lifecycle-")); +}); + +afterEach(async () => { + await rm(output, {recursive: true, force: true}); +}); + +const createCompiler = (entry = "entry.js"): Compiler => { + return rspack({ + mode: "development", + context: fixtures, + entry: path.join(fixtures, entry), + output: {path: output, filename: "bundle.js"}, + }); +}; + +// A deliberately failing shutdown hook prevents Rspack from reaching native cleanup. +// It fails once; a second close in these tests releases the real compiler after checking the error. +const closeCompiler = (compiler: Compiler): Promise => { + return new Promise((resolve, reject) => { + compiler.close(error => { + if (error) { + reject(error); + } else { + resolve(); + } + }); + }); +}; + +test("returns the real Stats only after asynchronous compiler shutdown finishes", async () => { + const compiler = createCompiler(); + const closing = Promise.withResolvers(); + const release = Promise.withResolvers(); + let compilation: Stats | undefined; + let settled = false; + let closes = 0; + + compiler.hooks.done.tap("CaptureStats", stats => { + compilation = stats; + }); + compiler.hooks.shutdown.tapPromise("HoldShutdown", async () => { + closes++; + closing.resolve(); + await release.promise; + }); + + const result = build(compiler).then(stats => { + settled = true; + + return stats; + }); + + try { + await closing.promise; + expect(settled).toBe(false); + expect(compilation?.hasErrors()).toBe(false); + } finally { + release.resolve(); + } + + expect(await result).toBe(compilation); + expect(closes).toBe(1); + expect(await readFile(path.join(output, "bundle.js"), "utf8")).toContain("buildLifecycleFixture"); +}); + +test("closes a failed compilation and preserves its diagnostic Stats", async () => { + const compiler = createCompiler("missing-import.js"); + const closing = Promise.withResolvers(); + const release = Promise.withResolvers(); + let settled = false; + let closed = false; + + compiler.hooks.shutdown.tapPromise("ObserveShutdown", async () => { + closing.resolve(); + await release.promise; + closed = true; + }); + + const result = build(compiler).catch(error => { + settled = true; + + return error; + }); + + try { + await closing.promise; + expect(settled).toBe(false); + } finally { + release.resolve(); + } + + const error = await result; + + expect(error).toBeInstanceOf(BuildError); + expect(closed).toBe(true); + expect(error.stats).toBeInstanceOf(Stats); + expect(error.stats.hasErrors()).toBe(true); + expect(error.stats.toString({all: false, errors: true})).toContain("not-present.js"); +}); + +test("closes after a fatal plugin error and preserves the original cause", async () => { + const compiler = createCompiler(); + const failure = new Error("beforeRun failed"); + let closed = false; + + compiler.hooks.beforeRun.tap("FailBuild", () => { + throw failure; + }); + compiler.hooks.shutdown.tap("ObserveShutdown", () => { + closed = true; + }); + + await expect(build(compiler)).rejects.toMatchObject({name: "BuildError", cause: failure}); + expect(closed).toBe(true); +}); + +test("rejects a successful compilation when closing fails and retains Stats", async () => { + const compiler = createCompiler(); + const failure = new Error("shutdown failed"); + let closes = 0; + + compiler.hooks.shutdown.tap("FailFirstClose", () => { + if (++closes === 1) { + throw failure; + } + }); + + try { + const error = await build(compiler).catch(error => error); + + expect(error).toBeInstanceOf(BuildError); + expect(error.message).toBe("Rspack close error"); + expect(error.cause).toBe(failure); + expect(error.stats.hasErrors()).toBe(false); + expect(closes).toBe(1); + } finally { + await closeCompiler(compiler); + } +}); + +test("preserves both failures when compilation and shutdown fail", async () => { + const compiler = createCompiler(); + const compilationFailure = new Error("beforeRun failed"); + const closeFailure = new Error("shutdown failed"); + let closes = 0; + + compiler.hooks.beforeRun.tap("FailBuild", () => { + throw compilationFailure; + }); + compiler.hooks.shutdown.tap("FailFirstClose", () => { + if (++closes === 1) { + throw closeFailure; + } + }); + + try { + const error = await build(compiler).catch(error => error); + + expect(error).toBeInstanceOf(BuildError); + expect(error.message).toBe("Rspack compilation and close failed"); + expect(error.cause).toBeInstanceOf(AggregateError); + expect(error.cause.errors).toEqual([compilationFailure, closeFailure]); + expect(closes).toBe(1); + } finally { + await closeCompiler(compiler); + } +}); diff --git a/src/cli/builders/app/build.ts b/src/cli/builders/app/build.ts new file mode 100644 index 00000000..3ba9b6d1 --- /dev/null +++ b/src/cli/builders/app/build.ts @@ -0,0 +1,66 @@ +import type {Compiler, Stats} from "@rspack/core"; + +export class BuildError extends Error { + constructor( + message: string, + public readonly stats: Stats | undefined, + options?: ErrorOptions + ) { + super(message, options); + this.name = "BuildError"; + } +} + +/** Owns a one-shot compiler; settles only after its close callback, including failed builds. */ +export const build = async (compiler: Compiler): Promise => { + let stats: Stats | undefined; + let message = "Rspack compilation error"; + const failures: unknown[] = []; + + try { + await new Promise((resolve, reject) => { + compiler.run((error, result) => { + stats = result; + + if (error) { + reject(error); + } else { + resolve(); + } + }); + }); + + if (!stats) { + throw new Error("Rspack completed without compilation statistics"); + } + + if (stats.hasErrors()) { + throw new Error("Rspack reported compilation errors"); + } + } catch (error) { + failures.push(error); + } + + try { + await new Promise((resolve, reject) => { + compiler.close(error => { + if (error) { + reject(error); + } else { + resolve(); + } + }); + }); + } catch (error) { + message = failures.length ? "Rspack compilation and close failed" : "Rspack close error"; + failures.push(error); + } + + if (failures.length) { + throw new BuildError(message, stats, { + cause: failures.length === 1 ? failures[0] : new AggregateError(failures, message), + }); + } + + return stats!; +}; diff --git a/src/cli/builders/app/index.ts b/src/cli/builders/app/index.ts index 621c3ec9..7dae53b5 100644 --- a/src/cli/builders/app/index.ts +++ b/src/cli/builders/app/index.ts @@ -1,6 +1,9 @@ -import {rspack} from "@rspack/core"; +import path from "node:path"; -import {build, watch} from "./command"; +import {rspack, type Compiler, type Stats} from "@rspack/core"; + +import {build} from "./build"; +import {watch} from "./watch"; import configResolver from "@cli/resolvers/config"; import bundlerResolver from "@cli/resolvers/bundler"; @@ -9,30 +12,75 @@ import {processPluginHandler} from "@cli/resolvers/plugin"; import {OptionalConfig, ReadonlyConfig} from "@typing/config"; import {Command} from "@typing/app"; +export {BuildError} from "./build"; + const startup = async (config: ReadonlyConfig): Promise => { await Array.fromAsync(processPluginHandler(config.plugins, "startup", {config})); }; -export default async (config: OptionalConfig): Promise => { - const resolverConfig = await configResolver(config); +export type AppBuildConfig = Omit; - await startup(resolverConfig); +let buildActive = false; - const rspackConfig = await bundlerResolver(resolverConfig); +const createCompiler = async (config: ReadonlyConfig): Promise => { + await startup(config); - const compiler = rspack(rspackConfig); + const rspackConfig = await bundlerResolver(config); - switch (resolverConfig.command) { - case Command.Build: - build(compiler); - break; + return rspack(rspackConfig); +}; + +/** Sequential one-shot builds only; user config and plugins temporarily share the host environment. */ +export const buildApp = async (config: AppBuildConfig): Promise => { + if (buildActive) { + throw new Error("An app build is already running in this process; await it before starting another"); + } - case Command.Watch: - watch(compiler); - break; + const configFile = config.configFile ?? "adnbn.config.ts"; - default: - console.error("Unknown command"); - process.exit(1); + if (![".ts", ".mts", ".cts"].includes(path.extname(configFile))) { + throw new Error( + "buildApp requires a TypeScript config (.ts, .mts or .cts); use the CLI for other config formats" + ); } + + const environment = process.env; + const snapshot = {...environment}; + buildActive = true; + + try { + const resolved = await configResolver({...config, command: Command.Build}); + + if (resolved.command !== Command.Build) { + throw new Error("buildApp only supports the build command"); + } + + return await build(await createCompiler(resolved)); + } finally { + for (const key of Object.keys(environment)) { + if (!Object.hasOwn(snapshot, key)) { + delete environment[key]; + } + } + + Object.assign(environment, snapshot); + process.env = environment; + buildActive = false; + } +}; + +export default async (config: OptionalConfig): Promise => { + const resolverConfig = await configResolver(config); + + if (resolverConfig.command !== Command.Build && resolverConfig.command !== Command.Watch) { + throw new Error("Unknown command"); + } + + const compiler = await createCompiler(resolverConfig); + + if (resolverConfig.command === Command.Build) { + return await build(compiler); + } + + watch(compiler); }; diff --git a/src/cli/builders/app/tests/fixtures/entry.js b/src/cli/builders/app/tests/fixtures/entry.js new file mode 100644 index 00000000..ab9655b0 --- /dev/null +++ b/src/cli/builders/app/tests/fixtures/entry.js @@ -0,0 +1 @@ +globalThis.buildLifecycleFixture = "ready"; diff --git a/src/cli/builders/app/tests/fixtures/missing-import.js b/src/cli/builders/app/tests/fixtures/missing-import.js new file mode 100644 index 00000000..bfa2dfd4 --- /dev/null +++ b/src/cli/builders/app/tests/fixtures/missing-import.js @@ -0,0 +1 @@ +import "./not-present.js"; diff --git a/src/cli/builders/app/command.ts b/src/cli/builders/app/watch.ts similarity index 55% rename from src/cli/builders/app/command.ts rename to src/cli/builders/app/watch.ts index 415eb57d..8d720810 100644 --- a/src/cli/builders/app/command.ts +++ b/src/cli/builders/app/watch.ts @@ -1,34 +1,5 @@ import {Compiler} from "@rspack/core"; -export const build = (compiler: Compiler) => { - compiler.run((err, stats) => { - if (err) { - console.error("Rspack compilation error:", err); - process.exit(1); - } - - if (stats?.hasErrors()) { - console.error( - stats.toString({ - colors: true, - errors: true, - }) - ); - - process.exit(1); - } - - console.log(stats?.toString({colors: true})); - - compiler.close(closeErr => { - if (closeErr) { - console.error("Rspack close error:", closeErr); - process.exit(1); - } - }); - }); -}; - export const watch = (compiler: Compiler) => { const watching = compiler.watch( { diff --git a/src/cli/builders/manifest/Manifest.test.ts b/src/cli/builders/manifest/Manifest.test.ts index c26e4a92..86940a60 100644 --- a/src/cli/builders/manifest/Manifest.test.ts +++ b/src/cli/builders/manifest/Manifest.test.ts @@ -1,8 +1,10 @@ import ManifestV2 from "./ManifestV2"; import ManifestV3 from "./ManifestV3"; import {Browser, DataCollectionPermission} from "@typing/browser"; +import {CommandExecuteActionName} from "@typing/command"; +import type {ActionOptions} from "@typing/action"; import {Language} from "@typing/locale"; -import {ManifestIncognito, type OptionalManifest} from "@typing/manifest"; +import {ManifestIncognito, type ManifestPopup, type OptionalManifest} from "@typing/manifest"; describe("Manifest primitive properties", () => { it("name", () => { @@ -265,6 +267,139 @@ describe("Manifest common builder methods", () => { }); }); +describe.each([ + {version: 2, Builder: ManifestV2, field: "browser_action"}, + {version: 3, Builder: ManifestV3, field: "action"}, +] as const)("MV$version toolbar action", ({Builder, field}) => { + const createBuilder = () => + new Builder(Browser.Chrome) + .setName("Extension") + .setIcon("extension") + .setIcons( + new Map([ + ["default", new Map([[16, "icons/default.png"]])], + ["extension", new Map([[16, "icons/extension.png"]])], + ["action", new Map([[16, "icons/action.png"]])], + ["popup", new Map([[16, "icons/popup.png"]])], + ]) + ); + + it("does not declare a button for extension metadata or ordinary commands alone", () => { + const manifest = createBuilder() + .setCommands(new Set([{name: "open-settings", description: "Open settings"}])) + .build(); + + expect(manifest).not.toHaveProperty(field); + expect(manifest.icons).toEqual({16: "icons/extension.png"}); + }); + + it("declares configured appearance without a popup, command or background", () => { + const manifest = createBuilder().setAction({icon: "action", title: "Action"}).build(); + + expect(manifest).toHaveProperty(field, { + default_icon: {16: "icons/action.png"}, + default_title: "Action", + }); + expect(manifest).not.toHaveProperty(`${field}.default_popup`); + expect(manifest).not.toHaveProperty("commands"); + expect(manifest).not.toHaveProperty("background"); + expect(manifest.icons).toEqual({16: "icons/extension.png"}); + }); + + it("uses extension defaults for an explicit empty action", () => { + expect(createBuilder().setAction({}).build()).toHaveProperty(field, { + default_icon: {16: "icons/extension.png"}, + default_title: "Extension", + }); + }); + + it("uses extension defaults for an execute-action command", () => { + const manifest = createBuilder() + .setCommands(new Set([{name: CommandExecuteActionName}])) + .build(); + + expect(manifest).toHaveProperty(field, { + default_icon: {16: "icons/extension.png"}, + default_title: "Extension", + }); + }); + + it("uses configured appearance with an execute-action command", () => { + const manifest = createBuilder() + .setCommands(new Set([{name: CommandExecuteActionName}])) + .setAction({icon: "action", title: "Action"}) + .build(); + + expect(manifest).toHaveProperty(field, { + default_icon: {16: "icons/action.png"}, + default_title: "Action", + }); + }); + + it.each([ + {options: {}, title: "Action", icon: "action"}, + {options: {title: "Popup"}, title: "Popup", icon: "action"}, + {options: {icon: "popup"}, title: "Action", icon: "popup"}, + {options: {title: "Popup", icon: "popup"}, title: "Popup", icon: "popup"}, + ])("overrides popup fields independently: $options", ({options, title, icon}) => { + const action: ActionOptions = {icon: "action", title: "Action"}; + const popup: ManifestPopup = {path: "popup.html", ...options}; + + const before = createBuilder().setAction(action).setPopup(popup).build(); + const after = createBuilder().setPopup(popup).setAction(action).build(); + + expect(before).toHaveProperty(field, { + default_icon: {16: `icons/${icon}.png`}, + default_title: title, + default_popup: "popup.html", + }); + expect(after).toEqual(before); + }); + + it("uses extension defaults when the popup has no appearance overrides", () => { + expect(createBuilder().setPopup({path: "popup.html"}).build()).toHaveProperty(field, { + default_icon: {16: "icons/extension.png"}, + default_title: "Extension", + default_popup: "popup.html", + }); + }); + + it("preserves an explicitly empty tooltip", () => { + expect(createBuilder().setAction({title: ""}).build()).toHaveProperty(`${field}.default_title`, ""); + }); + + it("can declare a button without available icons", () => { + const manifest = new Builder(Browser.Chrome).setName("Extension").setAction({}).build(); + + expect(manifest).toHaveProperty(field, {default_title: "Extension"}); + }); + + it("keeps the existing default-group fallback for unknown icon groups", () => { + const manifest = createBuilder().setAction({icon: "missing"}).build(); + + expect(manifest).toHaveProperty(`${field}.default_icon`, {16: "icons/default.png"}); + }); + + it("clears popup overrides and explicit action settings without retaining stale data", () => { + const builder = createBuilder() + .setAction({icon: "action", title: "Action"}) + .setPopup({path: "popup.html", icon: "popup", title: "Popup"}); + + expect(builder.build()).toHaveProperty(`${field}.default_title`, "Popup"); + + builder.setPopup(undefined); + + expect(builder.build()).toHaveProperty(field, { + default_icon: {16: "icons/action.png"}, + default_title: "Action", + }); + + builder.setAction(undefined); + + expect(builder.build()).not.toHaveProperty(field); + }); +}); + describe.each([ ["ManifestV2", ManifestV2], ["ManifestV3", ManifestV3], diff --git a/src/cli/builders/manifest/ManifestBase.ts b/src/cli/builders/manifest/ManifestBase.ts index 4a28e719..8ee999c1 100644 --- a/src/cli/builders/manifest/ManifestBase.ts +++ b/src/cli/builders/manifest/ManifestBase.ts @@ -7,6 +7,7 @@ import {mergeWebAccessibleResources, normalizeDataCollectionPermissions} from ". import { CoreManifest, Manifest, + ManifestAction, ManifestAccessibleResource, ManifestAccessibleResources, ManifestBackground, @@ -36,6 +37,7 @@ import {CommandExecuteActionName} from "@typing/command"; import {DefaultIconGroupName} from "@typing/icon"; import {SidebarAlternativeBrowsers} from "@typing/sidebar"; import {ContentScriptMatches} from "@typing/content"; +import {ActionOptions} from "@typing/action"; type ManifestV3 = chrome.runtime.ManifestV3; type ManifestPermission = chrome.runtime.ManifestPermission; @@ -57,6 +59,7 @@ export default abstract class implements ManifestBuilder protected minimumVersion?: string; protected version?: string; protected icon?: string; + protected action?: ActionOptions; protected incognito?: ManifestIncognito; protected specific?: BrowserSpecific; protected locale?: Language; @@ -263,6 +266,12 @@ export default abstract class implements ManifestBuilder return this; } + public setAction(action?: ActionOptions): this { + this.action = action; + + return this; + } + public setSidebar(sidebar?: ManifestSidebar): this { this.sidebar = sidebar; @@ -816,6 +825,18 @@ export default abstract class implements ManifestBuilder return inInternalCommands || inOptionalCommands; } + protected getAction(): ManifestAction | undefined { + if (!this.action && !this.popup && !this.hasExecuteActionCommand()) { + return; + } + + return { + default_title: this.popup?.title ?? this.action?.title ?? this.name, + default_icon: this.getIconsByName(this.popup?.icon ?? this.action?.icon ?? this.icon), + ...(this.popup ? {default_popup: this.popup.path} : {}), + }; + } + protected getIconsByName(name?: string): CoreManifestIcons | undefined { if (this.icons.size === 0) { return; diff --git a/src/cli/builders/manifest/ManifestV2.ts b/src/cli/builders/manifest/ManifestV2.ts index 9bab70c9..09c49190 100644 --- a/src/cli/builders/manifest/ManifestV2.ts +++ b/src/cli/builders/manifest/ManifestV2.ts @@ -19,22 +19,10 @@ export default class extends ManifestBase { } protected buildAction(): Partial | undefined { - if (this.popup) { - const {icon, title, path} = this.popup; - - return { - browser_action: { - default_title: title || this.name, - default_popup: path, - default_icon: this.getIconsByName(icon), - }, - }; - } else if (this.hasExecuteActionCommand()) { - return { - browser_action: { - default_title: this.name, - }, - }; + const action = this.getAction(); + + if (action) { + return {browser_action: action}; } } diff --git a/src/cli/builders/manifest/ManifestV3.ts b/src/cli/builders/manifest/ManifestV3.ts index 0482a80f..bd3ca173 100644 --- a/src/cli/builders/manifest/ManifestV3.ts +++ b/src/cli/builders/manifest/ManifestV3.ts @@ -53,22 +53,10 @@ export default class extends ManifestBase { } protected buildAction(): Partial | undefined { - if (this.popup) { - const {path, icon, title} = this.popup; - - return { - action: { - default_title: title || this.name, - default_popup: path, - default_icon: this.getIconsByName(icon), - }, - }; - } else if (this.hasExecuteActionCommand()) { - return { - action: { - default_title: this.name, - }, - }; + const action = this.getAction(); + + if (action) { + return {action}; } } diff --git a/src/cli/bundler/plugins/entrypoint/EntrypointPlugin.ts b/src/cli/bundler/plugins/entrypoint/EntrypointPlugin.ts index b65bacf8..92a9fcb1 100644 --- a/src/cli/bundler/plugins/entrypoint/EntrypointPlugin.ts +++ b/src/cli/bundler/plugins/entrypoint/EntrypointPlugin.ts @@ -1,5 +1,6 @@ import _ from "lodash"; import path from "path"; +import {createHash} from "node:crypto"; import {Compiler, DynamicEntryPlugin, type EntryDescription, EntryNormalized} from "@rspack/core"; import {RspackVirtualModulePlugin as VirtualModulesPlugin} from "rspack-plugin-virtual-module"; @@ -46,6 +47,11 @@ export default class EntrypointPlugin { if (file.external) { const {ext} = path.parse(name); name = file.import + ext; + } else if (path.isAbsolute(name)) { + // A drive or UNC root cannot be embedded inside the virtual directory. + // Keep distinct absolute files distinct without carrying their root into it. + const key = createHash("sha256").update(name).digest("hex"); + name = path.join(key, path.basename(name)); } return path.join("virtual", name); diff --git a/src/cli/index.test.ts b/src/cli/index.test.ts deleted file mode 100644 index 2cc7785d..00000000 --- a/src/cli/index.test.ts +++ /dev/null @@ -1,75 +0,0 @@ -/** @jest-environment node */ - -import {spawnSync} from "child_process"; -import fs from "fs"; -import os from "os"; -import path from "path"; - -const cli = path.resolve(__dirname, "../../bin/adnbn.js"); -const fixtures = path.join(__dirname, "tests/fixtures/exit-code"); - -describe("CLI exit codes", () => { - let root: string; - let artifact: string; - - beforeEach(() => { - root = fs.mkdtempSync(path.join(os.tmpdir(), "adnbn-cli-exit-code-")); - artifact = path.join(root, "dist/exit-code-chrome-mv3"); - }); - - afterEach(() => { - fs.rmSync(root, {recursive: true, force: true}); - }); - - const run = (command: "build" | "watch", fixture: string) => { - fs.cpSync(path.join(fixtures, fixture), root, {recursive: true}); - - const result = spawnSync(process.execPath, [cli, command, root, "-a", "exit-code", "-b", "chrome"], { - cwd: root, - encoding: "utf8", - timeout: 30_000, - maxBuffer: 4 * 1024 * 1024, - }); - - if (result.error) { - throw result.error; - } - - expect(result.signal).toBeNull(); - - return {status: result.status, output: `${result.stdout}\n${result.stderr}`}; - }; - - test("build exits with 0 and emits an extension on success", () => { - const result = run("build", "success"); - - expect(result.status).toBe(0); - expect(JSON.parse(fs.readFileSync(path.join(artifact, "manifest.json"), "utf8"))).toMatchObject({ - manifest_version: 3, - version: "1.0.0", - default_locale: "en", - }); - expect(JSON.parse(fs.readFileSync(path.join(artifact, "_locales/fr/messages.json"), "utf8"))).toMatchObject({ - cart_items: {message: "article|articles"}, - locale: {message: "fr"}, - }); - }); - - test.each(["build", "watch"] as const)("%s exits with 1 when a plural key is missing at startup", command => { - const result = run(command, "missing-plural"); - - expect(result.output).toContain( - 'Locale "fr" is missing plural key "cart.items" required by default locale "en"' - ); - expect(result.status).toBe(1); - expect(fs.existsSync(path.join(artifact, "manifest.json"))).toBe(false); - }); - - test.each(["build", "watch"] as const)("%s exits with 1 when configuration is invalid", command => { - const result = run(command, "invalid-config"); - - expect(result.output).toContain('Invalid language "unsupported" provided by config'); - expect(result.status).toBe(1); - expect(fs.existsSync(path.join(artifact, "manifest.json"))).toBe(false); - }); -}); diff --git a/src/cli/index.ts b/src/cli/index.ts index 72e7fb3e..bbea1b82 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -2,7 +2,7 @@ import cac from "cac"; import {consola} from "consola"; import fs from "fs"; -import app from "./builders/app"; +import app, {BuildError} from "./builders/app"; import {Command} from "@typing/app"; import {Browser} from "@typing/browser"; @@ -51,7 +51,7 @@ cli.command("build [root]", "Build for production") .option("--analyze", "Visualize extension bundle") .action(async (root, options) => { try { - await app({ + const stats = await app({ command: Command.Build, mode: options.mode, debug: options.debug, @@ -62,8 +62,25 @@ cli.command("build [root]", "Build for production") configFile: options.config, analyze: options.analyze, }); + + if (stats) { + console.log(stats.toString({colors: true})); + } } catch (e) { - consola.error(e); + if (e instanceof BuildError) { + console.error(e.message); + + if (e.stats) { + console.error(e.stats.toString({colors: true, errors: true})); + } + + if (e.cause !== undefined) { + console.error(e.cause); + } + } else { + consola.error(e); + } + process.exitCode = 1; } }); diff --git a/src/cli/plugins/action.ts b/src/cli/plugins/action.ts new file mode 100644 index 00000000..50140fab --- /dev/null +++ b/src/cli/plugins/action.ts @@ -0,0 +1,11 @@ +import {definePlugin} from "@main/plugin"; +import {modifyLocaleMessageKey} from "@shared/locale"; + +export default definePlugin(() => ({ + name: "adnbn:action", + manifest: ({config, manifest}) => { + const {action} = config; + + manifest.setAction(action ? {icon: action.icon, title: modifyLocaleMessageKey(action.title)} : undefined); + }, +})); diff --git a/src/cli/plugins/content/RelayDeclaration.test.ts b/src/cli/plugins/content/RelayDeclaration.test.ts index 645628d8..5e9c035e 100644 --- a/src/cli/plugins/content/RelayDeclaration.test.ts +++ b/src/cli/plugins/content/RelayDeclaration.test.ts @@ -67,14 +67,6 @@ describe("Relay declarations", () => { }; const rootNames = [consumerFile, generatedPath]; - if (mode === "source") { - rootNames.push( - path.join(fixtureDir, "virtual.ts"), - path.join(projectDir, "src/cli/virtual/relay.ts"), - path.join(projectDir, "src/cli/virtual/virtual.d.ts") - ); - } - // Give the real generated declaration a consumer-side location so package self-resolution works. // The package case deliberately has neither source aliases nor ambient virtual module declarations. const host = ts.createCompilerHost(options); @@ -143,6 +135,7 @@ describe("Relay declarations", () => { "RelayFramesResult", "RelayMethod", "RelayNonEmptyReadonlyArray", + "RelayProtocolError", "RelayProxyTarget", "RelayResultTarget", "RelayScalarOptions", diff --git a/src/cli/plugins/content/tests/fixtures/relay-types/consumer.ts b/src/cli/plugins/content/tests/fixtures/relay-types/consumer.ts index a07580e3..9d55d85a 100644 --- a/src/cli/plugins/content/tests/fixtures/relay-types/consumer.ts +++ b/src/cli/plugins/content/tests/fixtures/relay-types/consumer.ts @@ -1,41 +1,9 @@ import * as api from "adnbn"; import * as local from "adnbn/relay"; import * as entry from "adnbn/entry/relay"; -import {Builder as ContentBuilder} from "adnbn/entry/content/vanilla"; - type Equal = (() => T extends A ? 1 : 2) extends () => T extends B ? 1 : 2 ? true : false; type Expect = T; -const definition = api.defineRelay({ - name: "scanner", - allFrames: api.RelayAllFrames.All, - method: api.RelayMethod.Scripting, - init: () => ({scan: (text: string) => text.length}), -}); - -new entry.Builder(definition, ContentBuilder); - -const preparedDefinition = api.defineRelay({ - name: "scanner", - init: () => ({scan: (text: string) => text.length}), - prepare: async () => ({title: "Prepared"}), - - render: ({data}) => { - const title: string = data.title; - // @ts-expect-error: Runtime constructors preserve the inferred prepare data. - data.missing; - - return title; - }, -}); - -new entry.Builder(preparedDefinition, ContentBuilder); -entry.default(preparedDefinition, ContentBuilder); - -const unresolved: entry.RelayUnresolvedDefinition> = {}; -new entry.Builder(unresolved, ContentBuilder); -entry.default(entry.resolveDefinition({default: definition}, "scanner"), ContentBuilder); - const original = local.getRelay("scanner"); const scalar = api.getRelay("scanner", 1); const top = api.getRelay("scanner", {tabId: 1}); diff --git a/src/cli/plugins/index.ts b/src/cli/plugins/index.ts index 4ac57cb3..f5ab67d5 100644 --- a/src/cli/plugins/index.ts +++ b/src/cli/plugins/index.ts @@ -1,3 +1,4 @@ +export {default as pluginAction} from "./action"; export {default as pluginAsset} from "./asset"; export {default as pluginBundler} from "./bundler"; export {default as pluginBackground} from "./background"; diff --git a/src/cli/plugins/offscreen/index.ts b/src/cli/plugins/offscreen/index.ts index db6354f9..3d2ab435 100644 --- a/src/cli/plugins/offscreen/index.ts +++ b/src/cli/plugins/offscreen/index.ts @@ -1,4 +1,3 @@ -import path from "path"; import {Configuration as RspackConfig, HtmlRspackPlugin, Plugins} from "@rspack/core"; import {RspackVirtualModulePlugin} from "rspack-plugin-virtual-module"; import HtmlRspackTagsPlugin from "html-rspack-tags-plugin"; @@ -17,7 +16,6 @@ import {Browser} from "@typing/browser"; import {BackgroundEntryName} from "@typing/background"; import type {OffscreenParametersMap} from "@typing/offscreen"; -const OffscreenTempDir = "virtual"; const OffscreenBackgroundModule = "offscreen.background.ts"; export default definePlugin(() => { @@ -75,14 +73,14 @@ export default definePlugin(() => { { [OffscreenBackgroundModule]: virtualOffscreenBackgroundModule(), }, - OffscreenTempDir + "virtual" ) ); rspack = { entry: { [BackgroundEntryName]: { - import: [path.join(OffscreenTempDir, OffscreenBackgroundModule)], + import: [OffscreenBackgroundModule], }, }, }; diff --git a/src/cli/plugins/style/README.md b/src/cli/plugins/style/README.md new file mode 100644 index 00000000..684ecb37 --- /dev/null +++ b/src/cli/plugins/style/README.md @@ -0,0 +1,81 @@ +# Shared and app styles + +With `mergeStyles` enabled, importing a shared stylesheet also includes the app +stylesheet at the same relative path. Both sources compile as one CSS module, +using the shared resource path and the existing app-specific class-name salt. +Shared body rules precede app body rules. Ordinary, `?unisolated`, and `?asis` +imports retain their existing routing and CSS module behavior. + +Before merging, the plugin rebases explicit `./` and `../` requests in `@use` +and `@forward` from each original file to the shared resource. Module parameters +remain intact; Sass resolves partials and directory indexes. Package requests, +aliases, built-in modules, and requests without an explicit relative prefix keep +their normal resolution. `rebase.ts` owns the request and URL rewriting; +`utils.ts` owns which nodes receive it, the prelude order, and deduplication. + +Only identical prepared prelude statements are deduplicated. Shared requests pass +through the same normalization, so equivalent spellings of one module match. Two +identical relative requests from different source directories can refer to +different modules. Use distinct `as` namespaces when both modules need to +coexist; Sass still reports namespace and configuration conflicts. + +## Resources configured through `with` + +The plugin also rebases literal app `url(...)` values, including values supplied +inside module configuration. Query strings and fragments are preserved for the +normal CSS and asset loaders. + +```scss +// src/apps/example/content/content.scss +@use "../theme/fonts" with ( + $inter-latin: url("../theme/fonts/Inter-Latin.woff2?browser") +); +``` + +```scss +// src/apps/example/theme/fonts/_index.scss +$inter-latin: url("./Inter-Latin.woff2?browser") !default; + +@font-face { + font-family: Inter; + src: $inter-latin format("woff2"); +} +``` + +Pass the complete `url(...)` from the app source. + +## Limitations + +- Only `@use` and `@forward` requests are rebased. Sass `@import` and + `meta.load-css()` requests keep resolving from the shared resource. +- Resources written inside imported partials are not rebased. Pass the complete + `url(...)` from the app source through `with`. +- URL aliases are not recognized. A `url(...)` without a scheme, root, `~`, `#`, + or `?` prefix is treated as a relative resource, so an aliased or package URL + written in an app source becomes a relative path and fails to resolve. Write a + full relative `url(...)` from the app source; alias-aware URL rewriting would + have to follow the bundler resolve settings. +- Dynamic Sass URL expressions and interpolated requests are left for Sass to + evaluate. +- Sass reports errors in the merged app body against the shared resource with + positions in the merged text. Errors inside imported modules keep their own + file positions. + +## Migration + +Relative `@use` and `@forward` requests in app sources used to resolve from the +shared file. They now resolve from the app file, as written. An app stylesheet +that intentionally reuses a shared module through such a request must address +it by a path or a configured alias to the shared directory. + +## Watch dependencies and errors + +Preparation runs through `sass-loader.additionalData`. Existing overrides are +registered with `addDependency`; absent overrides use `addMissingDependency` so +creation, removal, and recreation trigger compilation. Sass and the asset loader +register imported modules and resources. + +Preparation errors are emitted as compilation errors and produce no stylesheet, +because sass-loader 16 does not forward errors thrown by `additionalData` through +its loader callback. Sass compilation errors retain Sass's own diagnostics. A +failed override never falls back to a successful shared-only build. diff --git a/src/cli/plugins/style/index.ts b/src/cli/plugins/style/index.ts index a330f1eb..66ed0354 100644 --- a/src/cli/plugins/style/index.ts +++ b/src/cli/plugins/style/index.ts @@ -1,7 +1,13 @@ import _ from "lodash"; import path from "path"; import fs from "fs"; -import {Configuration as RspackConfig, CssExtractRspackPlugin, RuleSetUse, RuleSetUseItem} from "@rspack/core"; +import { + Configuration as RspackConfig, + CssExtractRspackPlugin, + LoaderContext, + RuleSetUse, + RuleSetUseItem, +} from "@rspack/core"; import {mergeStyleSources} from "./utils"; import {DocumentStylesLayer, DefaultStylesLayer} from "@cli/bundler/layers"; @@ -10,7 +16,6 @@ import {definePlugin} from "@main/plugin"; import {appFilenameResolver} from "@cli/bundler"; import {getAppSourcePath, getResolvePath, getSharedPath} from "@cli/resolvers/path"; -import {toPosix} from "@cli/utils/path"; import {ReadonlyConfig} from "@typing/config"; @@ -19,44 +24,42 @@ import {ReadonlyConfig} from "@typing/config"; const DefaultAsIsLoaderIdent = "adnbn-default-asis"; const DefaultModulesLoaderIdent = "adnbn-default-modules"; -// prettier-ignore -const styleMergerLoader = - (config: ReadonlyConfig) => - (sharedStyle: string, sharedPath: string): string | void => { - const sharedDir = getResolvePath(getSharedPath(config)); +const createStyleMerger = (config: ReadonlyConfig) => { + const sharedDir = getResolvePath(getSharedPath(config)); + const appDir = getResolvePath(getAppSourcePath(config)); - if (sharedPath.startsWith(sharedDir)) { - const relativePath = path.relative(sharedDir, sharedPath); + return (content: string, context: LoaderContext): string => { + const filename = context.resourcePath; + const relative = path.relative(sharedDir, filename); - const appDir = getResolvePath(getAppSourcePath(config)); - const appPath = getResolvePath(path.join(appDir, relativePath)); + if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) { + return content; + } - if (fs.existsSync(appPath)) { - let appStyle = fs.readFileSync(appPath, "utf8"); + const appPath = path.join(appDir, relative); - appStyle = appStyle.replace(/url\((['"]?)(.*?)\1\)/g, (match, quote, filePath) => { - if ( - filePath.startsWith("/") || - filePath.startsWith("http") || - filePath.startsWith("data:") - ) { - return match; - } + if (!fs.existsSync(appPath)) { + context.addMissingDependency(appPath); - const cssDir = path.dirname(appPath); - const assetAbs = path.resolve(cssDir, filePath); + return content; + } - const sharedFileDir = path.dirname(sharedPath); + context.addDependency(appPath); - const relativeToSharedFile = path.relative(sharedFileDir, assetAbs); + try { + return mergeStyleSources( + {filename, content}, + {filename: appPath, content: fs.readFileSync(appPath, "utf8")} + ); + } catch (error) { + // sass-loader prepares additionalData outside its error handler. Fail the compilation + // explicitly and supply no stylesheet instead of compiling an incomplete shared fallback. + context.emitError(error instanceof Error ? error : new Error(String(error))); - return `url("${toPosix(relativeToSharedFile)}")`; - }); - - return mergeStyleSources(sharedStyle, appStyle); - } - } - }; + return ""; + } + }; +}; export default definePlugin(() => { return { @@ -68,18 +71,14 @@ export default definePlugin(() => { const kebabApp = _.kebabCase(app); const createSassRuleSet = (rule: RuleSetUseItem): RuleSetUse => { - const rules: RuleSetUse = [CssExtractRspackPlugin.loader, rule, "sass-loader"]; - - if (mergeStyles) { - rules.push({ - loader: "source-modifier-loader", - options: { - modify: styleMergerLoader(config), - }, - }); - } - - return rules; + return [ + CssExtractRspackPlugin.loader, + rule, + { + loader: "sass-loader", + options: mergeStyles ? {additionalData: createStyleMerger(config)} : {}, + }, + ]; }; const createStyleRules = (defaultStyles = false) => [ diff --git a/src/cli/plugins/style/rebase.test.ts b/src/cli/plugins/style/rebase.test.ts new file mode 100644 index 00000000..471236c8 --- /dev/null +++ b/src/cli/plugins/style/rebase.test.ts @@ -0,0 +1,101 @@ +import path from "path"; + +import {rebaseModuleRequest, rebaseUrlFunctions} from "./rebase"; + +const source = path.resolve("src"); +const from = path.join(source, "apps", "alpha", "content", "content.scss"); +const to = path.join(source, "shared", "content", "content.scss"); + +describe("rebaseModuleRequest", () => { + test.each([ + ['"./theme/tokens" as appTokens', '"../../apps/alpha/content/theme/tokens" as appTokens'], + ["'../theme/fonts'", "'../../apps/alpha/theme/fonts'"], + [ + '"./theme/tokens" as theme-* show $theme-accent with ($accent: blue)', + '"../../apps/alpha/content/theme/tokens" as theme-* show $theme-accent with ($accent: blue)', + ], + [ + '"../theme/fonts" with (\n $font: url("../theme/font.woff2?browser")\n)', + '"../../apps/alpha/theme/fonts" with (\n $font: url("../theme/font.woff2?browser")\n)', + ], + ['/* app */ "./theme/tokens"', '/* app */ "../../apps/alpha/content/theme/tokens"'], + ])("rebases the relative request in %j", (params, expected) => { + expect(rebaseModuleRequest(params, from, to)).toBe(expected); + }); + + test.each([ + '"sass:math"', + '"some-package/theme"', + '"@theme/tokens"', + '"~theme/tokens"', + '"theme/tokens"', + '"pkg:some-package"', + '"./#{$theme}"', + '"./theme/tokens', + "", + ])("keeps %j as written", params => { + expect(rebaseModuleRequest(params, from, to)).toBe(params); + }); + + test.each([ + ['"./../common/tokens"', '"../common/tokens"'], + ['"./theme/./tokens" as tokens', '"./theme/tokens" as tokens'], + ['"../common/tokens"', '"../common/tokens"'], + ['"theme/tokens"', '"theme/tokens"'], + ])("normalizes %j for the same stylesheet", (params, expected) => { + expect(rebaseModuleRequest(params, to, to)).toBe(expected); + }); +}); + +describe("rebaseUrlFunctions", () => { + test.each([ + ["url(theme/plain.svg)", 'url("../../apps/alpha/content/theme/plain.svg")'], + [ + 'url( "./theme/spaced font.woff2?browser#face" )', + 'url( "../../apps/alpha/content/theme/spaced font.woff2?browser#face" )', + ], + ["url('./theme/icon.svg?next=a/../b#face')", "url('../../apps/alpha/content/theme/icon.svg?next=a/../b#face')"], + ['url("./theme/escaped\\ name.svg")', 'url("../../apps/alpha/content/theme/escaped\\ name.svg")'], + ['URL("../theme/upper.svg")', 'URL("../../apps/alpha/theme/upper.svg")'], + [ + 'url("./theme/font.woff2") format("woff2")', + 'url("../../apps/alpha/content/theme/font.woff2") format("woff2")', + ], + [ + 'url("./a.svg"), url("./b.svg")', + 'url("../../apps/alpha/content/a.svg"), url("../../apps/alpha/content/b.svg")', + ], + [ + 'image-set(url("./a.png") 1x, url("./a@2x.png") 2x)', + 'image-set(url("../../apps/alpha/content/a.png") 1x, url("../../apps/alpha/content/a@2x.png") 2x)', + ], + [ + '(\n $font: url("../theme/font.woff2?browser")\n)', + '(\n $font: url("../../apps/alpha/theme/font.woff2?browser")\n)', + ], + ])("rebases the literal URL in %j", (value, expected) => { + expect(rebaseUrlFunctions(value, from, to)).toBe(expected); + }); + + test.each([ + 'url("https://example.com/font.woff2")', + 'url("//example.com/font.woff2")', + 'url("data:image/svg+xml;base64,AAAA")', + 'url("chrome-extension://id/font.woff2")', + 'url("/font.woff2")', + 'url("#icon")', + 'url("?version=1")', + 'url("~package/font.woff2")', + "url($font)", + 'url("#{$directory}/font.woff2")', + "url(./#{$theme}.svg)", + "url(font-path())", + 'url("./a.svg" "b")', + 'url("./unclosed', + "url()", + '"url(./text.svg)"', + '$font format("woff2")', + ])("keeps %j as written", value => { + expect(rebaseUrlFunctions(value, from, to)).toBe(value); + }); +}); diff --git a/src/cli/plugins/style/rebase.ts b/src/cli/plugins/style/rebase.ts new file mode 100644 index 00000000..1e869ecb --- /dev/null +++ b/src/cli/plugins/style/rebase.ts @@ -0,0 +1,98 @@ +import path from "path"; +import valueParser from "postcss-value-parser"; + +import {toPosix} from "@cli/utils/path"; + +const RelativeRequestPattern = /^\.{1,2}\//; +// Schemes, root-relative paths, fragments, queries and ~-prefixed requests are not rebased. +const ExternalUrlPattern = /^(?:[a-z][a-z\d+.-]*:|[/#?~])/i; + +/** + * Rebases the explicit `./` or `../` module request of `@use` or `@forward` parameters written in the + * stylesheet `from` so it resolves from the stylesheet `to`. Namespaces, `show`, `hide` and `with` + * configuration stay as written; package, alias, built-in and interpolated requests keep their normal + * resolution. When both stylesheets are the same file, the request is only normalized. + */ +export const rebaseModuleRequest = (params: string, from: string, to: string): string => { + const parsed = valueParser(params); + const request = parsed.nodes.find(node => node.type !== "space" && node.type !== "comment"); + + if ( + request?.type !== "string" || + request.unclosed || + !RelativeRequestPattern.test(request.value) || + request.value.includes("#{") + ) { + return params; + } + + request.value = rebaseStylePath(request.value, from, to); + + return parsed.toString(); +}; + +/** + * Rebases literal relative `url(...)` values written in the stylesheet `from` so they resolve from the + * stylesheet `to`, including values nested in lists, functions and module configuration. Absolute URLs, + * schemes, root-relative paths, fragments, queries, ~-prefixed requests and Sass expressions stay as written. + * Rebased URLs are quoted and keep their query and fragment. + */ +export const rebaseUrlFunctions = (value: string, from: string, to: string): string => { + const parsed = valueParser(value); + let rebased = false; + + parsed.walk(node => { + if (node.type !== "function" || node.value.toLowerCase() !== "url") { + return; + } + + const tokens = node.nodes.filter(token => token.type !== "space" && token.type !== "comment"); + const url = tokens[0]; + + if (node.unclosed || tokens.length !== 1 || !isLiteralUrl(url)) { + return false; + } + + // Quote rebased URLs so spaces in the app directory remain part of the path. + const quoted: valueParser.StringNode = { + ...url, + type: "string", + quote: url.type === "string" ? url.quote : '"', + value: rebaseStylePath(url.value, from, to), + }; + + node.nodes = node.nodes.map(token => (token === url ? quoted : token)); + rebased = true; + + return false; + }); + + return rebased ? parsed.toString() : value; +}; + +const isLiteralUrl = (token: valueParser.Node): token is valueParser.StringNode | valueParser.WordNode => { + if (token.type === "string") { + return !token.unclosed && isLiteralPath(token.value); + } + + if (token.type === "word") { + return isLiteralPath(token.value) && !/[$()]/.test(token.value); + } + + return false; +}; + +const isLiteralPath = (value: string): boolean => { + return Boolean(value) && !ExternalUrlPattern.test(value) && !value.includes("#{"); +}; + +const rebaseStylePath = (request: string, from: string, to: string): string => { + const suffixIndex = request.search(/[?#]/); + const pathname = suffixIndex === -1 ? request : request.slice(0, suffixIndex); + const suffix = suffixIndex === -1 ? "" : request.slice(suffixIndex); + const directory = toPosix(path.relative(path.dirname(to), path.dirname(from))); + // Stylesheet requests are URLs, including on Windows. Keep query/fragment data out of path normalization. + const relative = path.posix.join(directory, pathname); + + return `${relative.startsWith("../") ? relative : `./${relative}`}${suffix}`; +}; diff --git a/src/cli/plugins/style/tests/fixtures/relative-conflict/app/content.scss b/src/cli/plugins/style/tests/fixtures/relative-conflict/app/content.scss new file mode 100644 index 00000000..c90b5443 --- /dev/null +++ b/src/cli/plugins/style/tests/fixtures/relative-conflict/app/content.scss @@ -0,0 +1,5 @@ +@use "./theme/tokens"; + +.badge { + background: tokens.$accent; +} diff --git a/src/cli/plugins/style/tests/fixtures/relative-conflict/app/theme/_tokens.scss b/src/cli/plugins/style/tests/fixtures/relative-conflict/app/theme/_tokens.scss new file mode 100644 index 00000000..2e174f9b --- /dev/null +++ b/src/cli/plugins/style/tests/fixtures/relative-conflict/app/theme/_tokens.scss @@ -0,0 +1 @@ +$accent: blue; diff --git a/src/cli/plugins/style/tests/fixtures/relative-conflict/shared/content.scss b/src/cli/plugins/style/tests/fixtures/relative-conflict/shared/content.scss new file mode 100644 index 00000000..e4811819 --- /dev/null +++ b/src/cli/plugins/style/tests/fixtures/relative-conflict/shared/content.scss @@ -0,0 +1,5 @@ +@use "./theme/tokens"; + +.badge { + color: tokens.$accent; +} diff --git a/src/cli/plugins/style/tests/fixtures/relative-conflict/shared/theme/_tokens.scss b/src/cli/plugins/style/tests/fixtures/relative-conflict/shared/theme/_tokens.scss new file mode 100644 index 00000000..a96ed09a --- /dev/null +++ b/src/cli/plugins/style/tests/fixtures/relative-conflict/shared/theme/_tokens.scss @@ -0,0 +1 @@ +$accent: red; diff --git a/src/cli/plugins/style/tests/fixtures/relative-modules/app/content.scss b/src/cli/plugins/style/tests/fixtures/relative-modules/app/content.scss new file mode 100644 index 00000000..0361da1d --- /dev/null +++ b/src/cli/plugins/style/tests/fixtures/relative-modules/app/content.scss @@ -0,0 +1,10 @@ +@forward "./theme/tokens" as theme-* show $theme-accent with ($accent: blue); +@forward "./theme/hidden" hide $private; +@use "./theme/tokens" as appTokens; +@use "./theme/fonts" with ( + $font: url("./theme/Inter.woff2?browser&v=a/../b#face") +); + +.badge { + background: appTokens.$accent; +} diff --git a/src/cli/plugins/style/tests/fixtures/relative-modules/app/theme/_hidden.scss b/src/cli/plugins/style/tests/fixtures/relative-modules/app/theme/_hidden.scss new file mode 100644 index 00000000..4f9ad094 --- /dev/null +++ b/src/cli/plugins/style/tests/fixtures/relative-modules/app/theme/_hidden.scss @@ -0,0 +1 @@ +$private: red; diff --git a/src/cli/plugins/style/tests/fixtures/relative-modules/app/theme/_metrics.scss b/src/cli/plugins/style/tests/fixtures/relative-modules/app/theme/_metrics.scss new file mode 100644 index 00000000..abc0c236 --- /dev/null +++ b/src/cli/plugins/style/tests/fixtures/relative-modules/app/theme/_metrics.scss @@ -0,0 +1 @@ +$weight: 400; diff --git a/src/cli/plugins/style/tests/fixtures/relative-modules/app/theme/_tokens.scss b/src/cli/plugins/style/tests/fixtures/relative-modules/app/theme/_tokens.scss new file mode 100644 index 00000000..3f610320 --- /dev/null +++ b/src/cli/plugins/style/tests/fixtures/relative-modules/app/theme/_tokens.scss @@ -0,0 +1 @@ +$accent: orange !default; diff --git a/src/cli/plugins/style/tests/fixtures/relative-modules/app/theme/fonts/_index.scss b/src/cli/plugins/style/tests/fixtures/relative-modules/app/theme/fonts/_index.scss new file mode 100644 index 00000000..5e03864e --- /dev/null +++ b/src/cli/plugins/style/tests/fixtures/relative-modules/app/theme/fonts/_index.scss @@ -0,0 +1,9 @@ +@use "../metrics"; + +$font: url("./default.woff2") !default; + +@font-face { + font-family: Inter; + font-weight: metrics.$weight; + src: $font; +} diff --git a/src/cli/plugins/style/tests/fixtures/relative-modules/shared/content.scss b/src/cli/plugins/style/tests/fixtures/relative-modules/shared/content.scss new file mode 100644 index 00000000..11116869 --- /dev/null +++ b/src/cli/plugins/style/tests/fixtures/relative-modules/shared/content.scss @@ -0,0 +1,5 @@ +@use "./theme/tokens" as sharedTokens; + +.badge { + color: sharedTokens.$accent; +} diff --git a/src/cli/plugins/style/tests/fixtures/relative-modules/shared/theme/_tokens.scss b/src/cli/plugins/style/tests/fixtures/relative-modules/shared/theme/_tokens.scss new file mode 100644 index 00000000..b511b753 --- /dev/null +++ b/src/cli/plugins/style/tests/fixtures/relative-modules/shared/theme/_tokens.scss @@ -0,0 +1 @@ +$accent: red !default; diff --git a/src/cli/plugins/style/tests/fixtures/same-module/app/content.scss b/src/cli/plugins/style/tests/fixtures/same-module/app/content.scss new file mode 100644 index 00000000..d8333753 --- /dev/null +++ b/src/cli/plugins/style/tests/fixtures/same-module/app/content.scss @@ -0,0 +1,5 @@ +@use "../shared/../common/tokens"; + +.badge { + color: tokens.$accent; +} diff --git a/src/cli/plugins/style/tests/fixtures/same-module/common/_tokens.scss b/src/cli/plugins/style/tests/fixtures/same-module/common/_tokens.scss new file mode 100644 index 00000000..a96ed09a --- /dev/null +++ b/src/cli/plugins/style/tests/fixtures/same-module/common/_tokens.scss @@ -0,0 +1 @@ +$accent: red; diff --git a/src/cli/plugins/style/tests/fixtures/same-module/shared/content.scss b/src/cli/plugins/style/tests/fixtures/same-module/shared/content.scss new file mode 100644 index 00000000..8b796477 --- /dev/null +++ b/src/cli/plugins/style/tests/fixtures/same-module/shared/content.scss @@ -0,0 +1,5 @@ +@use "./../common/tokens"; + +.badge { + color: tokens.$accent; +} diff --git a/src/cli/plugins/style/tests/fixtures/url-values/app/content.scss b/src/cli/plugins/style/tests/fixtures/url-values/app/content.scss new file mode 100644 index 00000000..81b17045 --- /dev/null +++ b/src/cli/plugins/style/tests/fixtures/url-values/app/content.scss @@ -0,0 +1,13 @@ +/* url("./comment.svg") */ +$icon: url("./theme/icon.svg"); + +.badge { + background: url(theme/plain.svg); + content: "url(./text.svg)"; +} + +@supports (background: url("./theme/support.svg")) { + .badge { + color: blue; + } +} diff --git a/src/cli/plugins/style/tests/fixtures/url-values/shared/content.scss b/src/cli/plugins/style/tests/fixtures/url-values/shared/content.scss new file mode 100644 index 00000000..37bd50e7 --- /dev/null +++ b/src/cli/plugins/style/tests/fixtures/url-values/shared/content.scss @@ -0,0 +1,5 @@ +$logo: url(./logo.svg); + +.badge { + background: url("./shared.svg"); +} diff --git a/src/cli/plugins/style/utils.test.ts b/src/cli/plugins/style/utils.test.ts index e91ccb93..3e9c355a 100644 --- a/src/cli/plugins/style/utils.test.ts +++ b/src/cli/plugins/style/utils.test.ts @@ -1,17 +1,29 @@ import fs from "fs"; import path from "path"; import sass from "sass"; +import {pathToFileURL} from "url"; -import {mergeStyleSources} from "./utils"; +import {mergeStyleSources, type StyleSource} from "./utils"; const fixtures = path.resolve(__dirname, "tests", "fixtures"); -const mergeFixture = (name: string): string => { - const fixture = (...parts: string[]): string => fs.readFileSync(path.join(fixtures, name, ...parts), "utf8"); +const readSource = (...parts: string[]): StyleSource => { + const filename = path.join(fixtures, ...parts); - return mergeStyleSources(fixture("shared.scss"), fixture("app.scss")); + return {filename, content: fs.readFileSync(filename, "utf8")}; }; +const mergeFixture = (name: string): string => + mergeStyleSources(readSource(name, "shared.scss"), readSource(name, "app.scss")); + +const mergeLocatedFixture = (name: string): string => + mergeStyleSources(readSource(name, "shared", "content.scss"), readSource(name, "app", "content.scss")); + +const compileLocatedFixture = (name: string): string => + sass.compileString(mergeLocatedFixture(name), { + url: pathToFileURL(path.join(fixtures, name, "shared", "content.scss")), + }).css; + const normalizeStyle = (style: string): string => style.replace(/\s+/g, " ").trim(); describe("mergeStyleSources", () => { @@ -134,6 +146,62 @@ describe("mergeStyleSources", () => { expect(normalizeStyle(merged).match(/\.badge \{ color: red; \}/g)).toHaveLength(2); }); + + test("rebases module requests before merging and preserves Sass configuration and forwarding", () => { + const merged = mergeLocatedFixture("relative-modules"); + + expect(merged).toContain('@use "./theme/tokens" as sharedTokens;'); + expect(merged).toContain('@use "../app/theme/tokens" as appTokens;'); + expect(merged).toContain('@forward "../app/theme/tokens" as theme-* show $theme-accent with ($accent: blue);'); + expect(merged).toContain('@forward "../app/theme/hidden" hide $private;'); + expect(normalizeStyle(merged)).toContain( + '@use "../app/theme/fonts" with ( $font: url("../app/theme/Inter.woff2?browser&v=a/../b#face") );' + ); + + const css = compileLocatedFixture("relative-modules"); + + expect(css).toContain("color: red"); + expect(css).toContain("background: blue"); + expect(css).toContain("font-weight: 400"); + expect(css).toContain('url("../app/theme/Inter.woff2?browser&v=a/../b#face")'); + }); + + test("does not deduplicate identical requests to different files or hide their namespace conflict", () => { + const merged = mergeLocatedFixture("relative-conflict"); + + expect(merged).toContain('@use "./theme/tokens";'); + expect(merged).toContain('@use "../app/theme/tokens";'); + expect(() => compileLocatedFixture("relative-conflict")).toThrow(/already a module with namespace "tokens"/); + }); + + test("normalizes shared requests so equivalent relative requests to the same module deduplicate", () => { + const merged = mergeLocatedFixture("same-module"); + + expect(merged).not.toContain("./../common/tokens"); + expect(merged.match(/@use "\.\.\/common\/tokens";/g)).toHaveLength(1); + expect(compileLocatedFixture("same-module").match(/color: red/g)).toHaveLength(2); + }); + + test("rebases app URLs in declarations and at-rule parameters and leaves shared values and comments as written", () => { + const merged = normalizeStyle(mergeLocatedFixture("url-values")); + + for (const rebased of [ + '$icon: url("../app/theme/icon.svg");', + 'background: url("../app/theme/plain.svg");', + '@supports (background: url("../app/theme/support.svg"))', + ]) { + expect(merged).toContain(rebased); + } + + for (const unchanged of [ + "$logo: url(./logo.svg);", + 'background: url("./shared.svg");', + 'content: "url(./text.svg)";', + '/* url("./comment.svg") */', + ]) { + expect(merged).toContain(unchanged); + } + }); }); declare global { diff --git a/src/cli/plugins/style/utils.ts b/src/cli/plugins/style/utils.ts index ee227633..695d51a7 100644 --- a/src/cli/plugins/style/utils.ts +++ b/src/cli/plugins/style/utils.ts @@ -1,6 +1,13 @@ import {ChildNode} from "postcss"; import * as scss from "postcss-scss"; +import {rebaseModuleRequest, rebaseUrlFunctions} from "./rebase"; + +export type StyleSource = { + filename: string; + content: string; +}; + type StyleSourceParts = { charsets: string[]; sassPrelude: string[]; @@ -8,9 +15,9 @@ type StyleSourceParts = { body: string[]; }; -export const mergeStyleSources = (sharedStyle: string, appStyle: string): string => { - const shared = splitStyleSource(sharedStyle); - const app = splitStyleSource(appStyle); +export const mergeStyleSources = (sharedStyle: StyleSource, appStyle: StyleSource): string => { + const shared = splitStyleSource(sharedStyle, sharedStyle.filename); + const app = splitStyleSource(appStyle, sharedStyle.filename); return [ ...dedupePrelude([...shared.charsets, ...app.charsets]), @@ -41,8 +48,29 @@ const dedupePrelude = (parts: string[]): string[] => { }); }; -const splitStyleSource = (source: string): StyleSourceParts => { - const root = scss.parse(source); +const splitStyleSource = (source: StyleSource, target: string): StyleSourceParts => { + const root = scss.parse(source.content, {from: source.filename}); + // Only a source compiled under another filename needs its resources rebased. + const relocated = source.filename !== target; + + root.walkAtRules(node => { + if (isModuleRuleNode(node)) { + // Shared requests pass through the same normalization, so equivalent prepared + // imports from both sources match when the prelude is deduplicated. + node.params = rebaseModuleRequest(node.params, source.filename, target); + } + + if (relocated) { + // Module configuration such as `with ($font: url(...))` carries app resources as well. + node.params = rebaseUrlFunctions(node.params, source.filename, target); + } + }); + + if (relocated) { + root.walkDecls(node => { + node.value = rebaseUrlFunctions(node.value, source.filename, target); + }); + } const parts: StyleSourceParts = { charsets: [], @@ -57,22 +85,26 @@ const splitStyleSource = (source: string): StyleSourceParts => { root.nodes?.forEach(node => { if (!bodyStarted && isCharsetNode(node)) { parts.charsets.push(stringifyNode(node)); + return; } if (!bodyStarted && node.type === "comment") { parts[preludeType === "sass" ? "sassPrelude" : "cssPrelude"].push(stringifyNode(node)); + return; } if (!bodyStarted && isSassPreludeNode(node)) { parts.sassPrelude.push(stringifyNode(node)); + return; } if (!bodyStarted && isCssPreludeNode(node)) { preludeType = "css"; parts.cssPrelude.push(stringifyNode(node)); + return; } @@ -95,16 +127,14 @@ const stringifyNode = (node: ChildNode): string => { const isCharsetNode = (node: ChildNode): boolean => node.type === "atrule" && node.name === "charset"; +const isModuleRuleNode = (node: ChildNode): boolean => node.type === "atrule" && ["forward", "use"].includes(node.name); + const isSassPreludeNode = (node: ChildNode): boolean => { if (node.type === "decl") { return node.prop.startsWith("$"); } - if (node.type !== "atrule") { - return false; - } - - return ["forward", "use"].includes(node.name); + return isModuleRuleNode(node); }; const isCssPreludeNode = (node: ChildNode): boolean => { diff --git a/src/cli/plugins/typescript/TypescriptConfig.ts b/src/cli/plugins/typescript/TypescriptConfig.ts index 3ea3c2af..3544aa56 100644 --- a/src/cli/plugins/typescript/TypescriptConfig.ts +++ b/src/cli/plugins/typescript/TypescriptConfig.ts @@ -4,7 +4,7 @@ import {TsConfigJson} from "type-fest"; import FileBuilder from "./FileBuilder"; -import {getResolvePath} from "@cli/resolvers/path"; +import {resolveRootPath} from "@cli/resolvers/path"; import {ReadonlyConfig} from "@typing/config"; import {PackageName} from "@typing/app"; @@ -50,7 +50,7 @@ export default class extends FileBuilder { public aliases(): Record { return _.merge( this.vendorAliases, - _.mapValues(this.alias(), value => getResolvePath(value)) + _.mapValues(this.alias(), value => path.resolve(resolveRootPath(this.config), value)) ); } diff --git a/src/cli/resolvers/bundler.ts b/src/cli/resolvers/bundler.ts index 8e3b12ff..5b35dd9a 100644 --- a/src/cli/resolvers/bundler.ts +++ b/src/cli/resolvers/bundler.ts @@ -1,9 +1,11 @@ import {Configuration as RspackConfig, RspackPluginInstance} from "@rspack/core"; import {RsdoctorRspackPlugin} from "@rsdoctor/rspack-plugin"; import {merge as mergeConfig} from "webpack-merge"; +import path from "node:path"; import manifestFactory from "../builders/manifest"; import {processPluginHandler} from "./plugin"; +import {resolveRootPath} from "./path"; import ManifestPlugin from "@cli/bundler/plugins/manifest"; import WatchPlugin from "@cli/bundler/plugins/watch"; @@ -53,9 +55,15 @@ const getConfigForManifest = async (config: ReadonlyConfig): Promise => { let rspack: RspackConfig = { + context: resolveRootPath(config), entry: {}, mode: config.mode, cache: false, + resolve: { + // Virtual entry modules may live outside the application. Keep issuer-local + // lookup first, then resolve the consumer's packages from its project root. + modules: ["node_modules", path.join(resolveRootPath(config), "node_modules")], + }, }; // prettier-ignore diff --git a/src/cli/resolvers/config.test.ts b/src/cli/resolvers/config.test.ts index 410e8c50..a9bf835e 100644 --- a/src/cli/resolvers/config.test.ts +++ b/src/cli/resolvers/config.test.ts @@ -2,6 +2,7 @@ jest.mock("../plugins", () => { const plugin = (name: string) => () => ({name}); return { + pluginAction: plugin("action"), pluginAsset: plugin("asset"), pluginBackground: plugin("background"), pluginBundler: plugin("bundler"), @@ -61,6 +62,27 @@ describe("config resolver", () => { expect(config.lang).toBe(Language.English); }); + test("does not enable an action by default", async () => { + const config = await resolveConfig({configFile: "package.json"}); + + expect(config.action).toBeUndefined(); + }); + + test("preserves explicit action options from build config", async () => { + const action = {icon: "active", title: "@action.title"}; + const config = await resolveConfig({configFile: "package.json", action}); + + expect(config.action).toEqual(action); + }); + + test("preserves an explicit empty action from user config", async () => { + mockedLoadConfig.mockResolvedValue({config: {action: {}}}); + + const config = await resolveConfig({configFile: "package.json"}); + + expect(config.action).toEqual({}); + }); + test("normalizes language from user config", async () => { mockedLoadConfig.mockResolvedValue({ config: { diff --git a/src/cli/resolvers/config.ts b/src/cli/resolvers/config.ts index bbc30e1e..3191a733 100644 --- a/src/cli/resolvers/config.ts +++ b/src/cli/resolvers/config.ts @@ -4,6 +4,7 @@ import {loadConfig} from "c12"; import _ from "lodash"; import { + pluginAction, pluginAsset, pluginBackground, pluginBundler, @@ -75,7 +76,7 @@ const getUserConfig = async (config: ReadonlyConfig): Promise => { if (existsSync(configFilePath)) { const {config: userConfig} = await loadConfig({ configFile: configFilePath, - dotenv: true, + dotenv: false, }); if (config.debug) { @@ -205,6 +206,7 @@ export default async (config: OptionalConfig): Promise => { author = undefined, homepage = "HOMEPAGE", icon = DefaultIconGroupName, + action, lang = Language.English, incognito, specific, @@ -279,6 +281,7 @@ export default async (config: OptionalConfig): Promise => { homepage, lang: resolveLanguage(lang), icon, + action, incognito, specific, manifest, @@ -367,6 +370,7 @@ export default async (config: OptionalConfig): Promise => { pluginStyle(), pluginLocale(), pluginMeta(), + pluginAction(), pluginContent(), pluginBackground(), pluginOptions(), diff --git a/src/cli/utils/path.test.ts b/src/cli/utils/path.test.ts new file mode 100644 index 00000000..08dedad8 --- /dev/null +++ b/src/cli/utils/path.test.ts @@ -0,0 +1,18 @@ +import path from "node:path"; + +import {toPosix, toPosixPath} from "./path"; + +test("absolute entrypoint imports preserve their filesystem root", () => { + const directory = path.resolve("project with spaces", "src"); + const filename = path.join(directory, "newtab.ts"); + + expect(toPosix(filename)).toBe(filename.split(path.sep).join("/")); + expect(toPosixPath(filename)).toBe(path.join(directory, "newtab").split(path.sep).join("/")); + expect(toPosixPath(path.join(directory, "index.ts"))).toBe(directory.split(path.sep).join("/")); +}); + +test("relative entrypoint imports keep their directory and index conventions", () => { + expect(toPosixPath("index.ts")).toBe("."); + expect(toPosixPath(path.join("src", "newtab.ts"))).toBe("src/newtab"); + expect(toPosixPath(path.join("src", "newtab", "index.ts"))).toBe("src/newtab"); +}); diff --git a/src/cli/utils/path.ts b/src/cli/utils/path.ts index eeb02aa3..23bb0220 100644 --- a/src/cli/utils/path.ts +++ b/src/cli/utils/path.ts @@ -2,7 +2,7 @@ import path from "path"; import _ from "lodash"; export const toPosix = (to: string): string => { - return path.posix.join(...to.split(path.sep)); + return path.normalize(to).split(path.sep).join("/"); }; export const toPosixPath = (pathname: string): string => { diff --git a/src/entry/content/lifecycle/nodes/FrameNode.test.ts b/src/entry/content/lifecycle/nodes/FrameNode.test.ts index e5ae54bb..abd098b0 100644 --- a/src/entry/content/lifecycle/nodes/FrameNode.test.ts +++ b/src/entry/content/lifecycle/nodes/FrameNode.test.ts @@ -9,11 +9,10 @@ import {ContentScriptEvent} from "@typing/content"; import {FrameNode, Node, MountNode, EventNode} from "./index"; import {ContainerRegistry, EventEmitter} from "../context"; import IsolationSetup from "../IsolationSetup"; -import {getContentScriptStylesRuntime} from "./isolated-styles"; +import {readContentStyles} from "#adnbn/runtime"; -jest.mock("@addon-core/browser", () => ({getUrl: (file: string) => `chrome-extension://fixture/${file}`})); jest.mock("#adnbn/page", () => ({aliases: {panel: "panel.html"}})); -jest.mock("./isolated-styles", () => ({getContentScriptStylesRuntime: jest.fn()})); +jest.mock("#adnbn/runtime", () => ({readContentStyles: jest.fn()})); test("FrameNode explains a disconnected host and allows mounting again once the mounter connects it", () => { const runtime = { @@ -23,7 +22,7 @@ test("FrameNode explains a disconnected host and allows mounting again once the delete: jest.fn(), load: jest.fn(), }; - jest.mocked(getContentScriptStylesRuntime).mockReturnValue(runtime); + jest.mocked(readContentStyles).mockReturnValue(runtime); const anchor = document.createElement("section"); const container = document.createElement("div"); document.body.append(anchor); @@ -158,7 +157,7 @@ test("FrameNode replaces lost targets, unregisters old styles and releases its l delete: jest.fn(), load: jest.fn(), }; - jest.mocked(getContentScriptStylesRuntime).mockReturnValue(runtime); + jest.mocked(readContentStyles).mockReturnValue(runtime); const anchor = document.createElement("section"); document.body.append(anchor); const recover = jest.fn(); @@ -246,7 +245,7 @@ test.each(["throw", "unmount"])("FrameNode cleans up when target creation trigge delete: jest.fn(), load: jest.fn(), }; - jest.mocked(getContentScriptStylesRuntime).mockReturnValue(runtime); + jest.mocked(readContentStyles).mockReturnValue(runtime); const anchor = document.createElement("section"); document.body.append(anchor); @@ -306,7 +305,7 @@ const createBoundaryNode = ( delete: jest.fn(), load: jest.fn(), }; - jest.mocked(getContentScriptStylesRuntime).mockReturnValue(runtime); + jest.mocked(readContentStyles).mockReturnValue(runtime); const mountedNode = new MountNode( new Node(document.body, document.createElement("section")), @@ -365,7 +364,7 @@ test.each([ expect(frame.style.borderTopWidth).toBe("2px"); expect(frame.getAttribute("src")).toBe( - options.page ? "chrome-extension://fixture/panel.html" : (options.src ?? null) + options.page ? `chrome-extension://${chrome.runtime.id}/panel.html` : (options.src ?? null) ); frame.dispatchEvent(new Event("load")); diff --git a/src/entry/content/lifecycle/nodes/isolated-styles.test.ts b/src/entry/content/lifecycle/nodes/isolated-styles.test.ts index 35ad06a8..dbe92b47 100644 --- a/src/entry/content/lifecycle/nodes/isolated-styles.test.ts +++ b/src/entry/content/lifecycle/nodes/isolated-styles.test.ts @@ -6,7 +6,6 @@ import type {ContentScriptStylesRuntime} from "@typing/content"; import {getContentScriptStylesRuntime} from "./isolated-styles"; jest.mock("#adnbn/runtime", () => ({readContentStyles: jest.fn()})); -jest.mock("@addon-core/browser", () => ({getUrl: jest.fn()})); beforeEach(() => { jest.resetAllMocks(); @@ -31,5 +30,6 @@ test("initializes and returns the styles runtime supplied by the facade", () => expect(getContentScriptStylesRuntime()).toBe(runtime); expect(runtime.initialize).toHaveBeenCalledWith(getUrl); + expect(getUrl("style.css")).toBe(`chrome-extension://${chrome.runtime.id}/style.css`); expect(runtime.add).not.toHaveBeenCalled(); }); diff --git a/src/entry/relay/Builder.test.ts b/src/entry/relay/Builder.test.ts index ae36088d..0cf53fb5 100644 --- a/src/entry/relay/Builder.test.ts +++ b/src/entry/relay/Builder.test.ts @@ -2,23 +2,17 @@ import Builder from "./Builder"; import VanillaBuilder from "../content/adapters/vanilla/Builder"; import MountBuilder from "../content/lifecycle/MountBuilder"; import RelayManager from "@relay/RelayManager"; -import {isRelayContext} from "@relay/utils"; import {RelayAllFrames, RelayMethod, type RelayOptions} from "@typing/relay"; import type {ContentScriptDefinition} from "@typing/content"; -// Extend the shared random-ID mock for content marker attributes. +// Keep content marker IDs deterministic for lifecycle assertions. jest.mock("nanoid", () => ({nanoid: jest.fn(() => "mocked-id"), customAlphabet: () => () => "relaymarker"})); describe("Relay Builder", () => { - const manager = RelayManager.getInstance(); + let manager: ReturnType; beforeEach(() => { - // The shared harness mocks context detection; exercise its actual predicate here. - jest.mocked(isRelayContext).mockImplementation( - jest.requireActual("@relay/utils").isRelayContext - ); - - manager.clear(); + manager = RelayManager.getInstance(); }); afterEach(() => { diff --git a/src/entry/relay/Builder.ts b/src/entry/relay/Builder.ts index bb038896..c6a89e2c 100644 --- a/src/entry/relay/Builder.ts +++ b/src/entry/relay/Builder.ts @@ -4,8 +4,13 @@ import TransportBuilder from "./TransportBuilder"; import EntrypointBuilder from "../core/Builder"; -import {RelayUnresolvedDefinition} from "@typing/relay"; -import {ContentScriptBuilder, ContentScriptDefinition, ContentScriptIsolation} from "@typing/content"; +import {RelayDefinition, RelayUnresolvedDefinition} from "@typing/relay"; +import { + ContentScriptBuilder, + ContentScriptBuilderConstructor, + ContentScriptDefinition, + ContentScriptIsolation, +} from "@typing/content"; import {TransportType} from "@typing/transport"; export default class Builder< @@ -18,9 +23,20 @@ export default class Builder< protected readonly _transport: TransportBuilder; protected readonly _content: ContentScriptBuilder; + // Preserve the named definition during inference instead of expanding its DOM option unions. + constructor( + definition: RelayDefinition, + contentBuilder: ContentScriptBuilderConstructor + ); + + constructor( + definition: RelayUnresolvedDefinition, + contentBuilder: ContentScriptBuilderConstructor + ); + constructor( protected readonly definition: RelayUnresolvedDefinition, - contentBuilder: new (definition: ContentScriptDefinition>) => ContentScriptBuilder + contentBuilder: ContentScriptBuilderConstructor ) { super(); diff --git a/src/entry/relay/index.test.ts b/src/entry/relay/index.test.ts index 6ea77c71..c9198647 100644 --- a/src/entry/relay/index.test.ts +++ b/src/entry/relay/index.test.ts @@ -1,14 +1,13 @@ import relay, {resolveDefinition} from "./index"; import VanillaBuilder from "../content/adapters/vanilla/Builder"; import RelayManager from "@relay/RelayManager"; -import {isRelayContext} from "@relay/utils"; import {RelayMethod} from "@typing/relay"; import type {ContentScriptDefinition} from "@typing/content"; jest.mock("nanoid", () => ({nanoid: jest.fn(() => "mocked-id"), customAlphabet: () => () => "relaymarker"})); describe("Relay startup", () => { - const manager = RelayManager.getInstance(); + let manager: ReturnType; let content: VanillaBuilder | undefined; class ContentBuilder extends VanillaBuilder { @@ -19,11 +18,7 @@ describe("Relay startup", () => { } beforeEach(() => { - // Use the real predicate through the existing context mock in the shared harness. - jest.mocked(isRelayContext).mockImplementation( - jest.requireActual("@relay/utils").isRelayContext - ); - manager.clear(); + manager = RelayManager.getInstance(); content = undefined; }); @@ -80,7 +75,10 @@ describe("Relay startup", () => { { method: RelayMethod.Scripting, init: () => { - if (phase === "init") throw error; + if (phase === "init") { + throw error; + } + return {ready: true}; }, main: async () => { diff --git a/src/entry/relay/index.ts b/src/entry/relay/index.ts index 74fbf0b8..39b43a46 100644 --- a/src/entry/relay/index.ts +++ b/src/entry/relay/index.ts @@ -1,21 +1,40 @@ import Builder from "./Builder"; -import type {RelayUnresolvedDefinition} from "@typing/relay"; +import type {RelayDefinition, RelayUnresolvedDefinition} from "@typing/relay"; import type {TransportType} from "@typing/transport"; -import type {ContentScriptIsolation} from "@typing/content"; +import type {ContentScriptBuilderConstructor, ContentScriptIsolation} from "@typing/content"; export {Builder}; export {resolveDefinition} from "./resolvers/definition"; export type {RelayUnresolvedDefinition} from "@typing/relay"; +// Match Builder's full-definition overload before the partial bootstrap input. +export default function relay< + T extends TransportType, + Data = unknown, + Isolation extends `${ContentScriptIsolation}` = `${ContentScriptIsolation}`, +>( + definition: RelayDefinition, + contentBuilder: ContentScriptBuilderConstructor +): void; + +export default function relay< + T extends TransportType, + Data = unknown, + Isolation extends `${ContentScriptIsolation}` = `${ContentScriptIsolation}`, +>( + definition: RelayUnresolvedDefinition, + contentBuilder: ContentScriptBuilderConstructor +): void; + export default function relay< T extends TransportType, Data = unknown, Isolation extends `${ContentScriptIsolation}` = `${ContentScriptIsolation}`, >( definition: RelayUnresolvedDefinition, - contentBuilder: ConstructorParameters>>[1] + contentBuilder: ContentScriptBuilderConstructor ): void { new Builder(definition, contentBuilder).build().catch(error => { console.error("Failed to build relay: ", error); diff --git a/src/locale/README.md b/src/locale/README.md index 9b979e5b..93f6f361 100644 --- a/src/locale/README.md +++ b/src/locale/README.md @@ -13,13 +13,13 @@ description: Runtime translation providers, shared formatting, catalogue access, | `providers/AbstractLocale.ts` | Common string rendering, missing-message diagnostics, and language names. | | `providers/NativeLocale.ts` | Browser i18n message lookup and a singleton used by the public helpers. | | `providers/DynamicLocale.ts` | Compiled translations, language state per instance, and optional storage synchronization. | -| `providers/ObservableLocale.ts` | Observable dynamic state, immutable snapshots, shared instances, and subscription lifetimes. | +| `observers/ObservableLocale.ts` | Observable dynamic state, immutable snapshots, shared instances, and subscription lifetimes. | | `providers/CustomLocale.ts` | Internal provider for an explicitly supplied language and flat message dictionary; not re-exported by the public entrypoint. | | `helpers.ts` | Native shortcuts: `t`, `choice`, `key` for browser message references, and `resolve` for strings prefixed with `@`. | | `catalogue/` | Empty package fallback and typing for the generated data module. | | `storage/` | Built-in `LocaleStorage` implementation of the public `LocaleStorageDriver` contract. | | `adapters/react/` | React node rendering, native and dynamic locale hooks, and DOM language attributes through `useLocaleAttributes`. | -| `index.ts` | Public providers, helpers, and selected shared contracts. | +| `index.ts` | Public providers, observers, helpers, and selected shared contracts. | React hooks and their tests live in `adapters/react/hooks/`. The adapter's `index.ts` re-exports the hooks through `adnbn/locale/react`. @@ -111,6 +111,8 @@ Construction does not read or watch storage. The first subscriber connects one s `change()` publishes immediately and serializes persistence, including repeated selections of the current language. Old reads and storage echoes cannot overwrite a pending local selection. After successful writes, the provider reconciles with current storage. Save failures reject the returned promise without rolling back the selected language. Errors from automatic synchronization are logged; explicit `sync()` errors propagate to its caller. +A rejected `change()` does not mean that the language stayed unchanged: subscribers have already seen the optimistic selection. Once pending writes finish, a later `sync()` or storage notification may replace that selection with the saved language. + ## Dynamic translations in React `useLocale(storage?)` returns `LocaleReactDynamicContract`, extending the native React translation contract with `change(lang): Promise`. It connects the shared `ObservableLocale` through React's `useSyncExternalStore`. Components using the same instance update together on local selections and storage changes. The hook renders immediately using the initial language, then restores a saved selection asynchronously. It does not modify DOM attributes. diff --git a/src/locale/adapters/react/hooks/use-locale-attributes.test.ts b/src/locale/adapters/react/hooks/use-locale-attributes.test.ts index 3f45d0ea..3914a0db 100644 --- a/src/locale/adapters/react/hooks/use-locale-attributes.test.ts +++ b/src/locale/adapters/react/hooks/use-locale-attributes.test.ts @@ -1,4 +1,5 @@ -jest.mock("@addon-core/browser", () => ({getI18nMessage: jest.fn(() => "en")})); +import {getBrowserTest} from "@tests/browser-harness/session"; + jest.mock("#adnbn/locale", () => require("./tests/fixtures/dynamic-locale")); import {createElement, StrictMode} from "react"; @@ -17,12 +18,15 @@ const arabic: Pick = { dir: LocaleDir.RightToLeft, }; +beforeEach(() => { + getBrowserTest().harness.configurable.chrome.i18n.getMessage.setImplementation(() => "en"); +}); + afterEach(() => { cleanup(); document.documentElement.removeAttribute("lang"); document.documentElement.removeAttribute("dir"); document.body.replaceChildren(); - jest.restoreAllMocks(); }); test("defaults to html and restores existing and absent attributes in StrictMode", () => { diff --git a/src/locale/adapters/react/hooks/use-locale.test.ts b/src/locale/adapters/react/hooks/use-locale.test.ts index 2f69210b..e2fd8cd4 100644 --- a/src/locale/adapters/react/hooks/use-locale.test.ts +++ b/src/locale/adapters/react/hooks/use-locale.test.ts @@ -1,13 +1,12 @@ -jest.mock("@addon-core/browser", () => ({ - getI18nMessage: jest.fn((key: string) => (key === "locale" ? "en" : "Native {{name}}")), -})); +import {getBrowserTest} from "@tests/browser-harness/session"; + jest.mock("#adnbn/locale", () => require("./tests/fixtures/dynamic-locale")); import {createElement, StrictMode, useState} from "react"; import {renderToString} from "react-dom/server"; import {act, cleanup, fireEvent, render, renderHook, waitFor} from "@testing-library/react"; import {useLocale, useNativeLocale, type LocaleReactDynamicContract} from "../index"; -import {ObservableLocale} from "@locale/providers"; +import {ObservableLocale} from "@locale/observers"; import {MemoryLocaleStorage} from "@locale/tests/fixtures"; import {Language, LocaleDir, type LocaleStorageDriver} from "@typing/locale"; @@ -17,9 +16,14 @@ import type {Structure} from "./tests/fixtures/dynamic-locale"; const useFixtureLocale = (storage?: LocaleStorageDriver | false) => useLocale(storage) as LocaleReactDynamicContract; +beforeEach(() => { + getBrowserTest().harness.configurable.chrome.i18n.getMessage.setImplementation(key => + key === "locale" ? "en" : "Native {{name}}" + ); +}); + afterEach(() => { cleanup(); - jest.restoreAllMocks(); }); test("two roots share an instance, update immediately and keep previous render functions consistent", async () => { diff --git a/src/locale/adapters/react/hooks/use-locale.ts b/src/locale/adapters/react/hooks/use-locale.ts index 39d0638a..73a8da4e 100644 --- a/src/locale/adapters/react/hooks/use-locale.ts +++ b/src/locale/adapters/react/hooks/use-locale.ts @@ -2,7 +2,7 @@ import {useMemo, useSyncExternalStore} from "react"; import ReactLocale from "../ReactLocale"; -import {ObservableLocale} from "@locale/providers"; +import {ObservableLocale} from "@locale/observers"; import type {LocaleReactDynamicContract} from "../types"; import type {LocaleRegistry, LocaleStorageDriver} from "@typing/locale"; diff --git a/src/locale/adapters/react/hooks/use-native-locale.test.ts b/src/locale/adapters/react/hooks/use-native-locale.test.ts index 9479732f..f80acfb2 100644 --- a/src/locale/adapters/react/hooks/use-native-locale.test.ts +++ b/src/locale/adapters/react/hooks/use-native-locale.test.ts @@ -1,4 +1,5 @@ -jest.mock("@addon-core/browser", () => ({getI18nMessage: jest.fn()})); +import {getBrowserTest} from "@tests/browser-harness/session"; + jest.mock("#adnbn/locale", () => ({ keys: Object.keys(require("./tests/fixtures/native-messages.json")).map(key => key.replaceAll("_", ".")), languages: ["en", "fr"], @@ -7,7 +8,6 @@ jest.mock("#adnbn/locale", () => ({ import {createElement, createRef, StrictMode, useState} from "react"; import {createPortal} from "react-dom"; import {cleanup, fireEvent, render, renderHook} from "@testing-library/react"; -import {getI18nMessage} from "@addon-core/browser"; import {useNativeLocale, type LocaleReactContract} from "../index"; import {NativeLocale} from "@locale/providers"; import {Language, LocaleDir} from "@typing/locale"; @@ -28,12 +28,13 @@ const useFixtureLocale = () => useNativeLocale() as LocaleReactContract { beforeEach(() => { - jest.mocked(getI18nMessage).mockImplementation(key => messages[key as keyof typeof messages] ?? ""); + getBrowserTest().harness.configurable.chrome.i18n.getMessage.setImplementation( + key => messages[key as keyof typeof messages] ?? "" + ); }); afterEach(() => { cleanup(); - jest.restoreAllMocks(); }); test("works without a Provider and shares the existing native singleton across roots", () => { diff --git a/src/locale/adapters/react/hooks/use-native-locale.ts b/src/locale/adapters/react/hooks/use-native-locale.ts index bc5efbe3..829580e3 100644 --- a/src/locale/adapters/react/hooks/use-native-locale.ts +++ b/src/locale/adapters/react/hooks/use-native-locale.ts @@ -1,9 +1,13 @@ import {useMemo} from "react"; -import NativeLocale from "@locale/providers/NativeLocale"; + import ReactLocale from "../ReactLocale"; -import type {LocaleRegistry} from "@typing/locale"; + +import {NativeLocale} from "@locale/providers"; + import type {LocaleReactContract} from "../types"; +import type {LocaleRegistry} from "@typing/locale"; + /** Uses browser-selected translations with React substitutions. */ export const useNativeLocale = (): LocaleReactContract => { return useMemo(() => new ReactLocale(NativeLocale.getInstance()), []); diff --git a/src/locale/helpers.test.ts b/src/locale/helpers.test.ts index 01ce02c7..4ef06b59 100644 --- a/src/locale/helpers.test.ts +++ b/src/locale/helpers.test.ts @@ -1,37 +1,27 @@ -jest.mock("@addon-core/browser", () => ({ - getI18nMessage: jest.fn((key: string) => { - const messages: Record = { - locale: "en", - app_title: "App title", - app_greeting: "Hello {{name}}", - }; - - return messages[key] ?? ""; - }), -})); - -import {getI18nMessage} from "@addon-core/browser"; +import {getBrowserTest} from "@tests/browser-harness/session"; import {resolve} from "./index"; jest.mock("#adnbn/locale", () => ({keys: ["locale", "app.title", "app.greeting"], languages: ["en"]})); describe("locale resolve", () => { beforeEach(() => { - jest.clearAllMocks(); - }); + const messages: Record = { + locale: "en", + app_title: "App title", + app_greeting: "Hello {{name}}", + }; - afterEach(() => { - jest.restoreAllMocks(); + getBrowserTest().harness.configurable.chrome.i18n.getMessage.setImplementation(key => messages[key] ?? ""); }); test.each(["Plain title", "", "@"])('leaves "%s" unchanged without reading native messages', input => { expect(resolve(input)).toBe(input); - expect(getI18nMessage).not.toHaveBeenCalled(); + expect(getBrowserTest().harness.configurable.chrome.i18n.getMessage.calls).toHaveLength(0); }); test("resolves runtime markers through the native provider", () => { expect(resolve("@app.title")).toBe("App title"); - expect(getI18nMessage).toHaveBeenCalledWith("app_title"); + expect(getBrowserTest().harness.configurable.chrome.i18n.getMessage.calls.at(-1)?.args[0]).toBe("app_title"); }); test("keeps placeholders when resolving without substitutions", () => { diff --git a/src/locale/index.ts b/src/locale/index.ts index bf6bbc7c..2c5940a8 100644 --- a/src/locale/index.ts +++ b/src/locale/index.ts @@ -1,4 +1,5 @@ export * from "./providers"; +export {ObservableLocale} from "./observers"; export * from "./helpers"; export * from "./storage"; diff --git a/src/locale/providers/ObservableLocale.test.ts b/src/locale/observers/ObservableLocale.test.ts similarity index 79% rename from src/locale/providers/ObservableLocale.test.ts rename to src/locale/observers/ObservableLocale.test.ts index 6861617b..625f51f9 100644 --- a/src/locale/providers/ObservableLocale.test.ts +++ b/src/locale/observers/ObservableLocale.test.ts @@ -1,14 +1,17 @@ -jest.mock("@addon-core/browser", () => ({getI18nMessage: jest.fn(() => "en")})); -jest.mock("#adnbn/locale", () => require("./tests/fixtures/dynamic")); +jest.mock("#adnbn/locale", () => require("@locale/providers/tests/fixtures/dynamic")); -import {getI18nMessage} from "@addon-core/browser"; -import {ObservableLocale} from "@locale/providers"; +import {Storage} from "@addon-core/storage"; +import {PackageName} from "@typing/app"; +import {getBrowserTest} from "@tests/browser-harness/session"; +import {ObservableLocale} from "./index"; import {MemoryLocaleStorage} from "../tests/fixtures"; import {Language, type LocaleDynamicProvider, type LocaleStorageDriver} from "@typing/locale"; -import type {Structure} from "./tests/fixtures/dynamic"; +import type {Structure} from "@locale/providers/tests/fixtures/dynamic"; -afterEach(() => jest.restoreAllMocks()); +beforeEach(() => { + getBrowserTest().harness.configurable.chrome.i18n.getMessage.setResult("en"); +}); test("provides current translations while snapshots retain their original language", async () => { const storage = new MemoryLocaleStorage(); @@ -113,6 +116,7 @@ test("serializes writes, saves equal selections and ignores old storage echoes", await blocked.promise; await originalSet(lang); }); + const first = locale.change(Language.French); const second = locale.change(Language.EnglishGreatBritain); await Promise.resolve(); @@ -200,6 +204,7 @@ test("a failed subscription does not leave a listener behind", async () => { jest.spyOn(storage, "watch").mockImplementationOnce(() => { throw new Error("cannot subscribe"); }); + const locale = new ObservableLocale(storage); const failed = jest.fn(); expect(() => locale.subscribe(failed)).toThrow("cannot subscribe"); @@ -218,8 +223,11 @@ test("a subscriber can select another language without reordering persistence", const locale = new ObservableLocale(storage); let next: Promise | undefined; const stop = locale.subscribe(() => { - if (locale.lang() === Language.French) next = locale.change(Language.EnglishGreatBritain); + if (locale.lang() === Language.French) { + next = locale.change(Language.EnglishGreatBritain); + } }); + const first = locale.change(Language.French); expect(locale.lang()).toBe(Language.EnglishGreatBritain); expect(locale.snapshot().lang()).toBe(Language.EnglishGreatBritain); @@ -301,9 +309,7 @@ test("reports background synchronization errors and still releases the listener" }); test("custom storage and memory mode work when browser i18n is unavailable", async () => { - jest.mocked(getI18nMessage).mockImplementation(() => { - throw new Error("No i18n"); - }); + getBrowserTest().harness.capabilities.set("i18n.getMessage", false); const locale = new ObservableLocale(new MemoryLocaleStorage(Language.English)); expect(locale.lang()).toBe(Language.French); await locale.sync(); @@ -312,5 +318,71 @@ test("custom storage and memory mode work when browser i18n is unavailable", asy await memory.change(Language.English); expect(memory.lang()).toBe(Language.English); await expect(memory.sync()).rejects.toThrow("Language is not saving in storage"); - jest.mocked(getI18nMessage).mockReturnValue("en"); +}); + +test("restores, persists and observes the selected language through real browser storage", async () => { + const session = getBrowserTest(); + const storage = Storage.Local<{locale: Language}>({namespace: PackageName}); + const locale = new ObservableLocale(); + const restored = Promise.withResolvers(); + const externalChange = Promise.withResolvers(); + const listenersBefore = session.harness.storage.onChanged.listenerCount(); + + await storage.set("locale", Language.French); + await session.harness.storage.flushChanges(); + + const stop = locale.subscribe(() => { + if (locale.lang() === Language.French) { + restored.resolve(); + } + + if (locale.lang() === Language.English) { + externalChange.resolve(); + } + }); + + session.addCleanup(stop); + await restored.promise; + expect(locale.trans("app.title")).toBe("Catalogue français"); + expect(session.harness.storage.onChanged.listenerCount()).toBe(listenersBefore + 1); + + const previous = locale.snapshot(); + + await locale.change(Language.EnglishGreatBritain); + await expect(storage.get("locale")).resolves.toBe(Language.EnglishGreatBritain); + expect(previous.lang()).toBe(Language.French); + await storage.set("locale", Language.English); + await session.harness.storage.flushChanges(); + await externalChange.promise; + expect(locale.lang()).toBe(Language.English); + expect(session.harness.storage.local.set.calls).toHaveLength(3); + stop(); + expect(session.harness.storage.onChanged.listenerCount()).toBe(listenersBefore); +}); + +test("preserves browser storage read errors and allows a later synchronization", async () => { + const session = getBrowserTest(); + const storage = Storage.Local<{locale: Language}>({namespace: PackageName}); + const locale = new ObservableLocale(); + + session.harness.storage.local.get.failNext(new Error("Browser read denied")); + + await expect(locale.sync()).rejects.toThrow("Browser read denied"); + expect(locale.lang()).toBe(Language.English); + await storage.set("locale", Language.French); + await expect(locale.sync()).resolves.toBe(Language.French); +}); + +test("continues persistence after a browser storage write error", async () => { + const session = getBrowserTest(); + const storage = Storage.Local<{locale: Language}>({namespace: PackageName}); + const locale = new ObservableLocale(); + + session.harness.storage.local.set.failNext(new Error("Browser write denied")); + + await expect(locale.change(Language.French)).rejects.toThrow("Browser write denied"); + expect(locale.lang()).toBe(Language.French); + await expect(storage.get("locale")).resolves.toBeUndefined(); + await expect(locale.change(Language.EnglishGreatBritain)).resolves.toBe(Language.EnglishGreatBritain); + await expect(storage.get("locale")).resolves.toBe(Language.EnglishGreatBritain); }); diff --git a/src/locale/providers/ObservableLocale.ts b/src/locale/observers/ObservableLocale.ts similarity index 98% rename from src/locale/providers/ObservableLocale.ts rename to src/locale/observers/ObservableLocale.ts index dbf87939..7b228a03 100644 --- a/src/locale/providers/ObservableLocale.ts +++ b/src/locale/observers/ObservableLocale.ts @@ -1,5 +1,5 @@ -import DynamicLocale from "./DynamicLocale"; -import CustomLocale from "./CustomLocale"; +import {DynamicLocale} from "../providers"; +import CustomLocale from "../providers/CustomLocale"; import {LocaleStorage} from "../storage"; @@ -119,6 +119,7 @@ export default class ObservableLocale impleme if (!storage) { this.publish(); + return lang; } diff --git a/src/locale/observers/index.ts b/src/locale/observers/index.ts new file mode 100644 index 00000000..3412a20d --- /dev/null +++ b/src/locale/observers/index.ts @@ -0,0 +1 @@ +export {default as ObservableLocale} from "./ObservableLocale"; diff --git a/src/locale/providers/DynamicLocale.test.ts b/src/locale/providers/DynamicLocale.test.ts index 4a7af6ad..caa4839e 100644 --- a/src/locale/providers/DynamicLocale.test.ts +++ b/src/locale/providers/DynamicLocale.test.ts @@ -1,44 +1,35 @@ -jest.mock("@addon-core/browser", () => ({getI18nMessage: jest.fn(() => "en")})); -jest.mock("@addon-core/storage", () => ({Storage: {Local: jest.fn()}})); jest.mock("#adnbn/locale", () => require("./tests/fixtures/dynamic")); -import {getI18nMessage} from "@addon-core/browser"; -import {Storage, type StorageProvider} from "@addon-core/storage"; +import {getBrowserTest} from "@tests/browser-harness/session"; import DynamicLocale from "./DynamicLocale"; import {MemoryLocaleStorage} from "../tests/fixtures"; import {Language} from "@typing/locale"; import type {Structure} from "./tests/fixtures/dynamic"; -type Watcher = Record void>; const keys = ["greeting", "items", "app.title", "fallback", "empty", "__proto__"]; -let stored: string | undefined; -const listeners = new Set(); -const notify = (key: string, value: string | undefined) => { - for (const watcher of listeners) watcher[key]?.(value); +const i18n = () => getBrowserTest().harness.configurable.chrome.i18n.getMessage; +const storageHarness = () => getBrowserTest().harness.storage; +const stored = () => storageHarness().local.data["adnbn:locale"]; + +const notify = async (value: string | undefined) => { + if (value === undefined) { + await chrome.storage.local.remove("adnbn:locale"); + } else { + await chrome.storage.local.set({"adnbn:locale": value}); + } + + await storageHarness().flushChanges(); }; -const driver = { - get: jest.fn(async (_key: string) => stored), - set: jest.fn(async (key: string, value: string) => { - stored = value; - notify(key, value); - }), - watch: jest.fn((watcher: Watcher) => { - listeners.add(watcher); - return () => listeners.delete(watcher); - }), + +const seed = async (value: string | undefined) => { + await notify(value); + // Fixture preparation should not count as a write by the provider under test. + storageHarness().local.set.reset(); }; beforeEach(() => { - stored = undefined; - listeners.clear(); - jest.clearAllMocks(); - jest.mocked(getI18nMessage).mockReturnValue("en"); - jest.mocked(Storage.Local).mockReturnValue(driver as unknown as StorageProvider>); -}); - -afterEach(() => { - jest.restoreAllMocks(); + i18n().setResult("en"); }); test("reads the initial language from browser i18n and translates synchronously from the catalogue", () => { @@ -49,10 +40,10 @@ test("reads the initial language from browser i18n and translates synchronously expect(locale.trans("empty")).toBe(""); expect(locale.trans("__proto__")).toBe("Ordinary message"); expect(locale.choice("items", 2, {count: 2})).toBe("2 items"); - expect(getI18nMessage).toHaveBeenCalledTimes(1); - expect(getI18nMessage).toHaveBeenCalledWith("locale"); - expect(driver.get).not.toHaveBeenCalled(); - expect(driver.watch).not.toHaveBeenCalled(); + expect(i18n().calls).toHaveLength(1); + expect(i18n().calls[0].args[0]).toBe("locale"); + expect(storageHarness().local.get.calls).toHaveLength(0); + expect(storageHarness().onChanged.listenerCount()).toBe(0); }); test("exposes completed messages while retaining previously selected dictionaries", async () => { @@ -81,36 +72,35 @@ test("exposes completed messages while retaining previously selected dictionarie }); test("normalizes the native marker before selecting catalogue data", () => { - jest.mocked(getI18nMessage).mockReturnValue("en-GB"); + i18n().setResult("en-GB"); const locale = new DynamicLocale(false); expect(locale.lang()).toBe(Language.English); expect(locale.trans("app.title")).toBe("Catalogue"); }); test.each([undefined, "", "unknown"])("uses the build language when the native marker is invalid: %s", marker => { - jest.mocked(getI18nMessage).mockReturnValue(marker); + i18n().setImplementation(() => marker as string); const locale = new DynamicLocale(false); expect(locale.lang()).toBe(Language.French); expect(locale.trans("greeting", {name: "Ada"})).toBe("Bonjour Ada"); }); test("uses the build language when browser i18n is unavailable and changes synchronously in memory", async () => { - jest.mocked(getI18nMessage).mockImplementationOnce(() => { - throw new TypeError("Cannot read properties of undefined (reading 'getMessage')"); - }); + getBrowserTest().harness.capabilities.set("i18n.getMessage", false); const locale = new DynamicLocale(false); expect(locale.lang()).toBe(Language.French); expect(locale.trans("greeting", {name: "Ada"})).toBe("Bonjour Ada"); const changed = locale.change(Language.English); expect(locale.trans("greeting", {name: "Ada"})).toBe("Hello Ada"); await expect(changed).resolves.toBe(Language.English); - expect(Storage.Local).not.toHaveBeenCalled(); - expect(getI18nMessage).toHaveBeenCalledTimes(1); + expect(storageHarness().local.get.calls).toHaveLength(0); + expect(storageHarness().onChanged.listenerCount()).toBe(0); + expect(i18n().calls).toHaveLength(0); }); test("rejects a recognized native language that is absent from the catalogue", () => { const marker = "de"; - jest.mocked(getI18nMessage).mockReturnValue(marker); + i18n().setResult(marker); expect(() => new DynamicLocale(false)).toThrow('Language "de" is not available'); }); @@ -127,52 +117,63 @@ test("keeps public dot keys separate from catalogue keys and exposes available l expect(locale.keys().size).toBe(keys.length); }); -test("selects without saving and throws synchronously for an unavailable language without changing state", () => { - stored = Language.English; +test("selects without saving and throws synchronously for an unavailable language without changing state", async () => { + await seed(Language.English); const locale = new DynamicLocale(); expect(locale.select(Language.French)).toBe(Language.French); expect(locale.lang()).toBe(Language.French); expect(locale.trans("greeting", {name: "Ada"})).toBe("Bonjour Ada"); - expect(driver.set).not.toHaveBeenCalled(); - expect(stored).toBe(Language.English); + expect(storageHarness().local.set.calls).toHaveLength(0); + expect(stored()).toBe(Language.English); const messages = locale.messages(); expect(() => locale.select(Language.German)).toThrow('Language "de" is not available'); expect(locale.lang()).toBe(Language.French); expect(locale.messages()).toBe(messages); - expect(driver.set).not.toHaveBeenCalled(); - expect(stored).toBe(Language.English); + expect(storageHarness().local.set.calls).toHaveLength(0); + expect(stored()).toBe(Language.English); }); test("switches messages immediately while waiting for storage persistence", async () => { const write = Promise.withResolvers(); - driver.set.mockImplementationOnce(() => write.promise); - const locale = new DynamicLocale(); + const storage = new MemoryLocaleStorage(); + const persist = storage.set.bind(storage); + const save = jest.spyOn(storage, "set").mockImplementationOnce(async lang => { + await write.promise; + await persist(lang); + }); + + const locale = new DynamicLocale(storage); const saved = locale.change(Language.French); expect(locale.lang()).toBe(Language.French); expect(locale.trans("greeting", {name: "Ada"})).toBe("Bonjour Ada"); expect(locale.choice("items", 0, {count: 0})).toBe("0 article"); expect(locale.trans("fallback")).toBe("Shared default message"); expect(locale.trans("empty")).toBe(""); - expect(driver.set).toHaveBeenCalledWith("locale", Language.French); + expect(save).toHaveBeenCalledWith(Language.French); + expect(storage.value).toBeUndefined(); write.resolve(); await expect(saved).resolves.toBe(Language.French); + expect(storage.value).toBe(Language.French); }); test("persists a repeated selection of the current language under the framework namespace and locale key", async () => { const locale = new DynamicLocale(); await locale.change(Language.English); - expect(Storage.Local).toHaveBeenCalledWith({namespace: "adnbn"}); - expect(driver.set).toHaveBeenCalledWith("locale", Language.English); - expect(stored).toBe(Language.English); + expect(storageHarness().local.data).toEqual({"adnbn:locale": Language.English}); + expect(storageHarness().local.set.calls.at(-1)?.args).toEqual([{"adnbn:locale": Language.English}]); + expect(stored()).toBe(Language.English); }); test("rejects write errors without rolling back a newer selection", async () => { const write = Promise.withResolvers(); - driver.set.mockImplementationOnce(() => write.promise); - const locale = new DynamicLocale(); + const storage = new MemoryLocaleStorage(); + + jest.spyOn(storage, "set").mockImplementationOnce(() => write.promise); + + const locale = new DynamicLocale(storage); const first = locale.change(Language.French); await locale.change(Language.EnglishGreatBritain); const error = new Error("Storage write failed"); @@ -189,34 +190,38 @@ test("ignores prototype properties and rejects languages absent from the applica await expect(locale.change(Language.German)).rejects.toThrow('Language "de" is not available'); await expect(locale.change("__proto__" as Language)).rejects.toThrow("is not available"); expect(locale.lang()).toBe(Language.English); - expect(driver.set).not.toHaveBeenCalled(); + expect(storageHarness().local.set.calls).toHaveLength(0); }); test("restores the saved language only through explicit sync, without writing it back", async () => { - stored = Language.French; + await seed(Language.French); const locale = new DynamicLocale(); expect(locale.lang()).toBe(Language.English); - expect(driver.get).not.toHaveBeenCalled(); + expect(storageHarness().local.get.calls).toHaveLength(0); await expect(locale.sync()).resolves.toBe(Language.French); - expect(driver.get).toHaveBeenCalledWith("locale"); - expect(driver.set).not.toHaveBeenCalled(); + expect(storageHarness().local.get.calls.at(-1)?.args).toEqual(["adnbn:locale"]); + expect(storageHarness().local.set.calls).toHaveLength(0); }); test.each([undefined, "de"])("keeps the current language for invalid or absent storage: %s", async value => { - stored = value; + await seed(value); const warn = jest.spyOn(console, "warn").mockImplementation(); const locale = new DynamicLocale(); await expect(locale.sync()).resolves.toBe(Language.English); - expect(driver.set).not.toHaveBeenCalled(); - if (value) expect(warn).toHaveBeenCalledWith(`Incorrect language code in storage - "${value}"`); - else expect(warn).not.toHaveBeenCalled(); + expect(storageHarness().local.set.calls).toHaveLength(0); + + if (value) { + expect(warn).toHaveBeenCalledWith(`Incorrect language code in storage - "${value}"`); + } else { + expect(warn).not.toHaveBeenCalled(); + } }); test("propagates storage read errors without changing the language", async () => { const error = new Error("Storage read failed"); - driver.get.mockRejectedValueOnce(error); + storageHarness().local.get.failNext(error); const locale = new DynamicLocale(); - await expect(locale.sync()).rejects.toBe(error); + await expect(locale.sync()).rejects.toThrow(error.message); expect(locale.lang()).toBe(Language.English); }); @@ -224,43 +229,49 @@ test("applies external changes and own storage events without another write", as const locale = new DynamicLocale(); const handler = jest.fn(); const stop = locale.watch(handler); - notify("locale", Language.French); + + getBrowserTest().addCleanup(stop); + await notify(Language.French); expect(locale.lang()).toBe(Language.French); expect(handler).toHaveBeenLastCalledWith(Language.French); - expect(driver.set).not.toHaveBeenCalled(); + expect(storageHarness().local.set.calls).toHaveLength(1); await locale.change(Language.English); + await storageHarness().flushChanges(); expect(handler).toHaveBeenLastCalledWith(Language.English); - expect(driver.set).toHaveBeenCalledTimes(1); + expect(storageHarness().local.set.calls).toHaveLength(2); expect(handler).toHaveBeenCalledTimes(2); stop(); stop(); - notify("locale", Language.French); + await notify(Language.French); expect(locale.lang()).toBe(Language.English); - expect(listeners.size).toBe(0); + expect(storageHarness().onChanged.listenerCount()).toBe(0); }); test("retains the single-listener contract and permits subscribing again after unwatch", () => { const locale = new DynamicLocale(); + getBrowserTest().addCleanup(() => locale.unwatch()); locale.watch(); expect(() => locale.watch()).toThrow("Already subscribed"); locale.unwatch(); locale.watch(); - expect(listeners.size).toBe(1); + expect(storageHarness().onChanged.listenerCount()).toBe(1); locale.unwatch(); - expect(listeners.size).toBe(0); + expect(storageHarness().onChanged.listenerCount()).toBe(0); }); -test("reports invalid external changes without changing state or notifying the handler", () => { +test("reports invalid external changes without changing state or notifying the handler", async () => { const locale = new DynamicLocale(); const error = jest.spyOn(console, "error").mockImplementation(); const handler = jest.fn(); const stop = locale.watch(handler); - notify("locale", Language.German); - notify("locale", undefined); + + getBrowserTest().addCleanup(stop); + await notify(Language.German); + await notify(undefined); expect(error).toHaveBeenCalledWith("Error while changing language:", expect.any(Error)); expect(handler).not.toHaveBeenCalled(); expect(locale.lang()).toBe(Language.English); - expect(driver.set).not.toHaveBeenCalled(); + expect(storageHarness().local.set.calls).toHaveLength(1); stop(); }); @@ -269,17 +280,16 @@ test("supports memory-only changes without creating storage", async () => { const changed = locale.change(Language.French); expect(locale.lang()).toBe(Language.French); await expect(changed).resolves.toBe(Language.French); - expect(Storage.Local).not.toHaveBeenCalled(); - expect(driver.set).not.toHaveBeenCalled(); + expect(storageHarness().local.get.calls).toHaveLength(0); + expect(storageHarness().onChanged.listenerCount()).toBe(0); + expect(storageHarness().local.set.calls).toHaveLength(0); await expect(locale.sync()).rejects.toThrow("Language is not saving in storage"); expect(() => locale.watch()).toThrow("Language is not saved in storage"); expect(() => locale.unwatch()).not.toThrow(); }); test("uses a custom driver without constructing extension storage, including without browser i18n", async () => { - jest.mocked(getI18nMessage).mockImplementationOnce(() => { - throw new TypeError("Browser i18n is unavailable"); - }); + getBrowserTest().harness.capabilities.set("i18n.getMessage", false); const storage = new MemoryLocaleStorage(Language.English); const write = jest.spyOn(storage, "set"); const locale = new DynamicLocale(storage); @@ -287,6 +297,8 @@ test("uses a custom driver without constructing extension storage, including wit await expect(locale.sync()).resolves.toBe(Language.English); const handler = jest.fn(); const stop = locale.watch(handler); + + getBrowserTest().addCleanup(stop); const saved = locale.change(Language.French); expect(locale.trans("greeting", {name: "Ada"})).toBe("Bonjour Ada"); await expect(saved).resolves.toBe(Language.French); @@ -297,7 +309,8 @@ test("uses a custom driver without constructing extension storage, including wit await storage.set(Language.English); expect(handler).toHaveBeenCalledTimes(1); expect(locale.lang()).toBe(Language.French); - expect(Storage.Local).not.toHaveBeenCalled(); + expect(storageHarness().local.get.calls).toHaveLength(0); + expect(storageHarness().onChanged.listenerCount()).toBe(0); }); test("shares a supplied driver while keeping different drivers independent", async () => { diff --git a/src/locale/providers/NativeLocale.test.ts b/src/locale/providers/NativeLocale.test.ts index ec2ca089..4eec796f 100644 --- a/src/locale/providers/NativeLocale.test.ts +++ b/src/locale/providers/NativeLocale.test.ts @@ -1,9 +1,6 @@ -jest.mock("@addon-core/browser", () => ({ - getI18nMessage: jest.fn(), -})); jest.mock("#adnbn/locale", () => ({keys: ["demo.empty", "demo.title"], languages: ["en", "fr", "ru"]})); -import {getI18nMessage} from "@addon-core/browser"; +import {getBrowserTest} from "@tests/browser-harness/session"; import NativeLocale from "./NativeLocale"; interface Structure { @@ -17,21 +14,18 @@ describe("NativeLocale", () => { let error: jest.SpyInstance; beforeEach(() => { - jest.mocked(getI18nMessage) - .mockReset() - .mockImplementation(key => (key === "locale" ? "ru" : "")); + getBrowserTest().harness.configurable.chrome.i18n.getMessage.setImplementation(key => + key === "locale" ? "ru" : "" + ); + warn = jest.spyOn(console, "warn").mockImplementation(); error = jest.spyOn(console, "error").mockImplementation(); locale = new NativeLocale(); }); - afterEach(() => { - jest.restoreAllMocks(); - }); - test("preserves an empty message for a known build key without warning", () => { expect(locale.trans("demo.empty")).toBe(""); - expect(getI18nMessage).toHaveBeenLastCalledWith("demo_empty"); + expect(getBrowserTest().harness.configurable.chrome.i18n.getMessage.calls.at(-1)?.args[0]).toBe("demo_empty"); expect(warn).not.toHaveBeenCalled(); expect(error).not.toHaveBeenCalled(); }); @@ -43,7 +37,7 @@ describe("NativeLocale", () => { }); test.each(["Translated title", "0"])("preserves the nonempty message %j without consulting build keys", value => { - jest.mocked(getI18nMessage).mockReturnValue(value); + getBrowserTest().harness.configurable.chrome.i18n.getMessage.setResult(value); const keys = jest.spyOn(locale, "keys"); expect(locale.trans("demo.title")).toBe(value); @@ -53,14 +47,17 @@ describe("NativeLocale", () => { }); test("preserves nonempty messages outside the default build contract", () => { - jest.mocked(getI18nMessage).mockReturnValue("Extra translation"); + getBrowserTest().harness.configurable.chrome.i18n.getMessage.setResult("Extra translation"); expect(locale.get("demo.extra" as never)).toBe("Extra translation"); expect(warn).not.toHaveBeenCalled(); }); test("can translate a known message even when the language marker is missing", () => { - jest.mocked(getI18nMessage).mockImplementation(key => (key === "locale" ? "" : "Hello {{name}}")); + getBrowserTest().harness.configurable.chrome.i18n.getMessage.setImplementation(key => + key === "locale" ? "" : "Hello {{name}}" + ); + const withoutMarker = new NativeLocale(); expect(withoutMarker.trans("demo.title")).toBe("Hello {{name}}"); @@ -68,7 +65,9 @@ describe("NativeLocale", () => { }); test("does not treat an undefined API result as a valid empty translation", () => { - jest.mocked(getI18nMessage).mockReturnValue(undefined); + getBrowserTest().harness.configurable.chrome.i18n.getMessage.setImplementation( + () => undefined as unknown as string + ); expect(locale.trans("demo.empty")).toBe("demo.empty"); expect(warn).toHaveBeenCalledWith('Locale key "demo.empty" not found in "ru" language.'); @@ -95,6 +94,7 @@ describe("NativeLocale", () => { ["fr", "Français"], ["ru", "Русский"], ]); + expect(error).not.toHaveBeenCalled(); }); }); diff --git a/src/locale/providers/index.ts b/src/locale/providers/index.ts index f9db42bb..481d401d 100644 --- a/src/locale/providers/index.ts +++ b/src/locale/providers/index.ts @@ -1,4 +1,3 @@ export {default as AbstractLocale} from "./AbstractLocale"; export {default as DynamicLocale} from "./DynamicLocale"; export {default as NativeLocale} from "./NativeLocale"; -export {default as ObservableLocale} from "./ObservableLocale"; diff --git a/src/locale/storage/LocaleStorage.test.ts b/src/locale/storage/LocaleStorage.test.ts index 2a310142..5f27c20d 100644 --- a/src/locale/storage/LocaleStorage.test.ts +++ b/src/locale/storage/LocaleStorage.test.ts @@ -1,50 +1,36 @@ -jest.mock("@addon-core/storage", () => ({Storage: {Local: jest.fn()}})); - -import {Storage, type StorageProvider} from "@addon-core/storage"; +import {getBrowserTest} from "@tests/browser-harness/session"; import {LocaleStorage} from "./index"; import {Language} from "@typing/locale"; -type Watcher = {locale?: (value: unknown) => void}; -let value: unknown; -const listeners = new Set(); -const emit = (next: unknown) => { - value = next; - for (const watcher of listeners) watcher.locale?.(next); -}; -const provider = { - get: jest.fn(async (_key: string): Promise => value), - set: jest.fn(async (_key: string, next: Language) => emit(next)), - watch: jest.fn((watcher: Watcher) => { - listeners.add(watcher); - return () => listeners.delete(watcher); - }), -}; +const storageHarness = () => getBrowserTest().harness.storage; -beforeEach(() => { - value = undefined; - listeners.clear(); - jest.clearAllMocks(); - jest.mocked(Storage.Local).mockReturnValue(provider as unknown as StorageProvider<{locale: Language}>); -}); +const emit = async (value: unknown) => { + if (value === undefined) { + await chrome.storage.local.remove("adnbn:locale"); + } else { + await chrome.storage.local.set({"adnbn:locale": value}); + } -afterEach(() => jest.restoreAllMocks()); + await storageHarness().flushChanges(); +}; test("uses the package namespace and locale key without reading or subscribing at construction", async () => { const storage = new LocaleStorage(); - expect(Storage.Local).toHaveBeenCalledWith({namespace: "adnbn"}); - expect(provider.get).not.toHaveBeenCalled(); - expect(provider.watch).not.toHaveBeenCalled(); + + expect(storageHarness().local.get.calls).toHaveLength(0); + expect(storageHarness().onChanged.listenerCount()).toBe(0); await expect(storage.get()).resolves.toBeUndefined(); - expect(provider.get).toHaveBeenCalledWith("locale"); + expect(storageHarness().local.get.calls.at(-1)?.args).toEqual(["adnbn:locale"]); await storage.set(Language.French); - expect(provider.set).toHaveBeenCalledWith("locale", Language.French); + expect(storageHarness().local.set.calls.at(-1)?.args).toEqual([{"adnbn:locale": Language.French}]); + expect(storageHarness().local.data).toEqual({"adnbn:locale": Language.French}); await expect(storage.get()).resolves.toBe(Language.French); }); test.each([Language.English, Language.EnglishGreatBritain, Language.German])( "reads a language independently of the application catalogue: %s", async lang => { - value = lang; + await emit(lang); await expect(new LocaleStorage().get()).resolves.toBe(lang); } ); @@ -56,11 +42,14 @@ test.each([null, "", "unknown", "__proto__", "en-GB", 1, {}, ["en"]])( const storage = new LocaleStorage(); const handler = jest.fn(); const stop = storage.watch(handler); - emit(invalid); + + getBrowserTest().addCleanup(stop); + await emit(invalid); await expect(storage.get()).resolves.toBeUndefined(); expect(handler).not.toHaveBeenCalled(); expect(warn).toHaveBeenCalledWith("[LocaleStorage] Invalid language code in storage:", invalid); - expect(provider.set).not.toHaveBeenCalled(); + // Only the external write; validation must not rewrite the invalid value. + expect(storageHarness().local.set.calls).toHaveLength(1); stop(); } ); @@ -71,27 +60,34 @@ test("notifies every subscriber of own and external selections, ignores deletion const second = jest.fn(); const stopFirst = storage.watch(first); const stopSecond = storage.watch(second); + + getBrowserTest().addCleanup(stopFirst); + getBrowserTest().addCleanup(stopSecond); expect(first).not.toHaveBeenCalled(); - emit(Language.German); + await emit(Language.German); await storage.set(Language.French); - emit(undefined); + await storageHarness().flushChanges(); + await emit(undefined); expect(first.mock.calls).toEqual([[Language.German], [Language.French]]); expect(second.mock.calls).toEqual(first.mock.calls); stopFirst(); stopFirst(); - emit(Language.English); + await emit(Language.English); expect(first).toHaveBeenCalledTimes(2); expect(second).toHaveBeenLastCalledWith(Language.English); stopSecond(); - expect(listeners.size).toBe(0); - expect(provider.set).toHaveBeenCalledTimes(1); + expect(storageHarness().onChanged.listenerCount()).toBe(0); + expect(storageHarness().local.set.calls).toHaveLength(3); }); test("propagates storage errors to the caller", async () => { const error = new Error("Storage denied"); - provider.get.mockRejectedValueOnce(error); - provider.set.mockRejectedValueOnce(error); + + storageHarness().local.get.failNext(error); + storageHarness().local.set.failNext(error); + const storage = new LocaleStorage(); - await expect(storage.get()).rejects.toBe(error); - await expect(storage.set(Language.French)).rejects.toBe(error); + + await expect(storage.get()).rejects.toThrow("Storage denied"); + await expect(storage.set(Language.French)).rejects.toThrow("Storage denied"); }); diff --git a/src/main/config.ts b/src/main/config.ts index 24f06f88..27e518a7 100644 --- a/src/main/config.ts +++ b/src/main/config.ts @@ -1,5 +1,6 @@ import type {Config, ConfigDefinition, ReadonlyConfig, UserConfig} from "@typing/config"; +export type {ActionOptions} from "@typing/action"; export type {Config, ReadonlyConfig, ConfigDefinition, UserConfig}; export const defineConfig = (config: ConfigDefinition): ConfigDefinition => { diff --git a/src/main/relay.ts b/src/main/relay.ts index a986dff1..1ee67be1 100644 --- a/src/main/relay.ts +++ b/src/main/relay.ts @@ -20,6 +20,7 @@ import { export {RelayAllFrames, RelayFrameErrorKind, RelayMethod}; export {RelayDiscoveryError} from "@relay/discovery/RelayDiscovery"; +export {RelayProtocolError} from "@relay/result"; export type { RelayAllFramesOptions, diff --git a/src/message/adapters/react/index.ts b/src/message/adapters/react/index.ts index 9651bc92..8b0bff3e 100644 --- a/src/message/adapters/react/index.ts +++ b/src/message/adapters/react/index.ts @@ -1 +1 @@ -export {default as useMessageHandler} from "./useMessageHandler"; +export {default as useMessageHandler} from "./use-message-handler"; diff --git a/src/message/adapters/react/use-message-handler.test.ts b/src/message/adapters/react/use-message-handler.test.ts new file mode 100644 index 00000000..0b1efda2 --- /dev/null +++ b/src/message/adapters/react/use-message-handler.test.ts @@ -0,0 +1,232 @@ +import React from "react"; +import {act, cleanup, render, renderHook} from "@testing-library/react"; +import type {BrowserContextMessaging} from "@addon-core/browser/testing"; +import {getBrowserTest} from "@tests/browser-harness/session"; +import type {MessageData, MessageTargetHandler, MessageType} from "@typing/message"; +import {Message} from "@message/providers"; +import {useMessageHandler} from "./index"; + +type MessageMap = { + getStringLength: (data: string) => number; + toUpperCase: (str: string) => string; + sayHello: (data?: string) => string; +}; +type TextMessageType = "getStringLength" | "toUpperCase"; +type TextMessageHandler = MessageTargetHandler; +type TextHandlerProps = {type: TextMessageType; handler: TextMessageHandler}; +type SuspendedHandlerProps = {value: string}; +type BooleanMessages = {isEnabled: () => boolean}; + +let caller: BrowserContextMessaging; +let addListener: jest.SpyInstance; +let removeListener: jest.SpyInstance; + +beforeEach(() => { + const session = getBrowserTest(); + const sender = session.harness.contexts.create({kind: "background"}); + + caller = session.harness.messaging.forContext(sender); + // Observe the browser boundary while retaining its real registration behavior. + addListener = jest.spyOn(chrome.runtime.onMessage, "addListener"); + removeListener = jest.spyOn(chrome.runtime.onMessage, "removeListener"); + session.addCleanup(cleanup); +}); + +const listenerCount = () => getBrowserTest().context.onMessage.listenerCount(); + +function send>(type: K, data: MessageData): Promise { + return caller.chrome.runtime.sendMessage({id: "hook-test", type, data, timestamp: 0}); +} + +function useHarness>(type: K, handler: MessageTargetHandler) { + const rendersRef = React.useRef(0); + + rendersRef.current++; + useMessageHandler(type, handler); + + return {renders: rendersRef.current}; +} + +test("registers a handler and responds to another browser context", async () => { + expect(listenerCount()).toBe(0); + + renderHook(() => useMessageHandler<"getStringLength", MessageMap>("getStringLength", str => str.length)); + + expect(listenerCount()).toBe(1); + await expect(send("getStringLength", "test")).resolves.toMatchObject({ok: true, payload: 4}); +}); + +test("preserves a synchronous false response through the hook", async () => { + renderHook(() => useMessageHandler<"isEnabled", BooleanMessages>("isEnabled", () => false)); + + await expect(caller.chrome.runtime.sendMessage({type: "isEnabled"})).resolves.toMatchObject({ + ok: true, + payload: false, + }); +}); + +test.each(["commit", "replace"] as const)( + "keeps the committed handler during a suspended render until its %s", + async completion => { + let ready = false; + let release!: () => void; + let update!: React.Dispatch>; + const pending = new Promise(resolve => { + release = resolve; + }); + + function SuspendedHandler({value}: SuspendedHandlerProps) { + useMessageHandler<"sayHello", MessageMap>("sayHello", () => value); + + if (value === "pending" && !ready) { + throw pending; + } + + return React.createElement("div", null, value); + } + + function Parent() { + const [value, setValue] = React.useState("committed"); + + update = setValue; + + return React.createElement( + React.Suspense, + {fallback: "loading"}, + React.createElement(SuspendedHandler, {value}) + ); + } + + const view = render(React.createElement(Parent)); + + await act(async () => { + React.startTransition(() => update("pending")); + }); + + expect(view.container.textContent).toBe("committed"); + await expect(send("sayHello", undefined)).resolves.toMatchObject({payload: "committed"}); + expect(addListener).toHaveBeenCalledTimes(1); + expect(removeListener).not.toHaveBeenCalled(); + + await act(async () => { + if (completion === "replace") { + update("replacement"); + } else { + ready = true; + release(); + await pending; + } + }); + + const expected = completion === "replace" ? "replacement" : "pending"; + + expect(view.container.textContent).toBe(expected); + await expect(send("sayHello", undefined)).resolves.toMatchObject({payload: expected}); + expect(addListener).toHaveBeenCalledTimes(1); + expect(removeListener).not.toHaveBeenCalled(); + } +); + +test("adds and removes the listener on mount and unmount", async () => { + const {unmount} = renderHook(() => + useMessageHandler<"getStringLength", MessageMap>("getStringLength", str => str.length) + ); + + expect(listenerCount()).toBe(1); + unmount(); + expect(listenerCount()).toBe(0); + await expect(send("getStringLength", "test")).rejects.toThrow("Receiving end does not exist"); +}); + +test("updates the handler without resubscribing and resubscribes on type changes", async () => { + const initialProps: TextHandlerProps = {type: "getStringLength", handler: str => str.length}; + const {rerender} = renderHook( + ({type, handler}: TextHandlerProps) => useMessageHandler(type, handler), + {initialProps} + ); + + await expect(send("getStringLength", "test")).resolves.toMatchObject({payload: 4}); + expect(addListener).toHaveBeenCalledTimes(1); + expect(removeListener).not.toHaveBeenCalled(); + + rerender({type: "getStringLength", handler: str => str.length / 2}); + + expect(addListener).toHaveBeenCalledTimes(1); + expect(removeListener).not.toHaveBeenCalled(); + await expect(send("getStringLength", "test")).resolves.toMatchObject({payload: 2}); + + rerender({type: "toUpperCase", handler: str => str.toUpperCase()}); + + expect(addListener).toHaveBeenCalledTimes(2); + expect(removeListener).toHaveBeenCalledTimes(1); + expect(listenerCount()).toBe(1); + await expect(send("toUpperCase", "test")).resolves.toMatchObject({payload: "TEST"}); + await expect(send("getStringLength", "test")).resolves.toBeUndefined(); +}); + +test("uses Message.getInstance once across rerenders", () => { + const spy = jest.spyOn(Message, "getInstance"); + const {rerender, unmount} = renderHook( + ({handler}) => useMessageHandler<"sayHello", MessageMap>("sayHello", handler), + {initialProps: {handler: (name?: string) => `Hello ${name ?? ""}`.trim()}} + ); + + expect(spy).toHaveBeenCalledTimes(1); + rerender({handler: (name?: string) => `Hi ${name ?? ""}`.trim()}); + expect(spy).toHaveBeenCalledTimes(1); + unmount(); + expect(listenerCount()).toBe(0); +}); + +test("does not rerender on messages when the handler does not update state", async () => { + const {result} = renderHook(() => useHarness("sayHello", (data?: string) => (data ? `Hello ${data}` : "Hello"))); + + expect(result.current.renders).toBe(1); + + await act(async () => { + await expect(send("sayHello", "John")).resolves.toMatchObject({payload: "Hello John"}); + }); + + expect(result.current.renders).toBe(1); +}); + +test("changing the handler adds one render without resubscribing", () => { + const {result, rerender} = renderHook(({handler}) => useHarness("getStringLength", handler), { + initialProps: {handler: (str: string) => str.length}, + }); + const rendersBefore = result.current.renders; + + rerender({handler: str => Math.floor(str.length / 2)}); + + expect(addListener).toHaveBeenCalledTimes(1); + expect(removeListener).not.toHaveBeenCalled(); + expect(result.current.renders).toBe(rendersBefore + 1); +}); + +test("changing the type resubscribes and adds one render", () => { + const initialProps: TextHandlerProps = {type: "getStringLength", handler: str => str.length}; + const {result, rerender} = renderHook(({type, handler}: TextHandlerProps) => useHarness(type, handler), { + initialProps, + }); + const rendersBefore = result.current.renders; + + rerender({type: "toUpperCase", handler: str => str.toUpperCase()}); + + expect(addListener).toHaveBeenCalledTimes(2); + expect(removeListener).toHaveBeenCalledTimes(1); + expect(result.current.renders).toBe(rendersBefore + 1); +}); + +test("keeps one live subscription under StrictMode and removes it on unmount", async () => { + const Wrapper: React.FC<{children: React.ReactNode}> = ({children}) => + React.createElement(React.StrictMode, null, children); + const {unmount} = renderHook(() => useMessageHandler<"sayHello", MessageMap>("sayHello", () => "pong"), { + wrapper: Wrapper, + }); + + expect(listenerCount()).toBe(1); + await expect(send("sayHello", undefined)).resolves.toMatchObject({payload: "pong"}); + unmount(); + expect(listenerCount()).toBe(0); + expect(removeListener.mock.calls.length).toBe(addListener.mock.calls.length); +}); diff --git a/src/message/adapters/react/useMessageHandler.ts b/src/message/adapters/react/use-message-handler.ts similarity index 74% rename from src/message/adapters/react/useMessageHandler.ts rename to src/message/adapters/react/use-message-handler.ts index 801b4542..677ef0e3 100644 --- a/src/message/adapters/react/useMessageHandler.ts +++ b/src/message/adapters/react/use-message-handler.ts @@ -1,4 +1,4 @@ -import {useCallback, useEffect, useRef} from "react"; +import {useCallback, useLayoutEffect, useRef} from "react"; import {MessageDictionary, MessageTargetHandler, MessageType} from "@typing/message"; import {Message} from "@message/providers"; @@ -14,13 +14,17 @@ export default function useMessageHandler, T extends Me } const handlerRef = useRef(handler); - handlerRef.current = handler; + + useLayoutEffect(() => { + // A suspended or abandoned render must not replace the active handler. + handlerRef.current = handler; + }, [handler]); const stableWrapper = useCallback>((data, sender) => { return handlerRef.current(data, sender); }, []); - useEffect(() => { + useLayoutEffect(() => { return messageRef.current!.watch(type, stableWrapper); }, [type, stableWrapper]); } diff --git a/src/message/adapters/react/useMessageHandler.test.ts b/src/message/adapters/react/useMessageHandler.test.ts deleted file mode 100644 index dc5700cf..00000000 --- a/src/message/adapters/react/useMessageHandler.test.ts +++ /dev/null @@ -1,173 +0,0 @@ -import React from "react"; -import {act, renderHook} from "@testing-library/react"; -import useMessageHandler from "./useMessageHandler"; -import {Message} from "../../providers"; - -type MessageMap = { - getStringLength: (data: string) => number; - toUpperCase: (str: string) => string; - sayHello: (data?: string) => string; - fetchUser: (name: string) => Promise<{name: string}>; -}; - -let message: Message; - -beforeEach(() => { - jest.clearAllMocks(); - message = new Message(); - // @ts-ignore - test-only access to manager - message["manager"].clear(); -}); - -function useHarness(type: K, handler: MessageMap[K]) { - const rendersRef = React.useRef(0); - rendersRef.current++; - - // Using explicit generics to match hook signature order - useMessageHandler(type as any, handler as any); - - return {renders: rendersRef.current}; -} - -test("registers a specific handler for a given message type", async () => { - expect(chrome.runtime.onMessage.hasListeners()).toBe(false); - - renderHook(() => useMessageHandler<"getStringLength", MessageMap>("getStringLength", str => str.length)); - - expect(chrome.runtime.onMessage.hasListeners()).toBe(true); - expect(chrome.runtime.onMessage.addListener).toHaveBeenCalledWith(expect.any(Function)); - - const result = await message.send("getStringLength", "test"); - - expect(result).toBe(4); -}); - -test("adds and removes the listener on mount and unmount", async () => { - expect(chrome.runtime.onMessage.hasListeners()).toBe(false); - - const {unmount} = renderHook(() => { - useMessageHandler<"getStringLength", MessageMap>("getStringLength", str => str.length); - }); - - expect(chrome.runtime.onMessage.hasListeners()).toBe(true); - expect(chrome.runtime.onMessage.addListener).toHaveBeenCalledWith(expect.any(Function)); - - unmount(); - - expect(chrome.runtime.onMessage.hasListeners()).toBe(false); - expect(chrome.runtime.onMessage.removeListener).toHaveBeenCalledWith(expect.any(Function)); -}); - -test("does not resubscribe on handler change, resubscribes on type change", async () => { - expect(chrome.runtime.onMessage.hasListeners()).toBe(false); - - const {rerender} = renderHook( - ({type, handler}) => useMessageHandler<"getStringLength" | "toUpperCase", MessageMap>(type as any, handler), - { - initialProps: {type: "getStringLength" as const, handler: (str: string) => str.length}, - } - ); - - expect(chrome.runtime.onMessage.hasListeners()).toBe(true); - - expect(await message.send("getStringLength", "test")).toBe(4); - - const addCallsAfterMount = (chrome.runtime.onMessage.addListener as jest.Mock).mock.calls.length; - const removeCallsAfterMount = (chrome.runtime.onMessage.removeListener as jest.Mock).mock.calls.length; - - rerender({type: "getStringLength", handler: (str: string) => str.length / 2}); - - // No resubscribe on handler change - expect((chrome.runtime.onMessage.removeListener as jest.Mock).mock.calls.length).toBe(removeCallsAfterMount); - expect((chrome.runtime.onMessage.addListener as jest.Mock).mock.calls.length).toBe(addCallsAfterMount); - - expect(await message.send("getStringLength", "test")).toBe(2); - - // Change type -> should resubscribe - // @ts-ignore - rerender({type: "toUpperCase", handler: (str: string) => str.toUpperCase()}); - - expect((chrome.runtime.onMessage.removeListener as jest.Mock).mock.calls.length).toBe(removeCallsAfterMount + 1); - expect((chrome.runtime.onMessage.addListener as jest.Mock).mock.calls.length).toBe(addCallsAfterMount + 1); - - expect(await message.send("toUpperCase", "test")).toBe("TEST"); - expect(await message.send("getStringLength", "test")).toBe(undefined); -}); - -test("uses Message.getInstance and does not recreate between rerenders", () => { - const spy = jest.spyOn(Message, "getInstance"); - - const {rerender, unmount} = renderHook( - ({handler}) => useMessageHandler<"sayHello", MessageMap>("sayHello", handler), - { - initialProps: {handler: (name?: string) => `Hello ${name ?? ""}`.trim()}, - } - ); - - expect(spy).toHaveBeenCalledTimes(1); - - rerender({handler: (name?: string) => `Hi ${name ?? ""}`.trim()}); - - expect(spy).toHaveBeenCalledTimes(1); - - unmount(); -}); - -test("does not rerender on incoming message if handler doesn't touch state", async () => { - const {result} = renderHook(() => useHarness("sayHello", (data?: string) => (data ? `Hello ${data}` : "Hello"))); - - expect(result.current.renders).toBe(1); - - await act(async () => { - await message.send("sayHello", "John"); - }); - - expect(result.current.renders).toBe(1); -}); - -test("changing handler causes no resubscribe and only the expected one rerender", () => { - const {result, rerender} = renderHook(({handler}) => useHarness("getStringLength", handler as any), { - initialProps: {handler: (str: string) => str.length}, - }); - - const addCalls = (chrome.runtime.onMessage.addListener as jest.Mock).mock.calls.length; - const removeCalls = (chrome.runtime.onMessage.removeListener as jest.Mock).mock.calls.length; - const rendersBefore = result.current.renders; - - rerender({handler: (str: string) => Math.floor(str.length / 2)}); - - expect((chrome.runtime.onMessage.addListener as jest.Mock).mock.calls.length).toBe(addCalls); - expect((chrome.runtime.onMessage.removeListener as jest.Mock).mock.calls.length).toBe(removeCalls); - expect(result.current.renders).toBe(rendersBefore + 1); -}); - -test("changing type resubscribes and only one rerender occurs", () => { - const {result, rerender} = renderHook(({type}) => useHarness(type as any, ((s: string) => s.length) as any), { - initialProps: {type: "getStringLength" as const}, - }); - - const addCalls = (chrome.runtime.onMessage.addListener as jest.Mock).mock.calls.length; - const removeCalls = (chrome.runtime.onMessage.removeListener as jest.Mock).mock.calls.length; - const rendersBefore = result.current.renders; - - rerender({type: "toUpperCase" as any}); - - expect((chrome.runtime.onMessage.addListener as jest.Mock).mock.calls.length).toBe(addCalls + 1); - expect((chrome.runtime.onMessage.removeListener as jest.Mock).mock.calls.length).toBe(removeCalls + 1); - expect(result.current.renders).toBe(rendersBefore + 1); -}); - -test("works correctly under StrictMode (no leaked listeners)", () => { - const Wrapper: React.FC<{children: React.ReactNode}> = ({children}) => - React.createElement(React.StrictMode, null, children); - - const {unmount} = renderHook(() => useMessageHandler<"sayHello", MessageMap>("sayHello", () => "pong"), { - wrapper: Wrapper as any, - }); - - expect(chrome.runtime.onMessage.hasListeners()).toBe(true); - - unmount(); - - expect(chrome.runtime.onMessage.hasListeners()).toBe(false); -}); diff --git a/src/message/handlers/MapHandler.ts b/src/message/handlers/MapHandler.ts index 27351621..299498ee 100644 --- a/src/message/handlers/MapHandler.ts +++ b/src/message/handlers/MapHandler.ts @@ -22,7 +22,9 @@ export default class MapHandler extends AbstractHan if (handler) { if (typeof handler === "function") { - return handler(data, sender) || null; + const result = handler(data, sender); + + return result === undefined ? null : result; } else { throw new Error(`Handler for type "${type}" in object of handlers must be a function`); } diff --git a/src/message/handlers/SingleHandler.ts b/src/message/handlers/SingleHandler.ts index c291fbda..dbdbcd28 100644 --- a/src/message/handlers/SingleHandler.ts +++ b/src/message/handlers/SingleHandler.ts @@ -22,7 +22,9 @@ export default class SingleHandler extends Abstract sender: MessageSender ): MessageResponse> | undefined | null { if (type === this.messageType) { - return this.handler(data, sender) || null; + const result = this.handler(data, sender); + + return result === undefined ? null : result; } } } diff --git a/src/message/message-response.test.ts b/src/message/message-response.test.ts new file mode 100644 index 00000000..38c3de67 --- /dev/null +++ b/src/message/message-response.test.ts @@ -0,0 +1,87 @@ +import {createTabFixture} from "@addon-core/browser/testing"; +import {getBrowserTest} from "@tests/browser-harness/session"; +import {MessageResultEnvelopeProperty, type MessageSendOptions} from "@typing/message"; +import {isRemoteMessageError} from "./error"; +import {Message} from "./providers"; + +type ResponseHandler = () => unknown; +type ResponseMessages = {probe: ResponseHandler}; + +describe.each(["runtime", "tab"] as const)("Message response over %s messaging", target => { + let message: Message; + let options: MessageSendOptions | undefined; + let restoreContext: () => void; + + beforeEach(() => { + const session = getBrowserTest(); + + session.harness.tabs.set([createTabFixture({id: 7})]); + + const receiver = session.harness.contexts.create( + target === "runtime" + ? {kind: "background"} + : {kind: "contentScript", tabId: 7, frameId: 0, url: "https://example.test/"} + ); + + restoreContext = session.useContext(receiver); + message = new Message(); + options = target === "runtime" ? undefined : {tabId: 7, frameId: 0}; + }); + + test("preserves the channel error when a general handler sends no response", async () => { + message.watch(() => {}); + restoreContext(); + + const result = message.send("probe", undefined, options); + + await expect(result).rejects.toThrow("The message port closed before a response was received."); + expect(isRemoteMessageError(await result.catch(error => error))).toBe(false); + }); + + describe.each(["single", "map"] as const)("with %s registration", registration => { + const send = (handler: ResponseHandler): Promise => { + if (registration === "single") { + message.watch("probe", handler); + } else { + message.watch({probe: handler}); + } + + restoreContext(); + + return message.send("probe", undefined, options); + }; + + test("returns undefined when an async handler has no return value", async () => { + await expect(send(async () => {})).resolves.toBeUndefined(); + }); + + test("preserves the null response of a sync handler with no return value", async () => { + await expect(send(() => {})).resolves.toBeNull(); + }); + + test.each([null, false, 0, ""])("preserves the sync result %p", async value => { + await expect(send(() => value)).resolves.toBe(value); + }); + + test.each([null, false, 0, ""])("preserves the async result %p", async value => { + await expect(send(async () => value)).resolves.toBe(value); + }); + + test("unwraps only the outer envelope when the payload resembles a response", async () => { + const payload = {[MessageResultEnvelopeProperty]: true, ok: true}; + + await expect(send(async () => payload)).resolves.toEqual(payload); + }); + + test.each([false, true])("preserves remote errors from an async=%s handler", async asynchronous => { + const fail = () => { + throw new TypeError("Response failed"); + }; + const result = send(asynchronous ? async () => fail() : fail); + + await expect(result).rejects.toBeInstanceOf(TypeError); + await expect(result).rejects.toThrow("Response failed"); + expect(isRemoteMessageError(await result.catch(error => error))).toBe(true); + }); + }); +}); diff --git a/src/message/providers/Message.test.ts b/src/message/providers/Message.test.ts index b74f0ca4..1dbc63fb 100644 --- a/src/message/providers/Message.test.ts +++ b/src/message/providers/Message.test.ts @@ -1,6 +1,7 @@ import Message from "./Message"; -import {getBrowserInfo} from "@addon-core/browser"; -import * as env from "@main/env"; +import {createTabFixture, type BrowserContext} from "@addon-core/browser/testing"; +import {getBrowserTest} from "@tests/browser-harness/session"; +import type {MessageData, MessageResponse, MessageSendOptions, MessageType} from "@typing/message"; import {isRemoteMessageError, UnsupportedMessageTargetError} from "../error"; @@ -19,53 +20,94 @@ type MessageMap = { }; let message: Message; -const mockedEnv = env as jest.Mocked; -const mockedGetBrowserInfo = getBrowserInfo as jest.MockedFunction; - -beforeEach(async () => { - jest.clearAllMocks(); - mockedEnv.isBrowser.mockReturnValue(false); - mockedGetBrowserInfo.mockResolvedValue({ - name: "Firefox", - vendor: "Mozilla", - version: "153.0", - buildID: "test", - }); +let receiver: BrowserContext; + +beforeEach(() => { + const session = getBrowserTest(); + + receiver = session.harness.contexts.create({kind: "background"}); + session.useContext(receiver); message = new Message(); - message["manager"].clear(); }); +const caller = () => { + const session = getBrowserTest(); + + return session.harness.messaging.forContext(session.context); +}; + +// Each call completes before restoring the receiver globals used for registrations. +async function sendFromCaller>( + type: K, + data: MessageData, + options?: MessageSendOptions +): Promise> { + const session = getBrowserTest(); + const restore = session.useContext(session.context); + + try { + return await message.send(type, data, options); + } finally { + restore(); + } +} + +function useTabReceiver(): void { + const session = getBrowserTest(); + + session.harness.tabs.set([createTabFixture({id: 123})]); + session.harness.contexts.documents.create({ + documentId: "document-1", + tabId: 123, + frameId: 1, + url: "https://example.test/", + }); + receiver = session.harness.contexts.create({ + kind: "contentScript", + tabId: 123, + frameId: 1, + documentId: "document-1", + url: "https://example.test/", + }); + session.useContext(receiver); +} + +function useFirefox(version = "153.0"): void { + const session = getBrowserTest(); + + session.useContext(receiver, "firefox"); + session.harness.runtime.getBrowserInfo.setResult({name: "Firefox", vendor: "Mozilla", version, buildID: "test"}); +} + describe("watch method", () => { test("adds and removes the message listener on subscribe and unsubscribe", async () => { - expect(chrome.runtime.onMessage.hasListeners()).toBe(false); + expect(receiver.onMessage.listenerCount()).toBe(0); const unsubscribe = message.watch("getStringLength", (str: string) => str.length); - expect(chrome.runtime.onMessage.hasListeners()).toBe(true); - expect(chrome.runtime.onMessage.addListener).toHaveBeenCalledWith(expect.any(Function)); + expect(receiver.onMessage.listenerCount()).toBe(1); unsubscribe(); - expect(chrome.runtime.onMessage.hasListeners()).toBe(false); - expect(chrome.runtime.onMessage.removeListener).toHaveBeenCalled(); + expect(receiver.onMessage.listenerCount()).toBe(0); }); test("registers a specific handler for a given message type", async () => { message.watch("getStringLength", (str: string) => str.length); message.watch("toUpperCase", (str: string) => str.toUpperCase()); - const result_1 = await message.send("getStringLength", "test"); - const result_2 = await message.send("toUpperCase", "test"); + const firstResult = await sendFromCaller("getStringLength", "test"); + const secondResult = await sendFromCaller("toUpperCase", "test"); - expect(result_1).toBe(4); - expect(result_2).toBe("TEST"); - expect(chrome.runtime.onMessage.addListener).toHaveBeenCalledTimes(1); + expect(firstResult).toBe(4); + expect(secondResult).toBe("TEST"); + expect(receiver.onMessage.listenerCount()).toBe(1); }); test("registers async handler and resolves with its returned value", async () => { - message.watch("fetchUser", name => new Promise(resolve => setTimeout(() => resolve({name}), 100))); + message.watch("fetchUser", async name => ({name})); - const result = await message.send("fetchUser", "Tom"); + const result = await sendFromCaller("fetchUser", "Tom"); expect(result).toEqual({name: "Tom"}); }); @@ -76,39 +118,42 @@ describe("watch method", () => { getStringLength: str => str.length, }); - const result_1 = await message.send("getStringLength", "test"); - const result_2 = await message.send("toUpperCase", "test"); + const firstResult = await sendFromCaller("getStringLength", "test"); + const secondResult = await sendFromCaller("toUpperCase", "test"); - expect(result_1).toBe(4); - expect(result_2).toBe("TEST"); - expect(chrome.runtime.onMessage.addListener).toHaveBeenCalledTimes(1); + expect(firstResult).toBe(4); + expect(secondResult).toBe("TEST"); + expect(receiver.onMessage.listenerCount()).toBe(1); }); - test("registers a general handler for all message types", async () => { + test("delivers every type to a general handler even when it sends no response", async () => { + const received: unknown[] = []; + message.watch((type, data) => { - console.log(`TYPE: ${type}, DATA: ${data}`); + received.push({type, data}); }); - const result_1 = await message.send("getStringLength", "test"); - const result_2 = await message.send("toUpperCase", "test"); - - expect(result_1).toBe(undefined); - expect(result_2).toBe(undefined); - expect(chrome.runtime.onMessage.addListener).toHaveBeenCalledTimes(1); + await expect(sendFromCaller("getStringLength", "test")).rejects.toThrow("message port closed"); + await expect(sendFromCaller("toUpperCase", "test")).rejects.toThrow("message port closed"); + expect(received).toEqual([ + {type: "getStringLength", data: "test"}, + {type: "toUpperCase", data: "test"}, + ]); + expect(receiver.onMessage.listenerCount()).toBe(1); }); - test("prioritizes the first registered handler over others for the same message type", async () => { - message.watch("getStringLength", (str: string) => str.length); - message.watch((type, data) => { - console.log(`TYPE: ${type}, DATA: ${data}`); - }); + test("uses the response handler alongside a general observer", async () => { + const received: string[] = []; - const result_1 = await message.send("getStringLength", "test"); - const result_2 = await message.send("toUpperCase", "test"); + message.watch("getStringLength", str => str.length); + message.watch(type => { + received.push(type); + }); - expect(result_1).toBe(4); - expect(result_2).toBe(undefined); - expect(chrome.runtime.onMessage.addListener).toHaveBeenCalledTimes(1); + await expect(sendFromCaller("getStringLength", "test")).resolves.toBe(4); + await expect(sendFromCaller("toUpperCase", "test")).rejects.toThrow("message port closed"); + expect(received).toEqual(["getStringLength", "toUpperCase"]); + expect(receiver.onMessage.listenerCount()).toBe(1); }); }); @@ -116,7 +161,7 @@ describe("send method", () => { test("sends a message and returns the correct response from the handler", async () => { message.watch("getStringLength", str => str.length); - const result = await message.send("getStringLength", "test"); + const result = await sendFromCaller("getStringLength", "test"); expect(result).toBe(4); }); @@ -124,7 +169,7 @@ describe("send method", () => { test("sends a message without data and receive a correct response from the handler", async () => { message.watch("sayHello", () => "Hello"); - const result = await message.send("sayHello", undefined); + const result = await sendFromCaller("sayHello", undefined); expect(result).toBe("Hello"); }); @@ -132,88 +177,152 @@ describe("send method", () => { test("sends a message with correct structure", async () => { message.watch("getStringLength", str => str.length); - const result = await message.send("getStringLength", "test"); + const result = await sendFromCaller("getStringLength", "test"); expect(result).toBe(4); - expect(chrome.runtime.sendMessage).toHaveBeenCalledWith( + expect(caller().runtime.sendMessage.calls[0].args).toEqual([ { id: expect.any(String), type: "getStringLength", data: "test", timestamp: expect.any(Number), }, - expect.any(Function) - ); + ]); }); test("sends a message to tab when options is a number", async () => { + useTabReceiver(); message.watch("getStringLength", str => str.length); - const result = await message.send("getStringLength", "test", 123); + const result = await sendFromCaller("getStringLength", "test", 123); - expect(chrome.tabs.sendMessage).toHaveBeenCalledWith( + expect(caller().tabs.sendMessage.calls[0].args).toEqual([ 123, expect.objectContaining({type: "getStringLength", data: "test"}), - undefined, - expect.any(Function) - ); + {}, + ]); expect(result).toBe(4); }); - test("sends a message to tab when options is a object with tabId, frameId and documentId", async () => { + test("sends a message to the addressed tab, frame and document", async () => { + useTabReceiver(); message.watch("getStringLength", str => str.length); - const result = await message.send("getStringLength", "test", {tabId: 123, frameId: 1, documentId: "1"}); + const result = await sendFromCaller("getStringLength", "test", { + tabId: 123, + frameId: 1, + documentId: "document-1", + }); - expect(chrome.tabs.sendMessage).toHaveBeenCalledWith( + expect(caller().tabs.sendMessage.calls[0].args).toEqual([ 123, expect.objectContaining({type: "getStringLength", data: "test"}), - {frameId: 1, documentId: "1"}, - expect.any(Function) - ); + {frameId: 1, documentId: "document-1"}, + ]); expect(result).toBe(4); }); test("preserves documentId for Firefox 153 and newer", async () => { - mockedEnv.isBrowser.mockReturnValue(true); + useTabReceiver(); + useFirefox(); message.watch("getStringLength", str => str.length); - const result = await message.send("getStringLength", "test", {tabId: 123, frameId: 1, documentId: "1"}); + const result = await sendFromCaller("getStringLength", "test", { + tabId: 123, + frameId: 1, + documentId: "document-1", + }); - expect(chrome.tabs.sendMessage).toHaveBeenCalledWith( + expect(caller().tabs.sendMessage.calls[0].args).toEqual([ 123, expect.objectContaining({type: "getStringLength", data: "test"}), - {frameId: 1, documentId: "1"}, - expect.any(Function) - ); - expect(mockedGetBrowserInfo).toHaveBeenCalledTimes(1); + {frameId: 1, documentId: "document-1"}, + ]); + expect(getBrowserTest().harness.runtime.getBrowserInfo.calls).toHaveLength(1); expect(result).toBe(4); }); test("rejects documentId targeting on Firefox older than 153", async () => { - mockedEnv.isBrowser.mockReturnValue(true); - mockedGetBrowserInfo.mockResolvedValue({ - name: "Firefox", - vendor: "Mozilla", - version: "152.0", - buildID: "test", - }); + useTabReceiver(); + useFirefox("152.0"); await expect( - message.send("getStringLength", "test", {tabId: 123, documentId: "document-1"}) + sendFromCaller("getStringLength", "test", {tabId: 123, documentId: "document-1"}) ).rejects.toBeInstanceOf(UnsupportedMessageTargetError); - expect(chrome.tabs.sendMessage).not.toHaveBeenCalled(); + expect(caller().tabs.sendMessage.calls).toHaveLength(0); }); test("caches the Firefox version used for documentId capability checks", async () => { - mockedEnv.isBrowser.mockReturnValue(true); + useTabReceiver(); + useFirefox(); + message.watch("getStringLength", str => str.length); + + await sendFromCaller("getStringLength", "test", {tabId: 123, documentId: "document-1"}); + await sendFromCaller("getStringLength", "again", {tabId: 123, documentId: "document-1"}); + + expect(getBrowserTest().harness.runtime.getBrowserInfo.calls).toHaveLength(1); + }); + + test.each(["frame", "document"] as const)("delivers only to the selected %s", async selector => { + useTabReceiver(); + message.watch("getStringLength", str => str.length); + + const {harness} = getBrowserTest(); + const sibling = harness.contexts.create({ + kind: "contentScript", + tabId: 123, + frameId: 2, + url: "https://example.test/other", + }); + const received: unknown[] = []; + + sibling.onMessage.on((body, _sender, reply) => { + received.push(body); + reply("wrong frame"); + }); + + const options: MessageSendOptions = + selector === "frame" ? {tabId: 123, frameId: 1} : {tabId: 123, documentId: "document-1"}; + + await expect(sendFromCaller("getStringLength", "test", options)).resolves.toBe(4); + expect(received).toEqual([]); + }); + + test("does not deliver to another tab", async () => { + useTabReceiver(); message.watch("getStringLength", str => str.length); + getBrowserTest().harness.tabs.set([createTabFixture({id: 123}), createTabFixture({id: 456})]); + + const result = sendFromCaller("getStringLength", "test", 456); + + await expect(result).rejects.toThrow("Receiving end does not exist"); + expect(isRemoteMessageError(await result.catch(error => error))).toBe(false); + }); + + test("rejects an unknown Firefox version before sending to a document", async () => { + useTabReceiver(); + useFirefox("unknown"); + + await expect( + sendFromCaller("getStringLength", "test", {tabId: 123, documentId: "document-1"}) + ).rejects.toBeInstanceOf(UnsupportedMessageTargetError); + expect(caller().tabs.sendMessage.calls).toHaveLength(0); + }); + + test("preserves a failed Firefox version lookup as the capability error cause", async () => { + useTabReceiver(); + useFirefox(); + + const cause = new Error("Browser info unavailable"); + + getBrowserTest().harness.runtime.getBrowserInfo.failNext(cause); - await message.send("getStringLength", "test", {tabId: 123, documentId: "document-1"}); - await message.send("getStringLength", "test", {tabId: 123, documentId: "document-2"}); + const result = sendFromCaller("getStringLength", "test", {tabId: 123, documentId: "document-1"}); - expect(mockedGetBrowserInfo).toHaveBeenCalledTimes(1); + await expect(result).rejects.toBeInstanceOf(UnsupportedMessageTargetError); + await expect(result).rejects.toMatchObject({cause: {message: cause.message}}); + expect(caller().tabs.sendMessage.calls).toHaveLength(0); }); test("rejects when a sync handler throws", async () => { @@ -221,11 +330,11 @@ describe("send method", () => { throw new TypeError(data); }); - await expect(message.send("throwSync", "sync boom")).rejects.toMatchObject({ + await expect(sendFromCaller("throwSync", "sync boom")).rejects.toMatchObject({ name: "TypeError", message: "sync boom", }); - await expect(message.send("throwSync", "sync boom")).rejects.toBeInstanceOf(TypeError); + await expect(sendFromCaller("throwSync", "sync boom")).rejects.toBeInstanceOf(TypeError); }); test("marks restored handler errors as remote", async () => { @@ -233,7 +342,7 @@ describe("send method", () => { throw new TypeError(data); }); - const error = await message.send("throwSync", "sync boom").catch(cause => cause); + const error = await sendFromCaller("throwSync", "sync boom").catch(cause => cause); expect(error).toBeInstanceOf(TypeError); expect(isRemoteMessageError(error)).toBe(true); @@ -244,11 +353,11 @@ describe("send method", () => { throw new RangeError(data); }); - await expect(message.send("throwAsync", "async boom")).rejects.toMatchObject({ + await expect(sendFromCaller("throwAsync", "async boom")).rejects.toMatchObject({ name: "RangeError", message: "async boom", }); - await expect(message.send("throwAsync", "async boom")).rejects.toBeInstanceOf(RangeError); + await expect(sendFromCaller("throwAsync", "async boom")).rejects.toBeInstanceOf(RangeError); }); test("rejects when a handler throws a primitive value", async () => { @@ -256,7 +365,7 @@ describe("send method", () => { throw data; }); - await expect(message.send("throwPrimitive", "primitive boom")).rejects.toMatchObject({ + await expect(sendFromCaller("throwPrimitive", "primitive boom")).rejects.toMatchObject({ name: "Error", message: "primitive boom", }); @@ -267,7 +376,7 @@ describe("send method", () => { throw {name: "CustomError", message: data}; }); - await expect(message.send("throwPlainObject", "plain object boom")).rejects.toMatchObject({ + await expect(sendFromCaller("throwPlainObject", "plain object boom")).rejects.toMatchObject({ name: "CustomError", message: "plain object boom", }); @@ -276,29 +385,29 @@ describe("send method", () => { test("returns envelope-like user data as payload", async () => { message.watch("envelopeLikePayload", () => ({ok: false, error: "user payload"})); - await expect(message.send("envelopeLikePayload", undefined)).resolves.toEqual({ + await expect(sendFromCaller("envelopeLikePayload", undefined)).resolves.toEqual({ ok: false, error: "user payload", }); }); test("returns raw invalid failure envelope as payload", async () => { - (chrome.runtime.sendMessage as jest.Mock).mockImplementationOnce((msg, callback) => { - callback?.({ok: false, error: "raw payload"}); + receiver.onMessage.on((_message, _sender, reply) => { + reply({ok: false, error: "raw payload"}); }); - await expect(message.send("rawEnvelopeLikePayload", undefined)).resolves.toEqual({ + await expect(sendFromCaller("rawEnvelopeLikePayload", undefined)).resolves.toEqual({ ok: false, error: "raw payload", }); }); test("returns raw success envelope-like response as payload", async () => { - (chrome.runtime.sendMessage as jest.Mock).mockImplementationOnce((msg, callback) => { - callback?.({ok: true, payload: "raw payload"}); + receiver.onMessage.on((_message, _sender, reply) => { + reply({ok: true, payload: "raw payload"}); }); - await expect(message.send("rawSuccessEnvelopeLikePayload", undefined)).resolves.toEqual({ + await expect(sendFromCaller("rawSuccessEnvelopeLikePayload", undefined)).resolves.toEqual({ ok: true, payload: "raw payload", }); @@ -313,42 +422,42 @@ describe("multiple handlers error for same message type", () => { message.watch("getStringLength", () => 1); message.watch("getStringLength", () => 2); - await expect(message.send("getStringLength", "test")).rejects.toThrow(errorMessage); + await expect(sendFromCaller("getStringLength", "test")).rejects.toThrow(errorMessage); }); test('with two "map" handlers', async () => { message.watch({getStringLength: () => 1}); message.watch({getStringLength: () => 2}); - await expect(message.send("getStringLength", "test")).rejects.toThrow(errorMessage); + await expect(sendFromCaller("getStringLength", "test")).rejects.toThrow(errorMessage); }); test('with two "general" handlers', async () => { message.watch(() => 1); message.watch(() => 2); - await expect(message.send("getStringLength", "test")).rejects.toThrow(errorMessage); + await expect(sendFromCaller("getStringLength", "test")).rejects.toThrow(errorMessage); }); test('with "type" and "map" handlers', async () => { message.watch("getStringLength", () => 1); message.watch({getStringLength: () => 1}); - await expect(message.send("getStringLength", "test")).rejects.toThrow(errorMessage); + await expect(sendFromCaller("getStringLength", "test")).rejects.toThrow(errorMessage); }); test('with "type" and "general" handlers', async () => { message.watch("getStringLength", () => 1); message.watch(() => 2); - await expect(message.send("getStringLength", "test")).rejects.toThrow(errorMessage); + await expect(sendFromCaller("getStringLength", "test")).rejects.toThrow(errorMessage); }); test('with "map" and "general" handlers', async () => { message.watch({getStringLength: () => 1}); message.watch(() => 2); - await expect(message.send("getStringLength", "test")).rejects.toThrow(errorMessage); + await expect(sendFromCaller("getStringLength", "test")).rejects.toThrow(errorMessage); }); test("with two instances watching the same message type", async () => { @@ -356,7 +465,7 @@ describe("multiple handlers error for same message type", () => { message.watch("getStringLength", data => data.length); secondMessage.watch("getStringLength", data => data.length); - await expect(message.send("getStringLength", "test")).rejects.toThrow(errorMessage); + await expect(sendFromCaller("getStringLength", "test")).rejects.toThrow(errorMessage); }); test("allows multiple handlers if one of them don't return value", async () => { @@ -367,6 +476,6 @@ describe("multiple handlers error for same message type", () => { } }); - expect(await message.send("getStringLength", "test")).toBe(4); + expect(await sendFromCaller("getStringLength", "test")).toBe(4); }); }); diff --git a/src/message/providers/Message.ts b/src/message/providers/Message.ts index f4673fc5..83f94f2c 100644 --- a/src/message/providers/Message.ts +++ b/src/message/providers/Message.ts @@ -130,7 +130,8 @@ export default class Message extends AbstractMessag } if (response.ok) { - return "payload" in response; + // JSON serialization omits payload when a successful handler returns undefined. + return true; } return this.isSerializedError(response.error); diff --git a/src/offscreen/OffscreenBridge.test.ts b/src/offscreen/OffscreenBridge.test.ts index 2661d0a4..ee184795 100644 --- a/src/offscreen/OffscreenBridge.test.ts +++ b/src/offscreen/OffscreenBridge.test.ts @@ -1,13 +1,11 @@ -import {isBackground} from "@addon-core/browser"; - -import {Message} from "@message/providers"; +import {getBrowserTest} from "@tests/browser-harness/session"; +import {OffscreenBackground, ProxyOffscreen, RegisterOffscreen} from "./index"; +import type {RpcAsyncProxy} from "@typing/rpc"; import OffscreenBridge from "./OffscreenBridge"; import {OffscreenBridgeReadyMessageType} from "@typing/offscreen"; -const mockedIsBackground = isBackground as jest.MockedFunction; - const parameters = { reasons: ["TESTING" as const], url: "offscreen.html", @@ -27,17 +25,24 @@ const dispatchReady = (iframe: HTMLIFrameElement) => { describe("OffscreenBridge", () => { beforeEach(() => { - jest.clearAllMocks(); - jest.restoreAllMocks(); - - document.body.innerHTML = ""; - mockedIsBackground.mockReturnValue(true); - - (OffscreenBridge as any).instance = undefined; - }); - - afterEach(() => { - jest.useRealTimers(); + const session = getBrowserTest(); + const originalUrl = location.href; + const background = session.harness.contexts.create({kind: "background"}); + + session.harness.runtime.setManifest({ + name: "Offscreen test", + version: "1.0.0", + manifest_version: 2, + background: {scripts: ["background.js"]}, + }); + process.env.MANIFEST_VERSION = "2"; + session.useContext(background, "firefox"); + history.replaceState(null, "", "/_generated_background_page.html"); + document.body.replaceChildren(); + session.addCleanup(() => { + document.body.replaceChildren(); + history.replaceState(null, "", originalUrl); + }); }); test("creates an iframe in background context", async () => { @@ -74,28 +79,60 @@ describe("OffscreenBridge", () => { expect(resolved).toBe(true); }); - test("sends creation request through message outside background context", async () => { - mockedIsBackground.mockReturnValue(false); + test("sends creation request through real messaging outside background context", async () => { + jest.useFakeTimers(); - const send = jest.spyOn(Message.prototype, "send").mockResolvedValue(undefined); - const bridge = new OffscreenBridge(); + const session = getBrowserTest(); - await expect(bridge.create(parameters)).resolves.toBeUndefined(); + new OffscreenBackground().build(); + session.useContext(session.context); + history.replaceState(null, "", "/popup.html"); + + const creation = new OffscreenBridge().create(parameters); + + await jest.advanceTimersByTimeAsync(0); + + const iframe = document.querySelector("iframe"); - expect(send).toHaveBeenCalledWith("offscreen-background", parameters); + expect(iframe?.getAttribute("src")).toBe(parameters.url); + dispatchReady(iframe!); + + await expect(creation).resolves.toBeUndefined(); + expect(session.harness.messaging.forContext(session.context).runtime.sendMessage.calls[0].args).toEqual([ + expect.objectContaining({type: "offscreen-background", data: parameters}), + ]); + }); + + test("propagates a failed creation response from the background context", async () => { + const session = getBrowserTest(); + + new OffscreenBackground().build(); + session.useContext(session.context); + history.replaceState(null, "", "/popup.html"); + + const creation = new OffscreenBridge().create(parameters); + const rejection = expect(creation).rejects.toThrow(`Offscreen iframe failed to load: ${parameters.url}`); + + await wait(); + document.querySelector("iframe")!.dispatchEvent(new Event("error")); + + await rejection; expect(document.querySelector("iframe")).toBeNull(); }); - test("uses a singleton instance for static createOffscreen", async () => { - const create = jest.spyOn(OffscreenBridge.prototype, "create").mockResolvedValue(undefined); + test("uses a singleton instance and shares pending static creation", async () => { + const bridge = OffscreenBridge.getInstance(); + const first = OffscreenBridge.createOffscreen(parameters); + const second = OffscreenBridge.createOffscreen(parameters); + const iframe = document.querySelector("iframe"); - await OffscreenBridge.createOffscreen(parameters); - await OffscreenBridge.createOffscreen(parameters); + expect(OffscreenBridge.getInstance()).toBe(bridge); + expect(document.querySelectorAll("iframe")).toHaveLength(1); + dispatchReady(iframe!); - expect(create).toHaveBeenCalledTimes(2); - expect(create.mock.instances[0]).toBe(create.mock.instances[1]); - expect(create).toHaveBeenNthCalledWith(1, parameters); - expect(create).toHaveBeenNthCalledWith(2, parameters); + await expect(Promise.all([first, second])).resolves.toEqual([undefined, undefined]); + await OffscreenBridge.createOffscreen(parameters); + expect(document.querySelector("iframe")).toBe(iframe); }); test("waits for an in-flight iframe creation when the same URL is requested again", async () => { @@ -176,6 +213,31 @@ describe("OffscreenBridge", () => { await expect(second).resolves.toBeUndefined(); }); + test.each(["source", "origin", "type"] as const)("ignores a ready message with the wrong %s", async field => { + const bridge = new OffscreenBridge(); + const creation = bridge.create(parameters); + const iframe = document.querySelector("iframe")!; + let resolved = false; + + creation.then(() => { + resolved = true; + }); + + window.dispatchEvent( + new MessageEvent("message", { + data: {type: field === "type" ? "unrelated" : OffscreenBridgeReadyMessageType}, + origin: field === "origin" ? "https://other.test" : location.origin, + source: field === "source" ? window : iframe.contentWindow, + }) + ); + + await wait(); + expect(resolved).toBe(false); + dispatchReady(iframe); + + await expect(creation).resolves.toBeUndefined(); + }); + test("removes the iframe when ready message times out", async () => { jest.useFakeTimers(); @@ -201,3 +263,51 @@ describe("OffscreenBridge", () => { await expect(second).resolves.toBeUndefined(); }); }); + +test.each([ + {profile: "firefox" as const, version: 2 as const}, + {profile: "firefox" as const, version: 3 as const}, + {profile: "chrome" as const, version: 2 as const}, +])("ProxyOffscreen waits for iframe readiness on $profile MV$version", async ({profile, version}) => { + const session = getBrowserTest(); + const originalUrl = location.href; + const background = session.harness.contexts.create({kind: "background"}); + const frameContext = session.harness.contexts.create({kind: "extensionPage"}); + const service = {sum: (a: number, b: number) => a + b}; + + const manifest = { + name: "Offscreen test", + version: "1.0.0", + background: {scripts: ["background.js"]}, + }; + + // Firefox MV3 uses background.scripts; the kit accepts manifests through Chrome's narrower type. + session.harness.runtime.setManifest({...manifest, manifest_version: version} as chrome.runtime.Manifest); + process.env.MANIFEST_VERSION = String(version); + session.useContext(background, profile); + history.replaceState(null, "", "/_generated_background_page.html"); + session.addCleanup(() => { + document.body.replaceChildren(); + history.replaceState(null, "", originalUrl); + }); + + const restore = session.useContext(frameContext); + const registration = new RegisterOffscreen("math", () => service); + + registration.register(); + session.addCleanup(() => registration.destroy()); + restore(); + + const proxy = new ProxyOffscreen<"math", RpcAsyncProxy>("math", parameters).get(); + const result = proxy.sum(1, 2); + const iframe = document.querySelector("iframe"); + const caller = session.harness.messaging.forContext(background); + + expect(iframe).not.toBeNull(); + expect(caller.runtime.sendMessage.calls).toHaveLength(0); + dispatchReady(iframe!); + + await expect(result).resolves.toBe(3); + expect(caller.runtime.sendMessage.calls).toHaveLength(1); + expect(session.harness.offscreen.createDocument.calls).toHaveLength(0); +}); diff --git a/src/offscreen/providers/Offscreen.test.ts b/src/offscreen/providers/Offscreen.test.ts deleted file mode 100644 index 8e5dcfd2..00000000 --- a/src/offscreen/providers/Offscreen.test.ts +++ /dev/null @@ -1,392 +0,0 @@ -import {closeOffscreen, createOffscreen, hasOffscreen, hasOffscreenPath} from "@addon-core/browser"; -import {isOffscreen} from "@offscreen/utils"; - -import {isBrowser} from "@main/env"; - -import MessageManager from "@message/MessageManager"; - -import ProxyOffscreen from "./ProxyOffscreen"; -import RegisterOffscreen from "./RegisterOffscreen"; -import OffscreenManager from "../OffscreenManager"; - -import {RpcAsyncProxy} from "@typing/rpc"; -import {MessageTypeSeparator} from "@typing/message"; - -jest.mock("@offscreen/utils", () => ({isOffscreen: jest.fn()})); -jest.mock("@main/env", () => ({isBrowser: jest.fn()})); - -const mockedCloseOffscreen = closeOffscreen as jest.MockedFunction; -const mockedCreateOffscreen = createOffscreen as jest.MockedFunction; -const mockedHasOffscreen = hasOffscreen as jest.MockedFunction; -const mockedHasOffscreenPath = hasOffscreenPath as jest.MockedFunction; -const mockedIsBrowser = isBrowser as unknown as jest.Mock; -const mockedIsOffscreen = isOffscreen as jest.MockedFunction; - -type MockLockMode = "exclusive" | "shared"; -type MockLockOptions = {mode: MockLockMode}; -type MockLockCallback = () => unknown | Promise; - -type LockTask = { - mode: MockLockMode; - callback: MockLockCallback; - resolve: (value: unknown) => void; - reject: (error: unknown) => void; -}; - -class MockLockManager { - private readonly queues = new Map(); - - private readonly states = new Map(); - - public request(name: string, options: MockLockOptions, callback: MockLockCallback): Promise { - return new Promise((resolve, reject) => { - const queue = this.queues.get(name) ?? []; - queue.push({mode: options.mode, callback, resolve, reject}); - this.queues.set(name, queue); - this.drain(name); - }); - } - - private state(name: string): {shared: number; exclusive: boolean} { - const state = this.states.get(name) ?? {shared: 0, exclusive: false}; - - this.states.set(name, state); - - return state; - } - - private drain(name: string): void { - const queue = this.queues.get(name); - const state = this.state(name); - - if (!queue?.length || state.exclusive) { - return; - } - - const task = queue[0]; - - if (task.mode === "exclusive") { - if (state.shared > 0) { - return; - } - - queue.shift(); - state.exclusive = true; - this.run(name, task); - - return; - } - - while (queue[0]?.mode === "shared" && !state.exclusive) { - const sharedTask = queue.shift()!; - - state.shared++; - this.run(name, sharedTask); - } - } - - private run(name: string, task: LockTask): void { - Promise.resolve() - .then(task.callback) - .then(task.resolve, task.reject) - .finally(() => { - const state = this.state(name); - - if (task.mode === "exclusive") { - state.exclusive = false; - } else { - state.shared--; - } - - this.drain(name); - }); - } -} - -let existingOffscreenPath: string | undefined; -let blockedResolvers: Array<() => void> = []; -let blockedStarted = 0; - -const wait = (timeout = 0) => new Promise(resolve => setTimeout(resolve, timeout)); -const waitForBlockedCalls = async (count: number): Promise => { - for (let attempt = 0; attempt < 10; attempt++) { - if (blockedStarted >= count) { - return; - } - - await wait(); - } - - throw new Error(`Expected ${count} blocked offscreen calls to start, got ${blockedStarted}`); -}; - -beforeEach(async () => { - jest.clearAllMocks(); - - existingOffscreenPath = parameters.url; - blockedResolvers = []; - blockedStarted = 0; - - Object.defineProperty(navigator, "locks", { - configurable: true, - value: new MockLockManager(), - }); - - mockedHasOffscreen.mockImplementation(async () => existingOffscreenPath !== undefined); - mockedHasOffscreenPath.mockImplementation(async (path: string) => existingOffscreenPath === path); - mockedCloseOffscreen.mockImplementation(async () => { - existingOffscreenPath = undefined; - }); - mockedCreateOffscreen.mockImplementation(async (offscreenParameters: chrome.offscreen.CreateParameters) => { - existingOffscreenPath = offscreenParameters.url; - }); - - OffscreenManager.getInstance().clear(); - MessageManager.getInstance().clear(); - - new RegisterOffscreen(offscreenName, () => MatchService).register(); -}); - -const MatchService = { - sum: (a: number, b: number): number => a + b, - asyncSum: (a: number, b: number): Promise => { - return new Promise(resolve => setTimeout(() => resolve(a + b), 100)); - }, - blockedSum: (a: number, b: number): Promise => { - blockedStarted++; - - return new Promise(resolve => { - blockedResolvers.push(() => resolve(a + b)); - }); - }, - one: 1, - obj: { - concat: (a: string, b: string): string => a + " " + b, - zero: 0, - }, -}; - -type OffscreenType = typeof MatchService; -type OffscreenProxyType = RpcAsyncProxy; - -const offscreenName = "math"; -const parameters = { - reasons: ["TESTING" as const], - url: "offscreen.html", - justification: "for testing", -}; - -describe("ProxyOffscreen", () => { - beforeEach(async () => { - mockedIsOffscreen.mockReturnValue(false); - mockedIsBrowser.mockReturnValue(false); - }); - - test("throws an error when get() is called in offscreen context", async () => { - mockedIsOffscreen.mockReturnValue(true); - - const proxy = new ProxyOffscreen(offscreenName, parameters); - - expect(() => proxy.get()).toThrow( - `You are trying to get proxy offscreen service "${offscreenName}" from offscreen. You can get original offscreen service instead` - ); - }); - - test("returns a proxy when not in offscreen context", () => { - const offscreen = new ProxyOffscreen(offscreenName, parameters).get(); - - //@ts-ignore - expect(offscreen.__proxy).toBe(true); - }); - - test("invokes remote methods using Message.send", async () => { - const offscreen = new ProxyOffscreen(offscreenName, parameters).get(); - - expect(await offscreen.sum(1, 2)).toBe(3); - expect(chrome.runtime.sendMessage).toHaveBeenCalledWith( - expect.objectContaining({ - type: `offscreen${MessageTypeSeparator}${offscreenName}`, - data: { - path: "sum", - args: [1, 2], - }, - }), - expect.any(Function) - ); - }); - - test("accesses property on offscreen service object ", async () => { - const offscreen = new ProxyOffscreen(offscreenName, parameters).get(); - - expect(await offscreen.one()).toBe(1); - expect(chrome.runtime.sendMessage).toHaveBeenCalledWith( - expect.objectContaining({ - type: `offscreen${MessageTypeSeparator}${offscreenName}`, - data: { - path: "one", - args: [], - }, - }), - expect.any(Function) - ); - }); - - test("accesses nested method or property ", async () => { - const offscreen = new ProxyOffscreen(offscreenName, parameters).get(); - - expect(await offscreen.obj.concat("Hello", "world")).toBe("Hello world"); - expect(chrome.runtime.sendMessage).toHaveBeenCalledWith( - expect.objectContaining({ - type: `offscreen${MessageTypeSeparator}${offscreenName}`, - data: { - path: "obj.concat", - args: ["Hello", "world"], - }, - }), - expect.any(Function) - ); - - expect(await offscreen.obj.zero()).toBe(0); - expect(chrome.runtime.sendMessage).toHaveBeenCalledWith( - expect.objectContaining({ - type: `offscreen${MessageTypeSeparator}${offscreenName}`, - data: { - path: "obj.zero", - args: [], - }, - }), - expect.any(Function) - ); - }); - - test("handles proxied async methods that return promises", async () => { - const offscreen = new ProxyOffscreen(offscreenName, parameters).get(); - - expect(await offscreen.asyncSum(1, 2)).toBe(3); - }); - - test("does not recreate offscreen when URL hasn't changed", async () => { - jest.clearAllMocks(); - existingOffscreenPath = undefined; - - const proxyInstance = new ProxyOffscreen(offscreenName, parameters); - const offscreen = proxyInstance.get(); - - await offscreen.sum(1, 2); - - await offscreen.sum(3, 4); - - expect(createOffscreen).toHaveBeenCalledTimes(1); - expect(closeOffscreen).toHaveBeenCalledTimes(0); - }); - - test("recreates offscreen when URL changes", async () => { - jest.clearAllMocks(); - existingOffscreenPath = undefined; - - const proxyInstance1 = new ProxyOffscreen(offscreenName, parameters); - const offscreen1 = proxyInstance1.get(); - await offscreen1.sum(1, 2); - - const differentParams = {...parameters, url: "different-offscreen.html"}; - const proxyInstance2 = new ProxyOffscreen( - offscreenName, - differentParams - ); - const offscreen2 = proxyInstance2.get(); - await offscreen2.sum(3, 4); - - expect(createOffscreen).toHaveBeenCalledTimes(2); - expect(closeOffscreen).toHaveBeenCalledTimes(1); - }); - - test("runs same URL calls in parallel without recreating offscreen", async () => { - const proxyInstance = new ProxyOffscreen(offscreenName, parameters); - const offscreen = proxyInstance.get(); - - const first = offscreen.blockedSum(1, 2); - const second = offscreen.blockedSum(3, 4); - - await waitForBlockedCalls(2); - - blockedResolvers.forEach(resolve => resolve()); - - await expect(Promise.all([first, second])).resolves.toEqual([3, 7]); - - expect(createOffscreen).toHaveBeenCalledTimes(0); - expect(closeOffscreen).toHaveBeenCalledTimes(0); - }); - - test("waits for active calls before switching to a different URL", async () => { - const proxyInstance1 = new ProxyOffscreen(offscreenName, parameters); - const offscreen1 = proxyInstance1.get(); - const first = offscreen1.blockedSum(1, 2); - - await waitForBlockedCalls(1); - - const differentParams = {...parameters, url: "different-offscreen.html"}; - const proxyInstance2 = new ProxyOffscreen( - offscreenName, - differentParams - ); - const offscreen2 = proxyInstance2.get(); - const second = offscreen2.sum(3, 4); - - await wait(); - - expect(createOffscreen).toHaveBeenCalledTimes(0); - expect(closeOffscreen).toHaveBeenCalledTimes(0); - - blockedResolvers[0](); - - await expect(first).resolves.toBe(3); - await expect(second).resolves.toBe(7); - - expect(closeOffscreen).toHaveBeenCalledTimes(1); - expect(createOffscreen).toHaveBeenCalledWith(differentParams); - }); -}); - -describe("RegisterOffscreen", () => { - beforeEach(async () => { - mockedIsOffscreen.mockReturnValue(true); - }); - - test("throws an error when get() is called outside offscreen context", async () => { - mockedIsOffscreen.mockReturnValue(false); - - const proxy = new RegisterOffscreen(offscreenName, () => MatchService); - - expect(() => proxy.get()).toThrow( - `Offscreen service "${offscreenName}" can be getting only from offscreen context.` - ); - }); - - test("returns real offscreen service when called in offscreen context", () => { - const offscreen = new RegisterOffscreen( - offscreenName, - () => MatchService - ).get(); - - //@ts-ignore - expect(offscreen.__proxy).toBe(undefined); - }); - - test("invokes methods directly without using Message.send in offscreen", async () => { - const offscreen = new RegisterOffscreen( - offscreenName, - () => MatchService - ).get(); - - expect(offscreen.sum(1, 2)).toBe(3); - expect(chrome.runtime.sendMessage).toHaveBeenCalledTimes(0); - }); - - test("throws an error when attempting to register the same offscreen service twice", async () => { - const offscreen = new RegisterOffscreen(offscreenName, () => MatchService); - - expect(() => offscreen.register()).toThrow( - `A instance with name "${offscreenName}" already exists. The name must be unique.` - ); - }); -}); diff --git a/src/offscreen/providers/ProxyOffscreen.test.ts b/src/offscreen/providers/ProxyOffscreen.test.ts new file mode 100644 index 00000000..0e175864 --- /dev/null +++ b/src/offscreen/providers/ProxyOffscreen.test.ts @@ -0,0 +1,354 @@ +import {closeOffscreen} from "@addon-core/browser"; +import {getBrowserTest} from "@tests/browser-harness/session"; +import MockLockManager from "@tests/offscreen/MockLockManager"; +import {ProxyOffscreen, RegisterOffscreen} from "./index"; +import {RpcAsyncProxy} from "@typing/rpc"; +import {MessageTypeSeparator} from "@typing/message"; +import {OffscreenGlobalAccess} from "@typing/offscreen"; + +let blockedResolvers: Array<() => void> = []; +let blockedStarted = 0; +let locks: MockLockManager; + +const wait = () => new Promise(resolve => setTimeout(resolve)); +const waitUntil = async (condition: () => boolean, description: string): Promise => { + const deadline = Date.now() + 2000; + + while (Date.now() < deadline) { + if (condition()) { + return; + } + + await wait(); + } + + throw new Error(`Timed out waiting for ${description}`); +}; + +const waitForBlockedCalls = (count: number) => + waitUntil(() => blockedStarted >= count, `${count} blocked offscreen calls`); + +const lifecycle = () => getBrowserTest().harness.offscreen; +const calls = () => getBrowserTest().harness.messaging.forContext(getBrowserTest().context).runtime.sendMessage.calls; + +beforeEach(() => { + const session = getBrowserTest(); + const descriptor = Object.getOwnPropertyDescriptor(navigator, "locks"); + + blockedResolvers = []; + blockedStarted = 0; + locks = new MockLockManager(); + Object.defineProperty(navigator, "locks", {configurable: true, value: locks}); + session.addCleanup(() => { + if (descriptor) { + Object.defineProperty(navigator, "locks", descriptor); + } else { + Reflect.deleteProperty(navigator, "locks"); + } + }); + + // The kit owns document lifetime; this boundary hook starts the real application transport. + // Replace the passthrough spy with a post-creation hook when the kit provides one (tests/README.md). + const createContext = session.harness.contexts.create.bind(session.harness.contexts); + + jest.spyOn(session.harness.contexts, "create").mockImplementation(options => { + const context = createContext(options); + + if (options.kind === "offscreen") { + const restore = session.useContext(context); + const registration = new RegisterOffscreen(offscreenName, () => mathService); + + try { + registration.register(); + } finally { + restore(); + } + + const detach = context.onDispose(() => registration.destroy()); + + session.addCleanup(() => { + detach(); + registration.destroy(); + }); + } + + return context; + }); + + session.harness.contexts.create({kind: "offscreen", url: chrome.runtime.getURL(parameters.url)}); +}); + +const mathService = { + sum: (a: number, b: number): number => a + b, + asyncSum: (a: number, b: number): Promise => { + return new Promise(resolve => setTimeout(() => resolve(a + b), 100)); + }, + blockedSum: (a: number, b: number): Promise => { + blockedStarted++; + + return new Promise(resolve => { + blockedResolvers.push(() => resolve(a + b)); + }); + }, + one: 1, + obj: { + concat: (a: string, b: string): string => a + " " + b, + zero: 0, + }, +}; + +type OffscreenType = typeof mathService; +type OffscreenProxyType = RpcAsyncProxy; + +const offscreenName = "math"; +const parameters = { + reasons: ["TESTING" as const], + url: "offscreen.html", + justification: "for testing", +}; + +describe("ProxyOffscreen", () => { + test("throws an error when get() is called in offscreen context", async () => { + globalThis[OffscreenGlobalAccess] = true; + + const proxy = new ProxyOffscreen(offscreenName, parameters); + + expect(() => proxy.get()).toThrow( + `You are trying to get proxy offscreen service "${offscreenName}" from offscreen. You can get original offscreen service instead` + ); + }); + + test("returns a proxy when not in offscreen context", () => { + const offscreen = new ProxyOffscreen(offscreenName, parameters).get(); + + expect(Reflect.get(offscreen, "__proxy")).toBe(true); + }); + + test("invokes remote methods using Message.send", async () => { + const offscreen = new ProxyOffscreen(offscreenName, parameters).get(); + + expect(await offscreen.sum(1, 2)).toBe(3); + expect(calls().at(-1)?.args).toEqual([ + expect.objectContaining({ + type: `offscreen${MessageTypeSeparator}${offscreenName}`, + data: { + path: "sum", + args: [1, 2], + }, + }), + ]); + }); + + test("accesses property on offscreen service object ", async () => { + const offscreen = new ProxyOffscreen(offscreenName, parameters).get(); + + expect(await offscreen.one()).toBe(1); + expect(calls().at(-1)?.args).toEqual([ + expect.objectContaining({ + type: `offscreen${MessageTypeSeparator}${offscreenName}`, + data: { + path: "one", + args: [], + }, + }), + ]); + }); + + test("accesses nested method or property ", async () => { + const offscreen = new ProxyOffscreen(offscreenName, parameters).get(); + + expect(await offscreen.obj.concat("Hello", "world")).toBe("Hello world"); + expect(calls().at(-1)?.args).toEqual([ + expect.objectContaining({ + type: `offscreen${MessageTypeSeparator}${offscreenName}`, + data: { + path: "obj.concat", + args: ["Hello", "world"], + }, + }), + ]); + + expect(await offscreen.obj.zero()).toBe(0); + expect(calls().at(-1)?.args).toEqual([ + expect.objectContaining({ + type: `offscreen${MessageTypeSeparator}${offscreenName}`, + data: { + path: "obj.zero", + args: [], + }, + }), + ]); + }); + + test("handles proxied async methods that return promises", async () => { + const offscreen = new ProxyOffscreen(offscreenName, parameters).get(); + + jest.useFakeTimers(); + + const result = offscreen.asyncSum(1, 2); + + await jest.advanceTimersByTimeAsync(100); + await expect(result).resolves.toBe(3); + }); + + test("does not recreate offscreen when URL hasn't changed", async () => { + getBrowserTest().harness.contexts.remove(lifecycle().context!.info.contextId); + + const proxyInstance = new ProxyOffscreen(offscreenName, parameters); + const offscreen = proxyInstance.get(); + + await offscreen.sum(1, 2); + + await offscreen.sum(3, 4); + + expect(lifecycle().createDocument.calls).toHaveLength(1); + expect(lifecycle().closeDocument.calls).toHaveLength(0); + }); + + test("recreates offscreen when URL changes", async () => { + getBrowserTest().harness.contexts.remove(lifecycle().context!.info.contextId); + + const proxyInstance1 = new ProxyOffscreen(offscreenName, parameters); + const offscreen1 = proxyInstance1.get(); + await offscreen1.sum(1, 2); + + const differentParams = {...parameters, url: "different-offscreen.html"}; + const proxyInstance2 = new ProxyOffscreen( + offscreenName, + differentParams + ); + const offscreen2 = proxyInstance2.get(); + await offscreen2.sum(3, 4); + + expect(lifecycle().createDocument.calls).toHaveLength(2); + expect(lifecycle().closeDocument.calls).toHaveLength(1); + }); + + test("runs same URL calls in parallel without recreating offscreen", async () => { + const proxyInstance = new ProxyOffscreen(offscreenName, parameters); + const offscreen = proxyInstance.get(); + + const first = offscreen.blockedSum(1, 2); + const second = offscreen.blockedSum(3, 4); + + await waitForBlockedCalls(2); + + blockedResolvers.forEach(resolve => resolve()); + + await expect(Promise.all([first, second])).resolves.toEqual([3, 7]); + + expect(lifecycle().createDocument.calls).toHaveLength(0); + expect(lifecycle().closeDocument.calls).toHaveLength(0); + }); + + test("waits for active calls before switching to a different URL", async () => { + const proxyInstance1 = new ProxyOffscreen(offscreenName, parameters); + const offscreen1 = proxyInstance1.get(); + const first = offscreen1.blockedSum(1, 2); + + await waitForBlockedCalls(1); + + const differentParams = {...parameters, url: "different-offscreen.html"}; + const proxyInstance2 = new ProxyOffscreen( + offscreenName, + differentParams + ); + const offscreen2 = proxyInstance2.get(); + const second = offscreen2.sum(3, 4); + + await waitUntil(() => locks.pending("adnbn:offscreen:active") === 1, "the exclusive URL switch lock"); + + expect(lifecycle().createDocument.calls).toHaveLength(0); + expect(lifecycle().closeDocument.calls).toHaveLength(0); + + blockedResolvers[0](); + + await expect(first).resolves.toBe(3); + await expect(second).resolves.toBe(7); + + expect(lifecycle().closeDocument.calls).toHaveLength(1); + expect(lifecycle().createDocument.calls.at(-1)?.args).toEqual([differentParams]); + }); +}); + +test("recreates a document closed between calls", async () => { + const proxy = new ProxyOffscreen(offscreenName, parameters).get(); + const previous = lifecycle().context!; + + await expect(proxy.sum(1, 2)).resolves.toBe(3); + await closeOffscreen(); + expect(previous.disposed).toBe(true); + await expect(proxy.sum(3, 4)).resolves.toBe(7); + expect(lifecycle().context).not.toBe(previous); + expect(lifecycle().createDocument.calls).toHaveLength(1); +}); + +test("releases lifecycle locks after creation failure so a later call can retry", async () => { + getBrowserTest().harness.contexts.remove(lifecycle().context!.info.contextId); + lifecycle().beforeCreate.failNext(new Error("Creation failed")); + + const proxy = new ProxyOffscreen(offscreenName, parameters).get(); + + await expect(proxy.sum(1, 2)).rejects.toThrow("Creation failed"); + expect(lifecycle().context).toBeUndefined(); + await expect(proxy.sum(3, 4)).resolves.toBe(7); + expect(lifecycle().createDocument.calls).toHaveLength(2); +}); + +test("keeps the existing document and releases locks after closure failure", async () => { + const previous = lifecycle().context!; + const otherParameters = {...parameters, url: "other-offscreen.html"}; + const proxy = new ProxyOffscreen(offscreenName, otherParameters).get(); + + lifecycle().beforeClose.failNext(new Error("Closure failed")); + + await expect(proxy.sum(1, 2)).rejects.toThrow("Closure failed"); + expect(lifecycle().context).toBe(previous); + expect(previous.disposed).toBe(false); + expect(lifecycle().createDocument.calls).toHaveLength(0); + await expect(proxy.sum(3, 4)).resolves.toBe(7); + expect(previous.disposed).toBe(true); + expect(lifecycle().createDocument.calls).toHaveLength(1); +}); + +test("releases the active lock after messaging failure before switching URL", async () => { + const session = getBrowserTest(); + const sender = session.harness.messaging.forContext(session.context); + const proxy = new ProxyOffscreen(offscreenName, parameters).get(); + + sender.runtime.sendMessage.failNext(new Error("Channel closed")); + + await expect(proxy.sum(1, 2)).rejects.toThrow("Channel closed"); + + const other = new ProxyOffscreen(offscreenName, { + ...parameters, + url: "other-offscreen.html", + }).get(); + + await expect(other.sum(3, 4)).resolves.toBe(7); + expect(lifecycle().closeDocument.calls).toHaveLength(1); +}); + +test("serializes concurrent creation and sends only after the document is ready", async () => { + getBrowserTest().harness.contexts.remove(lifecycle().context!.info.contextId); + + let release!: () => void; + const pending = new Promise(resolve => { + release = resolve; + }); + + lifecycle().beforeCreate.setImplementation(() => pending); + + const proxy = new ProxyOffscreen(offscreenName, parameters).get(); + const first = proxy.sum(1, 2); + const second = proxy.sum(3, 4); + + await waitUntil(() => lifecycle().beforeCreate.calls.length === 1, "offscreen creation to start"); + expect(lifecycle().context).toBeUndefined(); + expect(calls()).toHaveLength(0); + expect(lifecycle().createDocument.calls).toHaveLength(1); + release(); + + await expect(Promise.all([first, second])).resolves.toEqual([3, 7]); + expect(lifecycle().createDocument.calls).toHaveLength(1); +}); diff --git a/src/offscreen/providers/RegisterOffscreen.test.ts b/src/offscreen/providers/RegisterOffscreen.test.ts new file mode 100644 index 00000000..e97ae25e --- /dev/null +++ b/src/offscreen/providers/RegisterOffscreen.test.ts @@ -0,0 +1,58 @@ +import {getBrowserTest} from "@tests/browser-harness/session"; +import {RegisterOffscreen} from "./index"; +import {OffscreenGlobalAccess} from "@typing/offscreen"; + +const offscreenName = "math"; +const mathService = {sum: (a: number, b: number): number => a + b}; +type OffscreenType = typeof mathService; + +beforeEach(() => { + const session = getBrowserTest(); + const context = session.harness.contexts.create({kind: "offscreen"}); + + session.useContext(context); + new RegisterOffscreen(offscreenName, () => mathService).register(); +}); + +describe("RegisterOffscreen", () => { + beforeEach(async () => { + globalThis[OffscreenGlobalAccess] = true; + }); + + test("throws an error when get() is called outside offscreen context", async () => { + globalThis[OffscreenGlobalAccess] = false; + + const proxy = new RegisterOffscreen(offscreenName, () => mathService); + + expect(() => proxy.get()).toThrow( + `Offscreen service "${offscreenName}" can be getting only from offscreen context.` + ); + }); + + test("returns real offscreen service when called in offscreen context", () => { + const offscreen = new RegisterOffscreen( + offscreenName, + () => mathService + ).get(); + + expect(offscreen).toBe(mathService); + }); + + test("invokes methods directly without using Message.send in offscreen", async () => { + const offscreen = new RegisterOffscreen( + offscreenName, + () => mathService + ).get(); + + expect(offscreen.sum(1, 2)).toBe(3); + expect(getBrowserTest().harness.messaging.calls).toHaveLength(0); + }); + + test("throws an error when attempting to register the same offscreen service twice", async () => { + const offscreen = new RegisterOffscreen(offscreenName, () => mathService); + + expect(() => offscreen.register()).toThrow( + `A instance with name "${offscreenName}" already exists. The name must be unique.` + ); + }); +}); diff --git a/src/relay/README.md b/src/relay/README.md index bc155ee3..d291a77e 100644 --- a/src/relay/README.md +++ b/src/relay/README.md @@ -78,7 +78,7 @@ The local accessor is not a way to address another frame. The remote accessor re ### Export ownership -- [`adnbn`](../main/relay.ts) exports `defineRelay`, the remote `getRelay`, and the public definition, call-target, proxy, result, and error contracts. Import `RelayAllFrames`, `RelayMethod`, `RelayFrameErrorKind`, and `RelayDiscoveryError` here. +- [`adnbn`](../main/relay.ts) exports `defineRelay`, the remote `getRelay`, and the public definition, call-target, proxy, result, and error contracts. Import `RelayAllFrames`, `RelayMethod`, `RelayFrameErrorKind`, `RelayDiscoveryError`, and `RelayProtocolError` here. - [`adnbn/relay`](./index.ts) exports only the local `getRelay` and `RelayRegistry`, `RelayName`, and `RelayTarget` types. The generated `.adnbn/relay.d.ts` augments this registry; both accessors derive their types from it. It does not redeclare accessor overloads. - [`adnbn/entry/relay`](../entry/relay/index.ts) is the internal bootstrap interface: `Builder`, `resolveDefinition`, the default `relay(definition, contentBuilder)` startup function, and `RelayUnresolvedDefinition`. The unresolved type represents merged runtime input, not the public `defineRelay` contract. @@ -296,21 +296,47 @@ Permission prompts, frame discovery, startup retries, and work inside the remote ### Verification -Run from the framework repository root. Build first so targeted tests can resolve the package's public exports: +Run from the framework repository root. The filtered `npm test` command builds the package and runs Relay unit, build, type and browser tests; it requires local Chrome and Firefox: ```bash -npm run build npm run typecheck -npm run test:relay -- --runInBand -npm run test:message -- --runInBand +npm test -- --testPathPatterns="Relay|src/entry/relay|browser/relay" --runInBand +npm run test:unit -- --testPathPatterns=src/message --runInBand +``` + +For Relay runtime unit tests without a build or browsers: + +```bash +npm run test:unit -- --testPathPatterns="src/relay/|src/entry/relay/" --runInBand ``` `typecheck` checks both source and test files; passing Jest alone does not prove test files are type-correct. For the full non-browser regression suite, including shared transport and content aggregation tests: ```bash -npm test -- --runInBand --testPathIgnorePatterns=tests/integration/browser +npm run check ``` -Important coverage lives in [`Relay.test.ts`](./providers/Relay.test.ts), [`RelayDiscovery.test.ts`](./discovery/RelayDiscovery.test.ts), [`RelayPermission.test.ts`](./RelayPermission.test.ts), [`RelayParser.test.ts`](../cli/entrypoint/parser/RelayParser.test.ts), [`RelayDriver.test.ts`](../cli/plugins/content/RelayDriver.test.ts), [`RelayDeclaration.test.ts`](../cli/plugins/content/RelayDeclaration.test.ts), and [`ContentManager.test.ts`](../cli/plugins/content/ContentManager.test.ts). +Important coverage lives in [`ProxyRelay.test.ts`](./providers/ProxyRelay.test.ts), [`RegisterRelay.test.ts`](./providers/RegisterRelay.test.ts), [`RelayScriptingAdapter.test.ts`](./adapters/RelayScriptingAdapter.test.ts), [`RelayDiscovery.test.ts`](./discovery/RelayDiscovery.test.ts), [`RelayPermission.test.ts`](./RelayPermission.test.ts), [`RelayParser.test.ts`](../cli/entrypoint/parser/RelayParser.test.ts), [`RelayDriver.test.ts`](../cli/plugins/content/RelayDriver.test.ts), [`RelayDeclaration.test.ts`](../cli/plugins/content/RelayDeclaration.test.ts), and [`ContentManager.test.ts`](../cli/plugins/content/ContentManager.test.ts). Keep regression cases for a rejected top frame plus successful iframe in Scripting `Any`, no manager retries in `Any`, partial explicit-batch failures, remote Messaging errors, document capability rejection, and permission precedence. Native frame enumeration, injection rejection semantics, and user activation require verification in a real extension context; browser mocks cannot establish those guarantees. + +### Scripting response protocol + +The injected `invoke-relay.ts` function envelopes successful values (including an explicit no-result marker), thrown +errors, rejected promises and both missing-manager failures. `Any` fails immediately when no manager is present; +addressed calls and `All` perform ten lookups at 0, 300, …, 2700 ms. A manager registered before the final lookup can +still fulfill the call. A host `timeoutMs` is independent of this retry schedule. MV2 checks the configured host permissions for the +`tabs.executeScript` path; MV3 also checks `scripting`. + +The host adapter validates every envelope before reading its fields. Null, primitives and malformed success/error +objects become `RelayProtocolError` outcomes with the existing `execution` kind. Valid remote errors retain the +`remote` kind. A malformed frame cannot discard another frame's success in a batch or in `Any`. +Scalar calls throw the public `RelayProtocolError` class exported by `adnbn`, so callers can use `instanceof`. +Remote errors with the same name retain their remote origin and do not become instances of this class. + +Relay unit tests use the shared Browser harness. A physical test fixture bundles the real RegisterRelay/RelayManager +into isolated guest realms; virtual guest time drives registration retries and asynchronous methods. This bundle is +independent of `dist`. Its esbuild configuration lives in `tests/runtime.ts`; adding non-JavaScript imports to the +guest module graph requires matching loaders there. Only the injected function module is excluded from Babel coverage, because serialized functions +cannot reference host Istanbul counters. The host adapter remains covered. Real Chrome MV3 and Firefox MV2/MV3 +regressions in `tests/integration/browser/relay` verify values and missing-manager errors across the browser boundary. diff --git a/src/relay/RelayPermission.test.ts b/src/relay/RelayPermission.test.ts index c77e5d87..8ff7d25b 100644 --- a/src/relay/RelayPermission.test.ts +++ b/src/relay/RelayPermission.test.ts @@ -1,28 +1,13 @@ -import {containsPermissions, onPermissionsAdded, onPermissionsRemoved} from "@addon-core/browser"; +import {getBrowserTest} from "@tests/browser-harness/session"; -import RelayPermission, {RelayPermissionGlobalKey} from "./RelayPermission"; +import RelayPermission from "./RelayPermission"; import {ContentScriptDeclarative} from "@typing/content"; import {RelayMethod, type RelayOptionsMap} from "@typing/relay"; -const mockedContainsPermissions = containsPermissions as jest.MockedFunction; -const mockedOnPermissionsAdded = onPermissionsAdded as jest.MockedFunction; -const mockedOnPermissionsRemoved = onPermissionsRemoved as jest.MockedFunction; - describe("RelayPermission", () => { - beforeEach(() => { - delete globalThis[RelayPermissionGlobalKey]; - mockedContainsPermissions.mockReset(); - mockedOnPermissionsAdded.mockClear(); - mockedOnPermissionsRemoved.mockClear(); - }); - - afterEach(() => { - delete globalThis[RelayPermissionGlobalKey]; - }); - test("checks every Relay stored in its Map", async () => { - mockedContainsPermissions.mockResolvedValue(true); + getBrowserTest().harness.permissions.contains.setResult(true); const relays: RelayOptionsMap = new Map([ [ "scanner", @@ -39,13 +24,15 @@ describe("RelayPermission", () => { await Promise.resolve(); await Promise.resolve(); - expect(mockedContainsPermissions).toHaveBeenCalledWith({ - origins: ["https://example.com/*"], - permissions: ["scripting"], - }); + expect(getBrowserTest().harness.permissions.contains.calls[0].args).toEqual([ + { + origins: ["https://example.com/*"], + permissions: ["scripting"], + }, + ]); expect(permission.allow("scanner")).toBe(true); - expect(mockedOnPermissionsAdded).toHaveBeenCalledTimes(1); - expect(mockedOnPermissionsRemoved).toHaveBeenCalledTimes(1); + expect(getBrowserTest().harness.permissions.onAdded.listenerCount()).toBe(1); + expect(getBrowserTest().harness.permissions.onRemoved.listenerCount()).toBe(1); }); test("returns one started instance for the current runtime context", () => { @@ -68,7 +55,58 @@ describe("RelayPermission", () => { expect(second).toBe(first); expect(first.allow("messaging")).toBe(true); - expect(mockedOnPermissionsAdded).toHaveBeenCalledTimes(1); - expect(mockedOnPermissionsRemoved).toHaveBeenCalledTimes(1); + expect(getBrowserTest().harness.permissions.onAdded.listenerCount()).toBe(1); + expect(getBrowserTest().harness.permissions.onRemoved.listenerCount()).toBe(1); }); }); + +test("refreshes optional Relay permissions after grants and revocations", async () => { + const {harness} = getBrowserTest(); + const required: chrome.permissions.Permissions = {permissions: ["scripting"], origins: ["https://example.com/*"]}; + const permission = RelayPermission.getInstance( + new Map([ + [ + "scanner", + { + name: "scanner", + method: RelayMethod.Scripting, + declarative: ContentScriptDeclarative.Optional, + matches: required.origins, + }, + ], + ]) + ); + + await permission.contains("scanner"); + expect(permission.allow("scanner")).toBe(false); + await harness.permissions.grant(required); + expect(permission.allow("scanner")).toBe(true); + await harness.permissions.revoke(required); + expect(permission.allow("scanner")).toBe(false); + expect(harness.permissions.contains.calls).toHaveLength(4); +}); + +test("does not request the MV3 scripting permission for an MV2 Relay", async () => { + const {harness} = getBrowserTest(); + + harness.runtime.setManifest({manifest_version: 2, name: "MV2 Relay", version: "1.0.0"}); + harness.permissions.set({origins: ["https://example.com/*"]}); + + const permission = RelayPermission.getInstance( + new Map([ + [ + "scanner", + { + name: "scanner", + method: RelayMethod.Scripting, + declarative: ContentScriptDeclarative.Optional, + matches: ["https://example.com/*"], + }, + ], + ]) + ); + + await expect(permission.contains("scanner")).resolves.toBe(true); + expect(harness.permissions.contains.calls[0].args).toEqual([{origins: ["https://example.com/*"], permissions: []}]); + expect(permission.allow("scanner")).toBe(true); +}); diff --git a/src/relay/RelayPermission.ts b/src/relay/RelayPermission.ts index d8e4663e..52adf5f7 100644 --- a/src/relay/RelayPermission.ts +++ b/src/relay/RelayPermission.ts @@ -1,4 +1,10 @@ -import {containsPermissions, onPermissionsAdded, onPermissionsRemoved, requestPermissions} from "@addon-core/browser"; +import { + containsPermissions, + getManifestVersion, + onPermissionsAdded, + onPermissionsRemoved, + requestPermissions, +} from "@addon-core/browser"; import {RelayMethod, RelayOptionsMap} from "@typing/relay"; import {ContentScriptDeclarative} from "@typing/content"; @@ -38,7 +44,7 @@ export default class RelayPermission { ? undefined : { origins: !declarative || declarative === ContentScriptDeclarative.Optional ? matches : [], - permissions: ["scripting"], + permissions: getManifestVersion() === 3 ? ["scripting"] : [], }; this.set(name, {allow, permissions}); diff --git a/src/relay/adapters/RelayScriptingAdapter.test.ts b/src/relay/adapters/RelayScriptingAdapter.test.ts new file mode 100644 index 00000000..70e2cd50 --- /dev/null +++ b/src/relay/adapters/RelayScriptingAdapter.test.ts @@ -0,0 +1,150 @@ +import {getBrowserTest} from "@tests/browser-harness/session"; +import {RelayProtocolError} from "@main/relay"; +import {RelayAllFrames, RelayFrameErrorKind} from "@typing/relay"; +import RelayScriptingAdapter from "./RelayScriptingAdapter"; +import {createRelayRuntime} from "../tests/runtime"; + +test.each([true, RelayAllFrames.Any] as const)( + "envelopes an absent manager immediately for any-frame calls %s", + async allFrames => { + const runtime = createRelayRuntime(false); + const adapter = new RelayScriptingAdapter("math", {tabId: 1, allFrames}); + + await expect(adapter.invoke([], "empty")).resolves.toEqual([ + expect.objectContaining({ + status: "rejected", + error: expect.objectContaining({kind: RelayFrameErrorKind.Remote, message: "Relay manager not found."}), + }), + ]); + expect(runtime.clock!.now).toBe(0); + expect(runtime.pendingExecutions).toBe(0); + } +); + +test("makes the tenth manager lookup at 2700ms, not 2699ms", async () => { + const runtime = createRelayRuntime(false); + const adapter = new RelayScriptingAdapter("math", {tabId: 1, frameId: 0}); + const result = adapter.invoke([], "empty"); + const rejected = expect(result).rejects.toThrow("Relay manager not found after 10 attempts."); + + runtime.clock!.advance(2699); + expect(runtime.pendingExecutions).toBe(1); + runtime.clock!.advance(1); + await rejected; +}); + +test("uses a real manager registered before the final lookup", async () => { + const runtime = createRelayRuntime(false); + const adapter = new RelayScriptingAdapter("math", {tabId: 1, frameId: 0}); + const result = adapter.invoke([1, 2], "sum"); + + runtime.clock!.advance(2699); + runtime.evaluate({documentId: "document-0"}, {source: "relayFixture.register();"}); + runtime.clock!.advance(1); + await expect(result).resolves.toBe(3); +}); + +test.each([ + null, + false, + 7, + "wrong", + [], + {}, + {ok: true}, + {ok: true, hasResult: true}, + {ok: false}, + {ok: false, error: {name: 7, message: "bad"}}, +])("rejects malformed injection envelopes: %p", async value => { + getBrowserTest().harness.scripting.executeScript.setResult([{frameId: 0, documentId: "document-0", result: value}]); + const adapter = new RelayScriptingAdapter("math", {tabId: 1}); + const result = adapter.invoke([]); + + await expect(result).rejects.toBeInstanceOf(RelayProtocolError); + await expect(result).rejects.toThrow('Relay "math" returned an invalid scripting response envelope.'); +}); + +test("does not turn a remote error with the same name into a local protocol error", async () => { + getBrowserTest().harness.scripting.executeScript.setResult([ + { + frameId: 0, + documentId: "document-0", + result: {ok: false, error: {name: "RelayProtocolError", message: "From the remote handler"}}, + }, + ]); + const adapter = new RelayScriptingAdapter("math", {tabId: 1}); + const result = adapter.invoke([]); + + await expect(result).rejects.toBeInstanceOf(Error); + await expect(result).rejects.not.toBeInstanceOf(RelayProtocolError); + await expect(result).rejects.toMatchObject({name: "RelayProtocolError", message: "From the remote handler"}); +}); + +test.each(["empty", "nullable"])("preserves a successful %s result across serialization", async method => { + createRelayRuntime(); + const adapter = new RelayScriptingAdapter("math", {tabId: 1, frameId: 0}); + + await expect(adapter.invoke([], method)).resolves.toBe(method === "empty" ? undefined : null); +}); + +test.each(["fail", "reject"])("preserves remote error details from %s", async method => { + createRelayRuntime(); + const adapter = new RelayScriptingAdapter("math", {tabId: 1, frameId: 0}); + + await expect(adapter.invoke([], method)).rejects.toMatchObject({ + name: method === "fail" ? "TypeError" : "RangeError", + message: method === "fail" ? "Remote failure" : "Async failure", + }); +}); + +test("isolates malformed responses within a batch", async () => { + getBrowserTest().harness.scripting.executeScript.queueResult( + [{frameId: 2, documentId: "document-2", result: null}], + [{frameId: 0, documentId: "document-0", result: {ok: true, hasResult: false}}] + ); + const adapter = new RelayScriptingAdapter("math", {tabId: 1, frameIds: [2, 0]}); + + await expect(adapter.invoke([])).resolves.toEqual([ + {target: {tabId: 1, frameId: 0, documentId: "document-0"}, status: "fulfilled", result: undefined}, + { + target: {tabId: 1, frameId: 2, documentId: "document-2"}, + status: "rejected", + error: expect.objectContaining({ + kind: RelayFrameErrorKind.Execution, + name: "RelayProtocolError", + }), + }, + ]); +}); + +test.each([true, RelayAllFrames.Any] as const)( + "selects a valid response despite a malformed sibling with allFrames %s", + async allFrames => { + getBrowserTest().harness.scripting.executeScript.setResult([ + {frameId: 0, documentId: "document-0", result: null}, + {frameId: 2, documentId: "document-2", result: {ok: true, hasResult: true, result: 42}}, + ]); + const adapter = new RelayScriptingAdapter("math", {tabId: 1, allFrames}); + + await expect(adapter.invoke([])).resolves.toEqual([ + {target: {tabId: 1, allFrames: RelayAllFrames.Any}, status: "fulfilled", result: 42}, + ]); + } +); + +test.each(["document", "runtime"])("rejects pending execution when its %s is removed", async owner => { + const runtime = createRelayRuntime(); + const adapter = new RelayScriptingAdapter("math", {tabId: 1, frameId: 0}); + const rejected = expect(adapter.invoke([1, 2], "asyncSum")).rejects.toThrow(/removed|disposed|cancel/i); + + expect(runtime.pendingExecutions).toBe(1); + + if (owner === "document") { + getBrowserTest().harness.contexts.documents.remove("document-0"); + } else { + runtime.dispose(); + } + + await rejected; + expect(runtime.pendingExecutions).toBe(0); +}); diff --git a/src/relay/adapters/RelayScriptingAdapter.ts b/src/relay/adapters/RelayScriptingAdapter.ts index e2e52e60..db2df494 100644 --- a/src/relay/adapters/RelayScriptingAdapter.ts +++ b/src/relay/adapters/RelayScriptingAdapter.ts @@ -9,7 +9,6 @@ import injectScriptFactory, { import {restoreError} from "@message/error"; -import type {MessageError} from "@typing/message"; import { RelayAllFrames, RelayFrameErrorKind, @@ -22,23 +21,16 @@ import { type RelayScalarOptions, } from "@typing/relay"; -import {fulfilledRelayFrame, injectScriptErrorKind, rejectedRelayFrame, sortRelayFrameResults} from "../result"; +import { + RelayProtocolError, + fulfilledRelayFrame, + injectScriptErrorKind, + rejectedRelayFrame, + sortRelayFrameResults, +} from "../result"; import RelayAdapter from "./RelayAdapter"; - -type RelayInvocationResult = - | { - ok: true; - hasResult: false; - } - | { - ok: true; - hasResult: true; - result: any; - } - | { - ok: false; - error: MessageError; - }; +import {invokeRelay} from "./invoke-relay"; +import type {RelayInvocationResult} from "./scripting-response"; export default class RelayScriptingAdapter extends RelayAdapter { private _injectScript?: InjectScriptContract; @@ -52,96 +44,6 @@ export default class RelayScriptingAdapter extends RelayAdapter { } public async invoke(args: any[], path?: string): Promise { - const func = ( - name: string, - path: string | null, - args: JsonValue[], - key: string, - retryManager: boolean - ): Promise => { - const serialize = (error: unknown): MessageError => { - if (error instanceof Error) { - return { - name: error.name, - message: error.message, - ...(error.stack ? {stack: error.stack} : {}), - }; - } - - if (typeof error === "object" && error !== null) { - const record = error as Record; - - return { - name: typeof record.name === "string" ? record.name : "Error", - message: - typeof record.message === "string" - ? record.message - : (() => { - try { - return JSON.stringify(error) ?? String(error); - } catch { - return String(error); - } - })(), - ...(typeof record.stack === "string" ? {stack: record.stack} : {}), - }; - } - - return {name: "Error", message: String(error)}; - }; - - const invoke = (manager: any): Promise => { - try { - return Promise.resolve(manager.property(name, {path, args})).then( - result => - result === undefined - ? {ok: true as const, hasResult: false as const} - : {ok: true as const, hasResult: true as const, result}, - error => ({ok: false as const, error: serialize(error)}) - ); - } catch (error) { - return Promise.resolve({ok: false as const, error: serialize(error)}); - } - }; - - const manager = globalThis[key]; - - if (manager) { - // Keep the normal Scripting path synchronous until the remote method starts. - return invoke(manager); - } - - if (!retryManager) { - return Promise.reject(new Error("Relay manager not found.")); - } - - return new Promise((resolve, reject) => { - const maxAttempts = 10; - const delay = 300; - let attempts = 0; - - const findManager = () => { - const delayedManager = globalThis[key]; - - if (delayedManager) { - resolve(invoke(delayedManager)); - return; - } - - attempts++; - - if (attempts >= maxAttempts) { - reject(new Error(`Relay manager not found after ${maxAttempts} attempts.`)); - return; - } - - setTimeout(findManager, delay); - }; - - findManager(); - }); - }; - const scriptArgs: [string, string | null, JsonValue[], string, boolean] = [ this.name, path ?? null, @@ -149,7 +51,7 @@ export default class RelayScriptingAdapter extends RelayAdapter { RelayGlobalKey, !this.isAnyFramesTarget(), ]; - const outcomes = await this.injectScript.run(func, scriptArgs); + const outcomes = await this.injectScript.run(invokeRelay, scriptArgs); const results = this.normalize(outcomes); if (this.isAnyFramesTarget()) { @@ -172,7 +74,7 @@ export default class RelayScriptingAdapter extends RelayAdapter { return this.unwrap(results); } - private normalize(outcomes: readonly InjectScriptResult[]): RelayFramesResult { + private normalize(outcomes: readonly InjectScriptResult[]): RelayFramesResult { return sortRelayFrameResults( outcomes.map(outcome => { const target = this.resultTarget(outcome.target); @@ -181,6 +83,16 @@ export default class RelayScriptingAdapter extends RelayAdapter { return rejectedRelayFrame(target, outcome.error, injectScriptErrorKind(outcome.error.kind)); } + if (!this.isResponse(outcome.value)) { + const error = new RelayProtocolError(this.name); + + if (!this.isBatchTarget(this.target)) { + throw error; + } + + return rejectedRelayFrame(target, error, RelayFrameErrorKind.Execution); + } + if (!outcome.value.ok) { return rejectedRelayFrame(target, outcome.value.error, RelayFrameErrorKind.Remote); } @@ -190,6 +102,37 @@ export default class RelayScriptingAdapter extends RelayAdapter { ); } + private isResponse(value: unknown): value is RelayInvocationResult { + if (typeof value !== "object" || value === null || Array.isArray(value)) { + return false; + } + + const response = value as Record; + + if (response.ok === true) { + return response.hasResult === false + ? !Object.hasOwn(response, "result") + : response.hasResult === true && Object.hasOwn(response, "result"); + } + + if ( + response.ok !== false || + typeof response.error !== "object" || + response.error === null || + Array.isArray(response.error) + ) { + return false; + } + + const error = response.error as Record; + + return ( + typeof error.name === "string" && + typeof error.message === "string" && + (error.stack === undefined || typeof error.stack === "string") + ); + } + private resultTarget(target: InjectScriptResultTarget): RelayResultTarget { if (target.allFrames === true) { return { diff --git a/src/relay/adapters/invoke-relay.ts b/src/relay/adapters/invoke-relay.ts new file mode 100644 index 00000000..cf01412b --- /dev/null +++ b/src/relay/adapters/invoke-relay.ts @@ -0,0 +1,100 @@ +import type {JsonValue} from "@addon-core/inject-script"; +import type {MessageError} from "@typing/message"; +import type {RelayInvocationResult} from "./scripting-response"; + +// Serialized by scripting.executeScript: all runtime dependencies must stay inside this function. +export const invokeRelay = ( + name: string, + path: string | null, + args: JsonValue[], + key: string, + retryManager: boolean +): Promise => { + const serialize = (error: unknown): MessageError => { + if (error instanceof Error) { + return { + name: error.name, + message: error.message, + ...(error.stack ? {stack: error.stack} : {}), + }; + } + + if (typeof error === "object" && error !== null) { + const record = error as Record; + + return { + name: typeof record.name === "string" ? record.name : "Error", + message: + typeof record.message === "string" + ? record.message + : (() => { + try { + return JSON.stringify(error) ?? String(error); + } catch { + return String(error); + } + })(), + ...(typeof record.stack === "string" ? {stack: record.stack} : {}), + }; + } + + return {name: "Error", message: String(error)}; + }; + + const invoke = (manager: any): Promise => { + try { + return Promise.resolve(manager.property(name, {path, args})).then( + result => + result === undefined + ? {ok: true as const, hasResult: false as const} + : {ok: true as const, hasResult: true as const, result}, + error => ({ok: false as const, error: serialize(error)}) + ); + } catch (error) { + return Promise.resolve({ok: false as const, error: serialize(error)}); + } + }; + + const manager = globalThis[key]; + + if (manager) { + // Keep the normal Scripting path synchronous until the remote method starts. + return invoke(manager); + } + + if (!retryManager) { + return Promise.resolve({ok: false, error: serialize(new Error("Relay manager not found."))}); + } + + return new Promise(resolve => { + const maxAttempts = 10; + const delay = 300; + // The synchronous lookup above is the first attempt. + let attempts = 1; + + const findManager = () => { + const delayedManager = globalThis[key]; + + if (delayedManager) { + resolve(invoke(delayedManager)); + + return; + } + + attempts++; + + if (attempts >= maxAttempts) { + resolve({ + ok: false, + error: serialize(new Error(`Relay manager not found after ${maxAttempts} attempts.`)), + }); + + return; + } + + setTimeout(findManager, delay); + }; + + setTimeout(findManager, delay); + }); +}; diff --git a/src/relay/adapters/scripting-response.ts b/src/relay/adapters/scripting-response.ts new file mode 100644 index 00000000..841508e6 --- /dev/null +++ b/src/relay/adapters/scripting-response.ts @@ -0,0 +1,16 @@ +import type {MessageError} from "@typing/message"; + +export type RelayInvocationResult = + | { + ok: true; + hasResult: false; + } + | { + ok: true; + hasResult: true; + result: any; + } + | { + ok: false; + error: MessageError; + }; diff --git a/src/relay/discovery/RelayDiscovery.test.ts b/src/relay/discovery/RelayDiscovery.test.ts index 5e54cdf7..108f25fc 100644 --- a/src/relay/discovery/RelayDiscovery.test.ts +++ b/src/relay/discovery/RelayDiscovery.test.ts @@ -1,10 +1,8 @@ -import {getAllFrames, getManifest} from "@addon-core/browser"; +import {getManifest} from "@addon-core/browser"; +import {getBrowserTest} from "@tests/browser-harness/session"; import RelayDiscovery, {RelayDiscoveryError} from "./RelayDiscovery"; -const mockedGetAllFrames = getAllFrames as jest.MockedFunction; -const mockedGetManifest = getManifest as jest.MockedFunction; - const manifest = { manifest_version: 3, name: "Relay test", @@ -12,11 +10,10 @@ const manifest = { } satisfies ReturnType; describe("RelayDiscovery", () => { + const frames = () => getBrowserTest().harness.configurable.chrome.webNavigation.getAllFrames; + beforeEach(() => { - jest.clearAllMocks(); - mockedGetAllFrames.mockReset(); - mockedGetManifest.mockReset(); - mockedGetManifest.mockReturnValue({...manifest, permissions: []}); + getBrowserTest().harness.runtime.setManifest({...manifest, permissions: []}); }); test("requires webNavigation for strict Messaging allFrames discovery", async () => { @@ -28,11 +25,11 @@ describe("RelayDiscovery", () => { message: expect.stringContaining('requires the "webNavigation" permission'), }) ); - expect(mockedGetAllFrames).not.toHaveBeenCalled(); + expect(frames().calls).toHaveLength(0); }); test("reports an unavailable manifest instead of silently degrading discovery", async () => { - mockedGetManifest.mockImplementation(() => { + getBrowserTest().harness.runtime.getManifest.setImplementation(() => { throw new Error("runtime.getManifest is unavailable"); }); const discovery = new RelayDiscovery(); @@ -44,11 +41,11 @@ describe("RelayDiscovery", () => { cause: expect.objectContaining({message: "runtime.getManifest is unavailable"}), }) ); - expect(mockedGetAllFrames).not.toHaveBeenCalled(); + expect(frames().calls).toHaveLength(0); }); test("returns deterministic unique targets through webNavigation", async () => { - mockedGetManifest.mockReturnValue({...manifest, permissions: ["webNavigation"]}); + getBrowserTest().harness.runtime.setManifest({...manifest, permissions: ["webNavigation"]}); const topFrame: chrome.webNavigation.GetAllFrameResultDetails = { frameId: 0, documentId: "document-0", @@ -68,19 +65,19 @@ describe("RelayDiscovery", () => { parentDocumentId: "document-0", url: "https://example.com/frame", }; - mockedGetAllFrames.mockResolvedValue([childFrame, topFrame, childFrame]); + frames().setResult([childFrame, topFrame, childFrame]); const discovery = new RelayDiscovery(); await expect(discovery.discover(5)).resolves.toEqual([ {tabId: 5, frameId: 0, documentId: "document-0"}, {tabId: 5, frameId: 2, documentId: "document-2"}, ]); - expect(mockedGetAllFrames).toHaveBeenCalledWith(5); + expect(frames().calls[0].args).toEqual([{tabId: 5}]); }); test("reports webNavigation discovery failures", async () => { - mockedGetManifest.mockReturnValue({...manifest, permissions: ["webNavigation"]}); - mockedGetAllFrames.mockRejectedValue(new Error("No tab with id 5")); + getBrowserTest().harness.runtime.setManifest({...manifest, permissions: ["webNavigation"]}); + frames().failNext(new Error("No tab with id 5")); const discovery = new RelayDiscovery(); await expect(discovery.discover(5)).rejects.toEqual( diff --git a/src/relay/providers/Relay.test.ts b/src/relay/providers/ProxyRelay.test.ts similarity index 58% rename from src/relay/providers/Relay.test.ts rename to src/relay/providers/ProxyRelay.test.ts index 4ea8892d..106b3f95 100644 --- a/src/relay/providers/Relay.test.ts +++ b/src/relay/providers/ProxyRelay.test.ts @@ -1,13 +1,16 @@ -import {getAllFrames, getManifest} from "@addon-core/browser"; +import {getManifest} from "@addon-core/browser"; -import TransportMessage from "@transport/TransportMessage"; -import {markRemoteMessageError} from "@message/error"; +import {getBrowserTest} from "@tests/browser-harness/session"; +import type {NodeScriptRuntime} from "@addon-core/browser/testing/node"; +import {createRelayRuntime} from "../tests/runtime"; +import type {RelayFixture} from "../tests/fixtures/runtime"; +import {MessageResultEnvelopeProperty, type MessageSendOptions} from "@typing/message"; +import type {TransportMessageData} from "@typing/transport"; import ProxyRelay, {type ProxyRelayParams} from "./ProxyRelay"; -import RegisterRelay from "./RegisterRelay"; + import RelayManager from "../RelayManager"; import RelayPermission from "../RelayPermission"; -import {isRelayContext} from "../utils"; import { RelayAllFrames, @@ -19,54 +22,28 @@ import { } from "@typing/relay"; import {RpcAsyncProxy} from "@typing/rpc"; -const mockedGetAllFrames = getAllFrames as jest.MockedFunction; -const mockedGetManifest = getManifest as jest.MockedFunction; -const activationOrder: string[] = []; -let relayPermission: RelayPermission; - -const manifest = { - manifest_version: 3, - name: "Relay test", - version: "1.0.0", -} satisfies ReturnType; - -beforeEach(async () => { - jest.clearAllMocks(); - activationOrder.splice(0); +const scripting = () => getBrowserTest().harness.scripting.executeScript; +const messaging = () => { + const {harness, context} = getBrowserTest(); - RelayManager.getInstance().clear(); - - new RegisterRelay(relayName, RelayMethod.Scripting, () => MatchRelay).register(); + return harness.messaging.forContext(context).tabs.sendMessage; +}; +const frames = () => getBrowserTest().harness.configurable.chrome.webNavigation.getAllFrames; +let runtime: NodeScriptRuntime; +let relayPermission: RelayPermission; - relayPermission = { - allow: jest.fn().mockReturnValue(true), - request: jest.fn().mockResolvedValue(true), - } as unknown as RelayPermission; +const manifest = {manifest_version: 3, name: "Relay test", version: "1.0.0"} satisfies ReturnType; - mockedGetManifest.mockReturnValue({...manifest, permissions: []}); - mockedGetAllFrames.mockResolvedValue([]); +beforeEach(() => { + runtime = createRelayRuntime(); + getBrowserTest().harness.runtime.setManifest({...manifest, permissions: []}); + getBrowserTest().harness.permissions.contains.setResult(true); + relayPermission = RelayPermission.getInstance( + new Map([[relayName, {...options, name: relayName, declarative: true}]]) + ); }); -const MatchRelay = { - sum: (a: number, b: number): number => a + b, - asyncSum: (a: number, b: number): Promise => { - return new Promise(resolve => setTimeout(() => resolve(a + b), 100)); - }, - activation: (): boolean => { - activationOrder.push("relay"); - return true; - }, - fail: (): never => { - throw new TypeError("Remote failure"); - }, - one: 1, - obj: { - concat: (a: string, b: string): string => a + " " + b, - zero: 0, - }, -}; - -type RelayType = typeof MatchRelay; +type RelayType = RelayFixture; type RelayProxyType = RpcAsyncProxy; type RelayBatchProxyType = RelayBatchRpcProxy; @@ -82,24 +59,24 @@ const createProxyRelay = (relayOptions: RelayOptions, params }; const expectScriptInjection = (expected: Partial>) => { - const [injection] = (chrome.scripting.executeScript as jest.Mock).mock.calls.at(-1); - - expect(injection).toEqual(expect.objectContaining(expected)); + expect(scripting().calls.at(-1)?.args[0]).toEqual(expect.objectContaining(expected)); }; const expectScriptTargets = (expected: chrome.scripting.InjectionTarget[]) => { - const targets = (chrome.scripting.executeScript as jest.Mock).mock.calls.map(([injection]) => injection.target); + expect( + scripting().calls.map(call => (call.args[0] as chrome.scripting.ScriptInjection).target) + ).toEqual(expected); +}; + +const expectMessageCall = (index: number, data: TransportMessageData, target: Exclude) => { + const {tabId, ...options} = target; - expect(targets).toEqual(expected); + expect(messaging().calls[index].args).toEqual([tabId, expect.objectContaining({data}), options]); }; describe("ProxyRelay", () => { - beforeEach(async () => { - (isRelayContext as jest.Mock).mockReturnValue(false); - }); - test("throws an error when get() is called in content script context", async () => { - (isRelayContext as jest.Mock).mockReturnValue(true); + RelayManager.getInstance(); const proxy = createProxyRelay(options, 1); @@ -137,7 +114,7 @@ describe("ProxyRelay", () => { expect(await relay.sum(1, 2)).toBe(3); - expect(chrome.scripting.executeScript).toHaveBeenCalledTimes(1); + expect(scripting().calls).toHaveLength(1); expectScriptInjection({ target: {tabId: 1}, @@ -195,16 +172,20 @@ describe("ProxyRelay", () => { test("calls async method on proxy and returns resolved value", async () => { const relay = createProxyRelay(options, 1).get(); - expect(await relay.asyncSum(1, 2)).toBe(3); + const result = relay.asyncSum(1, 2); + + runtime.clock!.advance(100); + expect(await result).toBe(3); }); test("starts a Scripting relay method before returning control to the caller", async () => { const relay = createProxyRelay(options, 1).get(); const result = relay.activation(); - activationOrder.push("caller"); - - expect(activationOrder).toEqual(["relay", "caller"]); + runtime.evaluate( + {documentId: "document-0"}, + {source: 'if (!relayFixture.started) { throw new Error("Relay method has not started"); }'} + ); await expect(result).resolves.toBe(true); }); @@ -251,7 +232,7 @@ describe("ProxyRelay", () => { }); test("normalizes an unobservable Scripting frame outcome", async () => { - (chrome.scripting.executeScript as jest.Mock).mockImplementationOnce(async () => [{frameId: 0}]); + scripting().queueResult([{frameId: 0, documentId: "document-0"}]); const relay = createProxyRelay(options, { tabId: 1, frameIds: [0], @@ -259,7 +240,7 @@ describe("ProxyRelay", () => { await expect(relay.sum(1, 2)).resolves.toEqual([ { - target: {tabId: 1, frameId: 0}, + target: {tabId: 1, frameId: 0, documentId: "document-0"}, status: "rejected", error: expect.objectContaining({ kind: RelayFrameErrorKind.Unobservable, @@ -270,29 +251,23 @@ describe("ProxyRelay", () => { }); test("keeps a target-gone failure isolated in an explicit Scripting batch", async () => { - (chrome.scripting.executeScript as jest.Mock) - .mockImplementationOnce(async injection => [ - { - frameId: 0, - result: await injection.func(...injection.args), - }, - ]) - .mockImplementationOnce(async () => { - throw new Error("No frame with id 2 in tab 1"); - }); + scripting().queueResult([ + {frameId: 0, documentId: "document-0", result: {ok: true, hasResult: true, result: 3}}, + ]); + scripting().failNext(new Error("No frame with id 2 in tab 1")); const relay = createProxyRelay(options, { tabId: 1, - frameIds: [0, 2], + frameIds: [2, 0], }).get(); await expect(relay.sum(1, 2)).resolves.toEqual([ - {target: {tabId: 1, frameId: 0}, status: "fulfilled", result: 3}, + {target: {tabId: 1, frameId: 0, documentId: "document-0"}, status: "fulfilled", result: 3}, { target: {tabId: 1, frameId: 2}, status: "rejected", error: expect.objectContaining({ kind: RelayFrameErrorKind.TargetGone, - message: "No frame with id 2 in tab 1", + message: expect.stringMatching(/frame.*2/i), }), }, ]); @@ -319,49 +294,21 @@ describe("ProxyRelay", () => { ]); expectScriptInjection({target: {tabId: 1, allFrames: true}}); - expect(mockedGetAllFrames).not.toHaveBeenCalled(); + expect(frames().calls).toHaveLength(0); } ); test.each([true, RelayAllFrames.Any] as const)( "returns a fulfilled Scripting outcome for allFrames %s when another frame is rejected", async allFrames => { - (chrome.scripting.executeScript as jest.Mock).mockResolvedValueOnce([ - {frameId: 0, error: new Error("Relay manager not found.")}, - {frameId: 2, result: {ok: true, hasResult: true, result: 3}}, - ]); - const relay = createProxyRelay(options, { - tabId: 1, - allFrames, - }).get(); - - await expect(relay.sum(1, 2)).resolves.toEqual([ + scripting().queueResult([ { - target: {tabId: 1, allFrames: RelayAllFrames.Any}, - status: "fulfilled", - result: 3, + frameId: 0, + documentId: "document-0", + result: {ok: false, error: {name: "Error", message: "Relay manager not found."}}, }, + {frameId: 2, documentId: "document-2", result: {ok: true, hasResult: true, result: 3}}, ]); - } - ); - - test.each([true, RelayAllFrames.Any] as const)( - "does not retry a missing Relay manager for Scripting allFrames %s", - async allFrames => { - (chrome.scripting.executeScript as jest.Mock).mockImplementationOnce(async injection => { - const injectedArgs = [...injection.args]; - injectedArgs[3] = `${RelayGlobalKey}Missing`; - const setTimeoutSpy = jest.spyOn(globalThis, "setTimeout"); - const execution = injection.func(...injectedArgs); - - expect(injectedArgs.at(-1)).toBe(false); - expect(setTimeoutSpy).not.toHaveBeenCalled(); - setTimeoutSpy.mockRestore(); - - const error = await execution.catch((error: unknown) => error); - - return [{frameId: 0, error}]; - }); const relay = createProxyRelay(options, { tabId: 1, allFrames, @@ -370,37 +317,13 @@ describe("ProxyRelay", () => { await expect(relay.sum(1, 2)).resolves.toEqual([ { target: {tabId: 1, allFrames: RelayAllFrames.Any}, - status: "rejected", - error: expect.objectContaining({ - kind: RelayFrameErrorKind.Execution, - message: "Relay manager not found.", - }), + status: "fulfilled", + result: 3, }, ]); } ); - test("keeps manager retries for addressed Scripting calls", async () => { - (chrome.scripting.executeScript as jest.Mock).mockImplementationOnce(async injection => { - const injectedArgs = [...injection.args]; - injectedArgs[3] = `${RelayGlobalKey}Missing`; - const setTimeoutSpy = jest - .spyOn(globalThis, "setTimeout") - .mockImplementation(() => 0 as unknown as ReturnType); - - injection.func(...injectedArgs); - - expect(injectedArgs.at(-1)).toBe(true); - expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), 300); - setTimeoutSpy.mockRestore(); - - return [{frameId: 0, error: new Error("Relay manager not found.")}]; - }); - const relay = createProxyRelay(options, {tabId: 1, frameId: 0}).get(); - - await expect(relay.sum(1, 2)).rejects.toThrow("Relay manager not found."); - }); - test("returns every Scripting outcome for RelayAllFrames.All", async () => { const relay = createProxyRelay(options, { tabId: 1, @@ -429,15 +352,12 @@ describe("ProxyRelay", () => { ]); expectScriptInjection({target: {tabId: 1, allFrames: true}}); - expect(mockedGetAllFrames).not.toHaveBeenCalled(); + expect(frames().calls).toHaveLength(0); }); test("checks Relay permission once for a batch call", async () => { - const permission = { - allow: jest.fn().mockReturnValue(true), - request: jest.fn(), - }; - relayPermission = permission as unknown as RelayPermission; + relayPermission.set(relayName, {allow: false}); + getBrowserTest().harness.permissions.request.setResult(true); const relay = createProxyRelay(options, { tabId: 1, @@ -446,20 +366,14 @@ describe("ProxyRelay", () => { await relay.sum(1, 2); - expect(permission.allow).toHaveBeenCalledTimes(1); - expect(permission.request).not.toHaveBeenCalled(); + expect(getBrowserTest().harness.permissions.request.calls).toHaveLength(1); }); test("fans Messaging calls out to explicit frameIds and keeps partial failures", async () => { - const send = jest.spyOn(TransportMessage.prototype, "send").mockImplementation(async (_data, target) => { - const frameId = typeof target === "object" ? target.frameId : undefined; - - if (frameId === 2) { - throw new Error("Frame with ID 2 was removed"); - } + const send = messaging(); - return frameId; - }); + send.failNext(new Error("Frame with ID 2 was removed")); + send.queueResult({[MessageResultEnvelopeProperty]: true, ok: true, payload: 0}); const relay = createProxyRelay( {...options, method: RelayMethod.Messaging}, {tabId: 1, frameIds: [2, 0]} @@ -477,12 +391,19 @@ describe("ProxyRelay", () => { }, ]); - expect(send).toHaveBeenCalledTimes(2); - send.mockRestore(); + expect(send.calls).toHaveLength(2); }); test("returns a per-frame timeout from Messaging", async () => { - const send = jest.spyOn(TransportMessage.prototype, "send").mockImplementation(() => new Promise(() => {})); + const receiver = getBrowserTest().harness.contexts.create({ + kind: "contentScript", + tabId: 1, + frameId: 2, + documentId: "document-2", + url: "https://example.com/", + }); + + receiver.onMessage.on(() => true); const relay = createProxyRelay( {...options, method: RelayMethod.Messaging}, {tabId: 1, frameIds: [2], timeoutMs: 5} @@ -498,12 +419,15 @@ describe("ProxyRelay", () => { }), }, ]); - - send.mockRestore(); }); test("passes timeoutMs to Inject Script", async () => { - (chrome.scripting.executeScript as jest.Mock).mockImplementationOnce(() => new Promise(() => {})); + getBrowserTest().harness.contexts.documents.remove("document-0"); + getBrowserTest().harness.contexts.documents.create({ + documentId: "pending", + tabId: 1, + url: "https://example.com/", + }); const relay = createProxyRelay(options, { tabId: 1, frameIds: [0], @@ -523,9 +447,11 @@ describe("ProxyRelay", () => { }); test("classifies restored remote Messaging errors structurally", async () => { - const send = jest - .spyOn(TransportMessage.prototype, "send") - .mockRejectedValue(markRemoteMessageError(new TypeError("Remote failure"))); + messaging().setResult({ + [MessageResultEnvelopeProperty]: true, + ok: false, + error: {name: "TypeError", message: "Remote failure"}, + }); const relay = createProxyRelay( {...options, method: RelayMethod.Messaging}, {tabId: 1, frameIds: [0]} @@ -542,14 +468,12 @@ describe("ProxyRelay", () => { }), }, ]); - - send.mockRestore(); }); test.each([true, RelayAllFrames.Any] as const)( "returns one native Messaging outcome for allFrames %s without discovery", async allFrames => { - const send = jest.spyOn(TransportMessage.prototype, "send").mockResolvedValue(3); + messaging().setResult({[MessageResultEnvelopeProperty]: true, ok: true, payload: 3}); const relay = createProxyRelay( {...options, method: RelayMethod.Messaging}, {tabId: 1, allFrames} @@ -563,14 +487,13 @@ describe("ProxyRelay", () => { }, ]); - expect(mockedGetAllFrames).not.toHaveBeenCalled(); - expect(send).toHaveBeenCalledWith({path: "sum", args: [1, 2]}, {tabId: 1}); - send.mockRestore(); + expect(frames().calls).toHaveLength(0); + expectMessageCall(0, {path: "sum", args: [1, 2]}, {tabId: 1}); } ); test("keeps allFrames false on the scalar top-frame contract", async () => { - const send = jest.spyOn(TransportMessage.prototype, "send").mockResolvedValue(3); + messaging().setResult({[MessageResultEnvelopeProperty]: true, ok: true, payload: 3}); const relay = createProxyRelay( {...options, method: RelayMethod.Messaging}, {tabId: 1, allFrames: false} @@ -578,13 +501,12 @@ describe("ProxyRelay", () => { await expect(relay.sum(1, 2)).resolves.toBe(3); - expect(mockedGetAllFrames).not.toHaveBeenCalled(); - expect(send).toHaveBeenCalledWith({path: "sum", args: [1, 2]}, {tabId: 1, frameId: 0}); - send.mockRestore(); + expect(frames().calls).toHaveLength(0); + expectMessageCall(0, {path: "sum", args: [1, 2]}, {tabId: 1, frameId: 0}); }); test("discovers strict Messaging allFrames targets through webNavigation", async () => { - mockedGetManifest.mockReturnValue({...manifest, permissions: ["webNavigation"]}); + getBrowserTest().harness.runtime.setManifest({...manifest, permissions: ["webNavigation"]}); const topFrame: chrome.webNavigation.GetAllFrameResultDetails = { frameId: 0, documentId: "document-0", @@ -604,10 +526,11 @@ describe("ProxyRelay", () => { parentDocumentId: "document-0", url: "https://example.com/frame", }; - mockedGetAllFrames.mockResolvedValue([childFrame, topFrame]); - const send = jest.spyOn(TransportMessage.prototype, "send").mockImplementation(async (_data, target) => { - return typeof target === "object" ? target.frameId : undefined; - }); + frames().setResult([childFrame, topFrame]); + messaging().queueResult( + {[MessageResultEnvelopeProperty]: true, ok: true, payload: 0}, + {[MessageResultEnvelopeProperty]: true, ok: true, payload: 3} + ); const relay = createProxyRelay( {...options, method: RelayMethod.Messaging}, {tabId: 1, allFrames: RelayAllFrames.All} @@ -626,89 +549,8 @@ describe("ProxyRelay", () => { }, ]); - expect(mockedGetAllFrames).toHaveBeenCalledWith(1); - expect(send).toHaveBeenNthCalledWith( - 1, - {path: "sum", args: [1, 2]}, - {tabId: 1, frameId: 0, documentId: "document-0"} - ); - expect(send).toHaveBeenNthCalledWith( - 2, - {path: "sum", args: [1, 2]}, - {tabId: 1, frameId: 3, documentId: "document-3"} - ); - send.mockRestore(); - }); -}); - -describe("RegisterRelay", () => { - beforeEach(async () => { - (isRelayContext as jest.Mock).mockReturnValue(true); - }); - - test("throws if trying to get registered relay from non-content script", async () => { - (isRelayContext as jest.Mock).mockReturnValue(false); - - const proxy = new RegisterRelay(relayName, RelayMethod.Scripting, () => MatchRelay); - - expect(() => proxy.get()).toThrow(`Relay "${relayName}" can be getting only from content script`); - }); - - test("returns real relay when called in content script context", () => { - const relay = new RegisterRelay( - relayName, - RelayMethod.Scripting, - () => MatchRelay - ).get(); - - expect(relay["__proxy"]).toBe(undefined); - }); - - test("calls method directly in content script without chrome.scripting", async () => { - const relay = new RegisterRelay( - relayName, - RelayMethod.Scripting, - () => MatchRelay - ).get(); - - expect(relay.sum(1, 2)).toBe(3); - expect(chrome.scripting.executeScript).toHaveBeenCalledTimes(0); - }); - - test("throws an error when attempting to register the same relay twice", async () => { - const relay = new RegisterRelay( - relayName, - RelayMethod.Scripting, - () => MatchRelay - ); - - expect(() => relay.register()).toThrow( - `A relay with the name "${relayName}" already exists. The relay name must be unique.` - ); - }); - - test("does not call parent register method when RelayMethod is 'scripting'", () => { - const registerRelay = new RegisterRelay(relayName, RelayMethod.Scripting, () => MatchRelay); - const parentRegisterSpy = jest.spyOn(Object.getPrototypeOf(Object.getPrototypeOf(registerRelay)), "register"); - jest.spyOn(RelayManager.getInstance(), "has").mockReturnValue(false); - - registerRelay.register(); - - expect(parentRegisterSpy).not.toHaveBeenCalled(); - - parentRegisterSpy.mockRestore(); - }); - - test("calls parent register method when method is 'messaging'", () => { - const registerRelay = new RegisterRelay(relayName, RelayMethod.Messaging, () => MatchRelay); - const parentRegisterSpy = jest.spyOn(Object.getPrototypeOf(Object.getPrototypeOf(registerRelay)), "register"); - parentRegisterSpy.mockReturnValue(MatchRelay); - - const result = registerRelay.register(); - - expect(result).toBe(MatchRelay); - expect(parentRegisterSpy).toHaveBeenCalledWith(); - - parentRegisterSpy.mockRestore(); + expect(frames().calls[0].args).toEqual([{tabId: 1}]); + expectMessageCall(0, {path: "sum", args: [1, 2]}, {tabId: 1, frameId: 0, documentId: "document-0"}); + expectMessageCall(1, {path: "sum", args: [1, 2]}, {tabId: 1, frameId: 3, documentId: "document-3"}); }); }); diff --git a/src/relay/providers/RegisterRelay.test.ts b/src/relay/providers/RegisterRelay.test.ts new file mode 100644 index 00000000..60817635 --- /dev/null +++ b/src/relay/providers/RegisterRelay.test.ts @@ -0,0 +1,72 @@ +import {createTabFixture} from "@addon-core/browser/testing"; +import {getBrowserTest} from "@tests/browser-harness/session"; +import {RelayGlobalKey, RelayMethod} from "@typing/relay"; +import RelayManager from "../RelayManager"; +import RegisterRelay from "./RegisterRelay"; +import RelayMessagingAdapter from "../adapters/RelayMessagingAdapter"; + +const math = {sum: (a: number, b: number): number => a + b}; + +beforeEach(() => { + new RegisterRelay("math", RelayMethod.Scripting, () => math).register(); +}); + +test("rejects access outside the Relay content context", () => { + Reflect.deleteProperty(globalThis, RelayGlobalKey); + + expect(() => new RegisterRelay("math", RelayMethod.Scripting, () => math).get()).toThrow( + 'Relay "math" can be getting only from content script' + ); +}); + +test("returns the registered object and calls it directly without scripting", () => { + const relay = new RegisterRelay("math", RelayMethod.Scripting, () => math).get(); + + expect(relay).toBe(math); + expect(relay.sum(1, 2)).toBe(3); + expect(getBrowserTest().harness.scripting.executeScript.calls).toHaveLength(0); +}); + +test("rejects duplicate scripting registrations", () => { + expect(() => new RegisterRelay("math", RelayMethod.Scripting, () => math).register()).toThrow( + 'A relay with the name "math" already exists. The relay name must be unique.' + ); +}); + +test.each([RelayMethod.Scripting, RelayMethod.Messaging])("owns registration and cleanup for %s", method => { + const session = getBrowserTest(); + const register = new RegisterRelay("another", method, () => math); + + expect(register.register()).toBe(math); + expect(RelayManager.getInstance().get("another")).toBe(math); + expect(session.context.onMessage.listenerCount()).toBe(method === RelayMethod.Messaging ? 1 : 0); + register.destroy(); + expect(RelayManager.getInstance().has("another")).toBe(false); + expect(session.context.onMessage.listenerCount()).toBe(0); +}); + +test("serves a real Relay messaging call from a different context", async () => { + const session = getBrowserTest(); + session.harness.tabs.set([createTabFixture({id: 1})]); + + const receiver = session.harness.contexts.create({ + kind: "contentScript", + tabId: 1, + frameId: 0, + url: "https://example.com/", + }); + const restore = session.useContext(receiver); + const register = new RegisterRelay("remote", RelayMethod.Messaging, () => math); + + register.register(); + session.addCleanup(() => register.destroy()); + restore(); + + const adapter = new RelayMessagingAdapter("remote", {tabId: 1, frameId: 0}); + + await expect(adapter.invoke([2, 3], "sum")).resolves.toBe(5); + expect(receiver.onMessage.listenerCount()).toBe(1); + register.destroy(); + expect(receiver.onMessage.listenerCount()).toBe(0); + await expect(adapter.invoke([2, 3], "sum")).rejects.toThrow(/Receiving end does not exist/); +}); diff --git a/src/relay/result.ts b/src/relay/result.ts index b72ca9e1..544569b7 100644 --- a/src/relay/result.ts +++ b/src/relay/result.ts @@ -16,6 +16,13 @@ export class RelayFrameTimeoutError extends Error { } } +export class RelayProtocolError extends Error { + public constructor(relayName: string) { + super(`Relay "${relayName}" returned an invalid scripting response envelope.`); + this.name = "RelayProtocolError"; + } +} + export const fulfilledRelayFrame = (target: RelayResultTarget, result: T): RelayFrameResult => ({ target, status: "fulfilled", diff --git a/src/relay/tests/fixtures/runtime.ts b/src/relay/tests/fixtures/runtime.ts new file mode 100644 index 00000000..c091fe16 --- /dev/null +++ b/src/relay/tests/fixtures/runtime.ts @@ -0,0 +1,29 @@ +import RelayManager from "@relay/RelayManager"; +import RegisterRelay from "@relay/providers/RegisterRelay"; +import {RelayMethod} from "@typing/relay"; + +const math = { + sum: (a: number, b: number): number => a + b, + asyncSum: (a: number, b: number): Promise => new Promise(resolve => setTimeout(() => resolve(a + b), 100)), + activation: (): boolean => { + globalThis.relayFixture.started = true; + + return true; + }, + fail: (): never => { + throw new TypeError("Remote failure"); + }, + reject: (): Promise => Promise.reject(new RangeError("Async failure")), + empty: (): void => {}, + nullable: (): null => null, + one: 1, + obj: {concat: (a: string, b: string): string => `${a} ${b}`, zero: 0}, +}; + +globalThis.relayFixture = { + started: false, + register: () => new RegisterRelay("math", RelayMethod.Scripting, () => math).register(), + has: () => RelayManager.getInstance().has("math"), +}; + +export type RelayFixture = typeof math; diff --git a/src/relay/tests/runtime.ts b/src/relay/tests/runtime.ts new file mode 100644 index 00000000..ffca838a --- /dev/null +++ b/src/relay/tests/runtime.ts @@ -0,0 +1,42 @@ +import path from "node:path"; +import {buildSync} from "esbuild"; +import type {NodeScriptRuntime} from "@addon-core/browser/testing/node"; +import {createTabFixture} from "@addon-core/browser/testing"; +import {getBrowserTest} from "@tests/browser-harness/session"; + +const source = buildSync({ + entryPoints: [path.resolve(__dirname, "fixtures/runtime.ts")], + tsconfig: path.resolve(__dirname, "../../../tsconfig.json"), + bundle: true, + write: false, + format: "iife", + platform: "browser", + target: "es2020", +}).outputFiles[0].text; + +export function createRelayRuntime(register = true): NodeScriptRuntime { + const session = getBrowserTest(); + + session.harness.tabs.set([createTabFixture({id: 1})]); + + for (const frameId of [0, 2]) { + session.harness.contexts.documents.create({ + documentId: `document-${frameId}`, + tabId: 1, + frameId, + url: "https://example.com/", + }); + } + + const runtime = session.createScriptRuntime({clock: true}); + + for (const frameId of [0, 2]) { + runtime.evaluate({documentId: `document-${frameId}`}, {source}); + + if (register) { + runtime.evaluate({documentId: `document-${frameId}`}, {source: "relayFixture.register();"}); + } + } + + return runtime; +} diff --git a/src/sandbox/SandboxMessage.test.ts b/src/sandbox/SandboxMessage.test.ts index 1d325e75..424a912c 100644 --- a/src/sandbox/SandboxMessage.test.ts +++ b/src/sandbox/SandboxMessage.test.ts @@ -1,15 +1,23 @@ -import {nanoid} from "nanoid"; +import {getBrowserTest} from "@tests/browser-harness/session"; import SandboxMessage from "./SandboxMessage"; import {SandboxMemory} from "./ports"; -import type {SandboxParameters} from "@typing/sandbox"; +import type {SandboxParameters, SandboxPort} from "@typing/sandbox"; + +const createMessage = (port: SandboxPort, parameters: Partial = {}): SandboxMessage => { + const message = new SandboxMessage("parser", port, parameters); + + getBrowserTest().addCleanup(() => message.dispose()); + + return message; +}; describe("SandboxMessage", () => { test("round-trips a request to the sandbox handler and resolves the response", async () => { const [hostPort, guestPort] = SandboxMemory.pair(); - const host = new SandboxMessage("parser", hostPort, {requestTimeout: 1000}); - const guest = new SandboxMessage("parser", guestPort); + const host = createMessage(hostPort, {requestTimeout: 1000}); + const guest = createMessage(guestPort); let receivedPath: string | undefined; @@ -25,8 +33,8 @@ describe("SandboxMessage", () => { test("propagates handler errors back to the caller", async () => { const [hostPort, guestPort] = SandboxMemory.pair(); - const host = new SandboxMessage("parser", hostPort, {requestTimeout: 1000}); - const guest = new SandboxMessage("parser", guestPort); + const host = createMessage(hostPort, {requestTimeout: 1000}); + const guest = createMessage(guestPort); guest.watch(() => { throw new TypeError("bad html"); @@ -39,31 +47,73 @@ describe("SandboxMessage", () => { }); test("rejects when no response arrives before requestTimeout", async () => { - const [hostPort] = SandboxMemory.pair(); // nothing watches the guest end + jest.useFakeTimers(); + + const [hostPort, guestPort] = SandboxMemory.pair(); + const host = createMessage(hostPort, {requestTimeout: 10}); + + createMessage(guestPort); // nothing watches the guest end + + const result = expect(host.send({path: "parse", args: []})).rejects.toThrow('Sandbox "parser" request'); + + await jest.advanceTimersByTimeAsync(0); + expect(jest.getTimerCount()).toBe(1); + await jest.advanceTimersByTimeAsync(9); + expect(jest.getTimerCount()).toBe(1); + await jest.advanceTimersByTimeAsync(1); + await result; + expect(jest.getTimerCount()).toBe(0); + }); + + test("correlates concurrent requests when responses arrive in reverse order", async () => { + jest.useFakeTimers(); + + const [hostPort, guestPort] = SandboxMemory.pair(); + const host = createMessage(hostPort, {requestTimeout: 1000}); + const guest = createMessage(guestPort); + const completed: number[] = []; + + guest.watch(({args}) => { + const value = args[0] as number; + + return new Promise(resolve => { + setTimeout( + () => { + completed.push(value); + resolve(value); + }, + (4 - value) * 10 + ); + }); + }); - const host = new SandboxMessage("parser", hostPort, {requestTimeout: 10}); + const result = Promise.all([ + host.send({path: "echo", args: [1]}), + host.send({path: "echo", args: [2]}), + host.send({path: "echo", args: [3]}), + ]); - await expect(host.send({path: "parse", args: []})).rejects.toThrow('Sandbox "parser" request'); + await jest.advanceTimersByTimeAsync(30); + await expect(result).resolves.toEqual([1, 2, 3]); + expect(completed).toEqual([3, 2, 1]); + expect(jest.getTimerCount()).toBe(0); }); - test("concurrent requests share the channel and all resolve", async () => { - // nanoid is globally mocked to a constant in tests; give each request a unique id. - let counter = 0; - jest.mocked(nanoid).mockImplementation(() => `req-${++counter}`); + test("rejects pending requests and clears their timers on dispose", async () => { + jest.useFakeTimers(); const [hostPort, guestPort] = SandboxMemory.pair(); - const host = new SandboxMessage("parser", hostPort, {requestTimeout: 1000}); - const guest = new SandboxMessage("parser", guestPort); - - guest.watch(({args}) => args[0]); - - await expect( - Promise.all([ - host.send({path: "echo", args: [1]}), - host.send({path: "echo", args: [2]}), - host.send({path: "echo", args: [3]}), - ]) - ).resolves.toEqual([1, 2, 3]); + const host = createMessage(hostPort, {requestTimeout: 1000}); + + createMessage(guestPort); + + const result = expect(host.send({path: "parse", args: []})).rejects.toThrow('Sandbox "parser" was disposed.'); + + await jest.advanceTimersByTimeAsync(0); + expect(jest.getTimerCount()).toBe(1); + host.dispose(); + await result; + expect(jest.getTimerCount()).toBe(0); }); test("caches one host channel per name and re-creates after dispose", () => { diff --git a/src/service/providers/ProxyService.context.test.ts b/src/service/providers/ProxyService.context.test.ts deleted file mode 100644 index 4b52ca3e..00000000 --- a/src/service/providers/ProxyService.context.test.ts +++ /dev/null @@ -1,126 +0,0 @@ -/** @jest-environment node */ - -jest.unmock("@addon-core/browser"); - -import {getService} from "@main/service"; - -import ProxyService from "./ProxyService"; - -import {MessageResultEnvelopeProperty, MessageTypeSeparator} from "@typing/message"; - -type ServiceProxy = { - someMethod(value: string): Promise; -}; - -type RuntimeSendMessage = (message: unknown, callback: (response: unknown) => void) => void; - -const serviceName = "test"; - -const setRuntime = (runtime?: {sendMessage?: RuntimeSendMessage; getManifest?: () => chrome.runtime.Manifest}) => { - Object.defineProperty(globalThis, "chrome", { - configurable: true, - writable: true, - value: runtime - ? { - runtime: { - id: "extension-id", - ...runtime, - }, - } - : undefined, - }); -}; - -const setExtensionDocument = (pathname: string) => { - Object.defineProperty(globalThis, "window", {configurable: true, value: {}}); - Object.defineProperty(globalThis, "location", {configurable: true, value: {pathname}}); -}; - -const getTestService = (): ServiceProxy => getService(serviceName as never) as ServiceProxy; - -describe("ProxyService context detection", () => { - afterEach(() => { - jest.clearAllMocks(); - }); - - test("creates a service proxy in an offscreen-like context with no runtime.getManifest", async () => { - const expectedResult = "background:value"; - const sendMessage = jest.fn, Parameters>( - (_message, callback) => { - callback({[MessageResultEnvelopeProperty]: true, ok: true, payload: expectedResult}); - } - ); - - setRuntime({sendMessage}); - setExtensionDocument("/offscreen.html"); - - expect(() => getTestService()).not.toThrow(); - - const service = getTestService(); - - // @ts-expect-error - The runtime proxy marker is intentionally hidden from its public type. - expect(service.__proxy).toBe(true); - await expect(service.someMethod("value")).resolves.toBe(expectedResult); - expect(sendMessage).toHaveBeenCalledWith( - expect.objectContaining({ - type: `service${MessageTypeSeparator}${serviceName}`, - data: { - path: "someMethod", - args: ["value"], - }, - }), - expect.any(Function) - ); - }); - - test("restores serialized background errors in an offscreen-like context", async () => { - const sendMessage = jest.fn, Parameters>( - (_message, callback) => { - callback({ - [MessageResultEnvelopeProperty]: true, - ok: false, - error: {name: "TypeError", message: "background failed"}, - }); - } - ); - - setRuntime({sendMessage}); - setExtensionDocument("/offscreen.html"); - - await expect(getTestService().someMethod("value")).rejects.toThrow("background failed"); - await expect(getTestService().someMethod("value")).rejects.toBeInstanceOf(TypeError); - }); - - test.each([ - ["popup", "/popup.html"], - ["sidebar", "/sidebar.html"], - ["content script", "/content-script.html"], - ])("creates a proxy in %s context", (_context, pathname) => { - setRuntime({ - getManifest: () => - ({manifest_version: 3, background: {service_worker: "background.js"}}) as chrome.runtime.Manifest, - }); - setExtensionDocument(pathname); - - expect(() => getTestService()).not.toThrow(); - }); - - test("creates a proxy when the WebExtension API is unavailable", () => { - setRuntime(); - setExtensionDocument("/standalone.html"); - - expect(() => getTestService()).not.toThrow(); - }); - - test("continues to reject service proxies in an MV3 service worker", () => { - setRuntime({ - getManifest: () => - ({manifest_version: 3, background: {service_worker: "background.js"}}) as chrome.runtime.Manifest, - }); - Object.defineProperty(globalThis, "window", {configurable: true, value: undefined}); - - expect(() => new ProxyService(serviceName).get()).toThrow( - `You are trying to get proxy service "${serviceName}" from background. You can get original service instead` - ); - }); -}); diff --git a/src/service/providers/ProxyService.test.ts b/src/service/providers/ProxyService.test.ts new file mode 100644 index 00000000..51fb3943 --- /dev/null +++ b/src/service/providers/ProxyService.test.ts @@ -0,0 +1,171 @@ +import { + createManifestFixture, + createTabFixture, + installGlobals, + type BrowserContext, +} from "@addon-core/browser/testing"; +import {getService} from "@main/service"; +import {getBrowserTest} from "@tests/browser-harness/session"; +import {ProxyService, RegisterService} from "./index"; +import {mathService, serviceName, type MathService} from "./tests/fixtures/math-service"; +import type {RpcAsyncProxy} from "@typing/rpc"; +import {MessageTypeSeparator} from "@typing/message"; + +type ServiceProxy = RpcAsyncProxy; + +let background: BrowserContext; + +const getTestService = (): ServiceProxy => getService(serviceName as never) as ServiceProxy; +const sender = () => getBrowserTest().harness.messaging.forContext(getBrowserTest().context); + +beforeEach(() => { + const session = getBrowserTest(); + background = session.harness.contexts.create({kind: "background"}); + + session.harness.runtime.setManifest( + createManifestFixture({manifest_version: 3, background: {service_worker: "background.js"}}) + ); + session.addCleanup(installGlobals({window: undefined})); + + const restore = session.useContext(background); + const registration = new RegisterService(serviceName, () => mathService); + + registration.register(); + session.addCleanup(() => registration.destroy()); + restore(); + session.addCleanup(installGlobals({window: {}, location: {pathname: "/popup.html"}})); +}); + +describe("ProxyService", () => { + test("rejects proxies in an MV3 background service worker", () => { + const session = getBrowserTest(); + + session.useContext(background); + session.addCleanup(installGlobals({window: undefined})); + + expect(() => new ProxyService(serviceName).get()).toThrow( + `You are trying to get proxy service "${serviceName}" from background. You can get original service instead` + ); + }); + + test("returns a proxy outside background", () => { + expect(Reflect.get(getTestService(), "__proxy")).toBe(true); + }); + + test("rejects proxies in an MV2 generated background page", () => { + const session = getBrowserTest(); + + session.harness.runtime.setManifest( + createManifestFixture({manifest_version: 2, background: {scripts: ["background.js"]}}) + ); + session.useContext(background); + session.addCleanup(installGlobals({window: {}, location: {pathname: "/_generated_background_page.html"}})); + + expect(() => getTestService()).toThrow( + `You are trying to get proxy service "${serviceName}" from background. You can get original service instead` + ); + }); + + test("invokes a real background method through runtime messaging", async () => { + await expect(getTestService().sum(1, 2)).resolves.toBe(3); + expect(sender().runtime.sendMessage.calls.at(-1)?.args).toEqual([ + expect.objectContaining({ + type: `service${MessageTypeSeparator}${serviceName}`, + data: {path: "sum", args: [1, 2]}, + }), + ]); + }); + + test("reads a service property", async () => { + await expect(getTestService().one()).resolves.toBe(1); + expect(sender().runtime.sendMessage.calls.at(-1)?.args).toEqual([ + expect.objectContaining({data: {path: "one", args: []}}), + ]); + }); + + test("accesses nested methods and falsy properties", async () => { + const service = getTestService(); + + await expect(service.obj.concat("Hello", "world")).resolves.toBe("Hello world"); + expect(sender().runtime.sendMessage.calls.at(-1)?.args).toEqual([ + expect.objectContaining({data: {path: "obj.concat", args: ["Hello", "world"]}}), + ]); + await expect(service.obj.zero()).resolves.toBe(0); + expect(sender().runtime.sendMessage.calls.at(-1)?.args).toEqual([ + expect.objectContaining({data: {path: "obj.zero", args: []}}), + ]); + }); + + test("awaits asynchronous background methods", async () => { + jest.useFakeTimers(); + + const result = getTestService().asyncSum(1, 2); + + await jest.advanceTimersByTimeAsync(100); + await expect(result).resolves.toBe(3); + }); + + test("calls background from an offscreen context without runtime.getManifest", async () => { + const session = getBrowserTest(); + const offscreen = session.harness.contexts.create({kind: "offscreen"}); + + session.harness.capabilities.set("runtime.getManifest", false); + session.useContext(offscreen); + session.addCleanup(installGlobals({location: {pathname: "/offscreen.html"}})); + + expect(chrome.runtime.getManifest).toBeUndefined(); + expect(Reflect.get(getTestService(), "__proxy")).toBe(true); + await expect(getTestService().sum(1, 2)).resolves.toBe(3); + expect(session.harness.messaging.forContext(offscreen).runtime.sendMessage.calls).toHaveLength(1); + }); + + test("restores real background errors in an offscreen context", async () => { + const session = getBrowserTest(); + const offscreen = session.harness.contexts.create({kind: "offscreen"}); + + jest.spyOn(console, "error").mockImplementation(() => {}); + + session.harness.capabilities.set("runtime.getManifest", false); + session.useContext(offscreen); + session.addCleanup(installGlobals({location: {pathname: "/offscreen.html"}})); + + await expect(getTestService().fail()).rejects.toThrow("background failed"); + await expect(getTestService().fail()).rejects.toBeInstanceOf(TypeError); + }); + + test.each([ + ["popup", "/popup.html"], + ["sidebar", "/sidebar.html"], + ["content script", "/content-script.html"], + ])("calls background from %s", async (name, pathname) => { + const session = getBrowserTest(); + session.harness.tabs.set([createTabFixture({id: 1, url: "https://example.com"})]); + + const context = session.harness.contexts.create( + name === "content script" + ? {kind: "contentScript", tabId: 1, frameId: 0, url: "https://example.com"} + : {kind: "extensionPage", url: chrome.runtime.getURL(pathname.slice(1))} + ); + + session.useContext(context); + session.addCleanup(installGlobals({location: {pathname}})); + + await expect(getTestService().sum(1, 2)).resolves.toBe(3); + expect(session.harness.messaging.forContext(context).runtime.sendMessage.calls).toHaveLength(1); + }); + + test("creates a proxy when WebExtension APIs are unavailable", () => { + getBrowserTest().addCleanup(installGlobals({chrome: undefined, browser: undefined})); + + expect(() => getTestService()).not.toThrow(); + }); + + test("propagates a transport failure and allows a later call", async () => { + const service = getTestService(); + + sender().runtime.sendMessage.failNext(new Error("Channel closed")); + + await expect(service.sum(1, 2)).rejects.toThrow("Channel closed"); + await expect(service.sum(3, 4)).resolves.toBe(7); + }); +}); diff --git a/src/service/providers/RegisterService.test.ts b/src/service/providers/RegisterService.test.ts new file mode 100644 index 00000000..ad6ba76c --- /dev/null +++ b/src/service/providers/RegisterService.test.ts @@ -0,0 +1,72 @@ +import {createManifestFixture, installGlobals, type BrowserContext} from "@addon-core/browser/testing"; +import {getBrowserTest} from "@tests/browser-harness/session"; +import {RegisterService} from "./index"; +import {mathService, serviceName, type MathService} from "./tests/fixtures/math-service"; + +let background: BrowserContext; +let registration: RegisterService; + +beforeEach(() => { + const session = getBrowserTest(); + + background = session.harness.contexts.create({kind: "background"}); + session.harness.runtime.setManifest( + createManifestFixture({manifest_version: 3, background: {service_worker: "background.js"}}) + ); + session.useContext(background); + session.addCleanup(installGlobals({window: undefined})); + registration = new RegisterService(serviceName, () => mathService); + registration.register(); + session.addCleanup(() => registration.destroy()); +}); + +describe("RegisterService", () => { + test("rejects direct access outside background", () => { + const session = getBrowserTest(); + + session.useContext(session.context); + session.addCleanup(installGlobals({window: {}, location: {pathname: "/popup.html"}})); + + expect(() => registration.get()).toThrow( + `Service "${serviceName}" can be getting only from background context.` + ); + }); + + test("returns the original service in background", () => { + expect(registration.get()).toBe(mathService); + }); + + test("returns the original service in an MV2 generated background page", () => { + const session = getBrowserTest(); + + session.harness.runtime.setManifest( + createManifestFixture({manifest_version: 2, background: {scripts: ["background.js"]}}) + ); + session.addCleanup(installGlobals({window: {}, location: {pathname: "/_generated_background_page.html"}})); + + expect(registration.get()).toBe(mathService); + }); + + test("invokes methods directly without runtime messaging", () => { + expect(registration.get().sum(1, 2)).toBe(3); + expect(getBrowserTest().harness.messaging.calls).toHaveLength(0); + }); + + test("rejects a duplicate service name", () => { + const duplicate = new RegisterService(serviceName, () => mathService); + + expect(() => duplicate.register()).toThrow( + `A instance with name "${serviceName}" already exists. The name must be unique.` + ); + expect(background.onMessage.listenerCount()).toBe(1); + }); + + test("removes its listener on destroy and allows registration again", () => { + expect(background.onMessage.listenerCount()).toBe(1); + expect(getBrowserTest().context.onMessage.listenerCount()).toBe(0); + registration.destroy(); + expect(background.onMessage.listenerCount()).toBe(0); + expect(registration.register()).toBe(mathService); + expect(background.onMessage.listenerCount()).toBe(1); + }); +}); diff --git a/src/service/providers/Service.test.ts b/src/service/providers/Service.test.ts deleted file mode 100644 index ffca1087..00000000 --- a/src/service/providers/Service.test.ts +++ /dev/null @@ -1,169 +0,0 @@ -import {isBackground} from "@addon-core/browser"; - -import MessageManager from "@message/MessageManager"; - -import ProxyService from "./ProxyService"; -import RegisterService from "./RegisterService"; -import ServiceManager from "../ServiceManager"; - -import {RpcAsyncProxy} from "@typing/rpc"; -import {MessageTypeSeparator} from "@typing/message"; - -jest.mock("@addon-core/browser", () => { - const actual = jest.requireActual("@addon-core/browser"); - return { - ...actual, - isBackground: jest.fn(), - }; -}); - -beforeEach(async () => { - jest.clearAllMocks(); - - ServiceManager.getInstance().clear(); - MessageManager.getInstance().clear(); - - new RegisterService(serviceName, () => MatchService).register(); -}); - -const MatchService = { - sum: (a: number, b: number): number => a + b, - asyncSum: (a: number, b: number): Promise => { - return new Promise(resolve => setTimeout(() => resolve(a + b), 100)); - }, - one: 1, - obj: { - concat: (a: string, b: string): string => a + " " + b, - zero: 0, - }, -}; - -type ServiceType = typeof MatchService; -type ServiceProxyType = RpcAsyncProxy; - -const serviceName = "math"; - -describe("ProxyService", () => { - beforeEach(async () => { - (isBackground as jest.Mock).mockReturnValue(false); - }); - - test("throws an error when get() is called in background context", async () => { - (isBackground as jest.Mock).mockReturnValue(true); - - const proxy = new ProxyService(serviceName); - - expect(() => proxy.get()).toThrow( - `You are trying to get proxy service "${serviceName}" from background. You can get original service instead` - ); - }); - - test("returns a proxy when not in background context", () => { - const service = new ProxyService(serviceName).get(); - - //@ts-ignore - expect(service.__proxy).toBe(true); - }); - - test("invokes remote methods using Message.send", async () => { - const service = new ProxyService(serviceName).get(); - - expect(await service.sum(1, 2)).toBe(3); - expect(chrome.runtime.sendMessage).toHaveBeenCalledWith( - expect.objectContaining({ - type: `service${MessageTypeSeparator}${serviceName}`, - data: { - path: "sum", - args: [1, 2], - }, - }), - expect.any(Function) - ); - }); - - test("accesses property on service object ", async () => { - const service = new ProxyService(serviceName).get(); - - expect(await service.one()).toBe(1); - expect(chrome.runtime.sendMessage).toHaveBeenCalledWith( - expect.objectContaining({ - type: `service${MessageTypeSeparator}${serviceName}`, - data: { - path: "one", - args: [], - }, - }), - expect.any(Function) - ); - }); - - test("accesses nested method or property ", async () => { - const service = new ProxyService(serviceName).get(); - - expect(await service.obj.concat("Hello", "world")).toBe("Hello world"); - expect(chrome.runtime.sendMessage).toHaveBeenCalledWith( - expect.objectContaining({ - type: `service${MessageTypeSeparator}${serviceName}`, - data: { - path: "obj.concat", - args: ["Hello", "world"], - }, - }), - expect.any(Function) - ); - - expect(await service.obj.zero()).toBe(0); - expect(chrome.runtime.sendMessage).toHaveBeenCalledWith( - expect.objectContaining({ - type: `service${MessageTypeSeparator}${serviceName}`, - data: { - path: "obj.zero", - args: [], - }, - }), - expect.any(Function) - ); - }); - - test("handles proxied async methods that return promises", async () => { - const service = new ProxyService(serviceName).get(); - - expect(await service.asyncSum(1, 2)).toBe(3); - }); -}); - -describe("RegisterService", () => { - beforeEach(async () => { - (isBackground as jest.Mock).mockReturnValue(true); - }); - - test("throws an error when get() is called outside background context", async () => { - (isBackground as jest.Mock).mockReturnValue(false); - - const proxy = new RegisterService(serviceName, () => MatchService); - - expect(() => proxy.get()).toThrow(`Service "${serviceName}" can be getting only from background context.`); - }); - - test("returns real service when called in background context", () => { - const service = new RegisterService(serviceName, () => MatchService).get(); - - //@ts-ignore - expect(service.__proxy).toBe(undefined); - }); - - test("invokes methods directly without using Message.send in background", async () => { - const service = new RegisterService(serviceName, () => MatchService).get(); - - expect(service.sum(1, 2)).toBe(3); - expect(chrome.runtime.sendMessage).toHaveBeenCalledTimes(0); - }); - - test("throws an error when attempting to register the same service twice", async () => { - const service = new RegisterService(serviceName, () => MatchService); - - expect(() => service.register()).toThrow( - `A instance with name "${serviceName}" already exists. The name must be unique.` - ); - }); -}); diff --git a/src/service/providers/tests/fixtures/math-service.ts b/src/service/providers/tests/fixtures/math-service.ts new file mode 100644 index 00000000..04cd7cc5 --- /dev/null +++ b/src/service/providers/tests/fixtures/math-service.ts @@ -0,0 +1,18 @@ +export const serviceName = "math"; + +export const mathService = { + sum: (a: number, b: number): number => a + b, + asyncSum: (a: number, b: number): Promise => { + return new Promise(resolve => setTimeout(() => resolve(a + b), 100)); + }, + fail: (): never => { + throw new TypeError("background failed"); + }, + one: 1, + obj: { + concat: (a: string, b: string): string => a + " " + b, + zero: 0, + }, +}; + +export type MathService = typeof mathService; diff --git a/src/types/action.ts b/src/types/action.ts new file mode 100644 index 00000000..8f9c3f8b --- /dev/null +++ b/src/types/action.ts @@ -0,0 +1,10 @@ +import type {IconName} from "@typing/icon"; + +/** Default appearance of the extension's toolbar button. */ +export interface ActionOptions { + /** Icon group name. Falls back to `config.icon` when omitted. */ + icon?: IconName; + + /** Tooltip text or locale key, such as `@action.title`. Defaults to the extension name. */ + title?: string; +} diff --git a/src/types/config.ts b/src/types/config.ts index 58b57653..8dd9c958 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -8,6 +8,7 @@ import {Plugin} from "@typing/plugin"; import {Language} from "@typing/locale"; import {Awaiter} from "@typing/helpers"; import {EnvFilterOptions, EnvFilterVariant} from "@typing/env"; +import {ActionOptions} from "@typing/action"; /** * Configuration options for building a browser extension. This interface defines @@ -151,6 +152,19 @@ export interface Config { */ icon: string; + /** + * Default appearance of the toolbar button (`action` in MV3, `browser_action` in MV2). + * An explicit object, including `{}`, declares the button without requiring a popup or command. + * It does not register a click handler. + * + * The applied popup overrides each explicitly specified icon/title independently. + * Missing values fall back to these settings, then to `icon` and the extension name. + * When omitted, only an applied popup or execute-action command declares the button. + * + * @example {icon: "active", title: "@action.title"} + */ + action?: ActionOptions; + /** * Browser-specific settings (populate manifest.browser_specific_settings). * @@ -449,7 +463,10 @@ export interface Config { /** * Flag indicating whether to merge styles from App and Shared directories. - * When `true`, styles from both directories will be combined. + * When `true`, importing a shared stylesheet also compiles the app stylesheet at the same + * relative path into the same CSS module, with shared rules before app rules. Relative `@use` + * and `@forward` requests and literal `url(...)` values in the merged sources resolve from the + * file that declares them. * * @default true */ diff --git a/src/types/content/definition.ts b/src/types/content/definition.ts index f1b32be8..f143ad0a 100644 --- a/src/types/content/definition.ts +++ b/src/types/content/definition.ts @@ -4,6 +4,7 @@ import type {ContentScriptBoundaryHandler} from "./boundary"; import type { ContentScriptAnchor, ContentScriptAnchorGetter, + ContentScriptBuilder, ContentScriptAppend, ContentScriptContainerCreator, ContentScriptContainerFactory, @@ -97,6 +98,11 @@ export type ContentScriptDefinition< Isolation extends `${ContentScriptIsolation}` = `${ContentScriptIsolation}`, > = ContentScriptDefinitionBase & ContentScriptRenderDefinition; +export type ContentScriptBuilderConstructor< + Data = unknown, + Isolation extends `${ContentScriptIsolation}` = `${ContentScriptIsolation}`, +> = new (definition: ContentScriptDefinition>) => ContentScriptBuilder; + export interface ContentScriptResolvedDefinition extends Omit< ContentScriptDefinitionBase, "anchor" | "marker" | "mount" | "container" | "watch" diff --git a/src/types/manifest.ts b/src/types/manifest.ts index bcbb1a66..1913a1ee 100644 --- a/src/types/manifest.ts +++ b/src/types/manifest.ts @@ -5,6 +5,7 @@ import {Language} from "@typing/locale"; import {BrowserSpecific, DataCollectionPermission} from "@typing/browser"; import {CspConfig} from "@typing/csp"; import {SandboxCspConfig} from "@typing/sandbox"; +import {ActionOptions} from "@typing/action"; export const ManifestMatchSchemes: ReadonlySet = new Set(["http", "https", "file", "ftp", "ws", "wss"]); @@ -33,6 +34,8 @@ export type ManifestPermission = chrome.runtime.ManifestPermission; export type ManifestOptionalPermission = chrome.runtime.ManifestOptionalPermission; +export type ManifestAction = NonNullable; + type ManifestFixed = Omit & { manifest_version: ManifestVersion; }; @@ -120,6 +123,9 @@ export interface ManifestBuilder { setIcon(icon?: string): this; // name of an icon set for manifest.icons + // Toolbar button + setAction(action?: ActionOptions): this; + // Entry setBackground(background?: ManifestBackground): this; diff --git a/src/types/popup.ts b/src/types/popup.ts index 1d11b310..22d44fc3 100644 --- a/src/types/popup.ts +++ b/src/types/popup.ts @@ -2,6 +2,7 @@ import {ViewDefinition, ViewOptions} from "@typing/view"; import {CspOptions} from "@typing/csp"; import {PermissionsOptions} from "@typing/permissions"; import type {ManifestPopup} from "@typing/manifest"; +import type {IconName} from "@typing/icon"; /** * Empty because popup aliases depend on the consuming application's entrypoints. @@ -14,7 +15,7 @@ export type PopupAlias = keyof PopupAliasRegistry extends never ? string : Extra export type PopupMap = Map; export interface PopupConfig { - icon?: string; + icon?: IconName; apply?: boolean; } diff --git a/src/types/sidebar.ts b/src/types/sidebar.ts index 2a7bf5e4..73b76995 100644 --- a/src/types/sidebar.ts +++ b/src/types/sidebar.ts @@ -3,6 +3,7 @@ import {Browser} from "@typing/browser"; import {CspOptions} from "@typing/csp"; import {PermissionsOptions} from "@typing/permissions"; import type {ManifestSidebar} from "@typing/manifest"; +import type {IconName} from "@typing/icon"; export const SidebarAlternativeBrowsers: ReadonlySet = new Set([Browser.Opera, Browser.Firefox]); @@ -19,7 +20,7 @@ export type SidebarAlias = keyof SidebarAliasRegistry extends never export type SidebarMap = Map; export interface SidebarConfig { - icon?: string; + icon?: IconName; apply?: boolean; } diff --git a/tests/BrowserStartupReporter.cjs b/tests/BrowserStartupReporter.cjs new file mode 100644 index 00000000..57cf03f9 --- /dev/null +++ b/tests/BrowserStartupReporter.cjs @@ -0,0 +1,66 @@ +const {mkdir, writeFile} = require("node:fs/promises"); +const path = require("node:path"); + +/** Collect structured session diagnostics from Jest workers, including failed test files. */ +class BrowserStartupReporter { + constructor(globalConfig, options = {}) { + this.outputFile = path.resolve( + globalConfig.rootDir, + options.outputFile ?? process.env.ADNBN_BROWSER_STARTUP_REPORT ?? ".cache/integration/browser-startup.json" + ); + this.samples = []; + } + + onTestResult(test, result) { + const prefix = "[browser-startup] "; + + for (const entry of result.console ?? []) { + if (entry.message.startsWith(prefix)) { + this.samples.push({testFile: test.path, ...JSON.parse(entry.message.slice(prefix.length))}); + } + } + } + + async onRunComplete() { + const summary = {}; + + for (const browser of ["chrome", "firefox"]) { + const samples = this.samples.filter(sample => sample.browser === browser); + const durations = samples.filter(sample => sample.status === "ready").map(sample => sample.durationMs); + + durations.sort((a, b) => a - b); + + const middle = Math.floor(durations.length / 2); + summary[browser] = { + attempts: samples.length, + failures: samples.filter(sample => sample.status === "failed").length, + medianMs: durations.length + ? durations.length % 2 + ? durations[middle] + : (durations[middle - 1] + durations[middle]) / 2 + : null, + p95Ms: durations[Math.ceil(durations.length * 0.95) - 1] ?? null, + maxMs: durations.at(-1) ?? null, + }; + } + + await mkdir(path.dirname(this.outputFile), {recursive: true}); + await writeFile( + this.outputFile, + JSON.stringify( + { + generatedAt: new Date().toISOString(), + node: process.version, + platform: process.platform, + arch: process.arch, + summary, + samples: this.samples, + }, + null, + 2 + ) + "\n" + ); + } +} + +module.exports = BrowserStartupReporter; diff --git a/tests/BrowserStartupReporter.test.ts b/tests/BrowserStartupReporter.test.ts new file mode 100644 index 00000000..0def5269 --- /dev/null +++ b/tests/BrowserStartupReporter.test.ts @@ -0,0 +1,63 @@ +import {mkdtemp, readFile, rm} from "fs/promises"; +import os from "os"; +import path from "path"; + +import BrowserStartupReporter from "./BrowserStartupReporter.cjs"; + +test("the startup report retains successes and failures across files and excludes failures from duration percentiles", async () => { + const rootDir = await mkdtemp(path.join(os.tmpdir(), "adnbn-startup-report-")); + const outputFile = path.join(rootDir, "report.json"); + + try { + const reporter = new BrowserStartupReporter({rootDir}, {outputFile}); + + for (const [index, durationMs] of [100, 300, 200, 400].entries()) { + reporter.onTestResult( + {path: path.join(rootDir, `${index}.test.ts`)}, + { + console: [ + { + message: + "[browser-startup] " + + JSON.stringify({ + browser: "chrome", + testName: "starts", + status: "ready", + durationMs, + timeoutMs: 30_000, + }), + }, + ], + } + ); + } + + reporter.onTestResult( + {path: path.join(rootDir, "failed.test.ts")}, + { + console: [ + { + message: + "[browser-startup] " + + JSON.stringify({ + browser: "firefox", + status: "failed", + durationMs: 30_000, + error: "startup deadline", + }), + }, + ], + } + ); + await reporter.onRunComplete(); + const report = JSON.parse(await readFile(outputFile, "utf8")); + + expect(report.samples).toHaveLength(5); + expect(new Set(report.samples.map(sample => sample.testFile)).size).toBe(5); + expect(report.summary.chrome).toEqual({attempts: 4, failures: 0, medianMs: 250, p95Ms: 400, maxMs: 400}); + expect(report.summary.firefox).toEqual({attempts: 1, failures: 1, medianMs: null, p95Ms: null, maxMs: null}); + expect(report.samples[4].error).toBe("startup deadline"); + } finally { + await rm(rootDir, {recursive: true, force: true}); + } +}); diff --git a/tests/README.md b/tests/README.md index d42fc000..758798b3 100644 --- a/tests/README.md +++ b/tests/README.md @@ -6,23 +6,54 @@ Use the npm test commands below, or `npm run test:run --` to pass arguments dire The observed Node 24 crashes contain `Builtins_BaselineOutOfLinePrologue` and `ClearStaleLeftTrimmedPointerVisitor`, matching the Sparkplug report in [nodejs/node#62393](https://github.com/nodejs/node/issues/62393). The earlier `--no-maglev` workaround did not prevent the crash. Successful repetitions measure observed stability; they do not prove a rare native fault impossible. Keep the workaround scoped to the reproduced environment and reassess it when the upstream fix is verified. Jest workers inherit the flag through `execArgv`; CLI fixture child processes and real browsers keep their normal settings. Tests are never automatically retried or skipped to hide a native crash. -| Command | Checks | Builds the package | -| ------------------------ | --------------------------------------------------------------------- | ------------------ | -| `npm run test:unit` | Node and DOM unit tests | No | -| `npm run test:build` | Compiler plugins, the built CLI and build integrations | Once | -| `npm run test:types` | All fixture applications and declaration consumer tests | Once | -| `npm run test:chrome` | Real Chrome scenarios | Once | -| `npm run test:firefox` | Real Firefox MV2/MV3 scenarios | Once | -| `npm test` | Every Jest project, including both browsers | Once | -| `npm run test:pre-push` | Framework typecheck, all non-browser tests and all fixture typechecks | Once | -| `npm run test:inventory` | Every test file belongs to exactly one project | No | - -`npm run typecheck` checks framework and test-runner types. `npm run typecheck:integration` builds the package, prepares every fixture application and checks it with its own `tsconfig.json`. Declaration consumers without an application config belong to the Jest `types` project. +| Command | Checks | Builds the package | +| ------------------------------ | -------------------------------------------------------------- | ------------------ | +| `npm test` | Every Jest project, including both browsers | Once | +| `npm run test:unit` | Node and DOM unit tests | No | +| `npm run test:watch` | Node and DOM unit tests in watch mode | No | +| `npm run test:build` | Compiler plugins, the built CLI and build integrations | Once | +| `npm run test:types` | Jest declaration consumer tests | Once | +| `npm run test:browser` | Chrome and Firefox scenarios | Once | +| `npm run test:browser:chrome` | Chrome scenarios | Once | +| `npm run test:browser:firefox` | Firefox scenarios | Once | +| `npm run test:inventory` | Test ownership, local mocks and framework reset inventory | No | +| `npm run test:run -- …` | Direct Jest arguments; caller selects groups and prepares dist | No | + +`npm run typecheck` checks framework and test-runner types. `npm run fixtures:prepare` builds the package and prepares +fixture applications for editor/manual use. `npm run typecheck:fixtures` builds the package, prepares every fixture +application and checks it with its own `tsconfig.json`. Declaration consumers without an application config belong to +the Jest `types` project and run through `test:types`; they are separate from fixture application typechecks. + +`npm run check` runs framework typechecking, one package build, all non-browser Jest projects and fixture preparation/typechecks. +Husky pre-push uses this same command. It does not launch browsers or collect coverage. + +Select a feature by path instead of adding a script for each module: + +```sh +npm run test:unit -- --testPathPatterns=src/locale +npm run test:unit -- --testPathPatterns=src/relay +npm run test:unit -- --runTestsByPath src/sandbox/SandboxMessage.test.ts +npm run test:watch -- --testPathPatterns=src/message +npm run test:unit -- --coverage +``` + +The selected group remains the boundary: `test:unit` never includes build or browser tests even when their paths match. +Commands that launch Jest forward arguments after `--` to Jest. Fixture preparation, fixture typechecking and `check` are separate +workflows, not Jest argument forwarders. Public build-dependent test commands always build first. CI and `check` reuse +one explicit build and invoke `test:run` (or its underlying wrapper) directly, avoiding nested build commands. + +The [2026-09-25 benchmark](benchmarks/2026-09-25.md) records final migration timings, raw runs and comparison limits. +The [Relay type-checking follow-up](benchmarks/2026-09-25-relay-types.md) records the subsequent constructor inference fix. +The [override scheduling follow-up](benchmarks/2026-09-25-override.md) compares the unchanged build matrix at eight and two workers. ## Groups and workers `jest.config.ts` defines six projects: `unit-node`, `unit-dom`, `build`, `types`, `chrome` and `firefox`. Put DOM tests in the `unit-dom` patterns. Tests that execute the built CLI, consume package declarations or compile imports from the built package belong to `build` or `types`. Keep unit tests runnable with no `dist` directory. Each browser file contains scenarios for one browser only. +Relay declaration tests keep the source/package × POSIX/Windows matrix for the generated registry, selectors and exports. +Constructor, prepared-data and bootstrap contracts live in `integration/types/relay.integration.test.ts`, checked against +both source aliases and the built package. Only its source case also checks the generated virtual entry module. + After a package build, direct Jest invocations can reuse that build: ```sh @@ -33,17 +64,108 @@ npm run test:run -- --selectProjects chrome --maxWorkers=4 Jest uses up to eight workers, leaving one CPU available on smaller machines. Override the pool with `-- --maxWorkers=N`, or use `-- --runInBand` for a serial diagnostic run. The limit applies to the whole Jest invocation; do not pass both flags together. CI sets explicit smaller pools for compiler and browser jobs. -The trailing `--verbose=false` in `test:unit` terminates Jest's variadic `--selectProjects` option so an appended test filename is treated as a filename, not another project name. +The trailing `--verbose=false` in group commands terminates Jest's variadic `--selectProjects` option so an appended test filename is treated as a filename, not another project name. `test:watch` terminates it with `--watch`. Preparation and fixture typechecks use a bounded queue of up to four processes. Set `ADNBN_TEST_WORKERS` to a positive integer to override it. Each application keeps its own configuration and TypeScript process. Queued builds and typechecks allow up to 120 seconds per command. Builds inside Jest scenarios retain the 30-second limit so a hung CLI does not outlive its enclosing test deadline. A failure is reported after all running work has finished, with the child process diagnostics preserved. See [integration/README.md](integration/README.md) for browser requirements and fixture layout. Browser tests use separate profiles, ports and application copies. Tests that edit or watch a fixture must retain their own copy and build; generated outputs must not be shared between mutable scenarios. +## Browser harness + +`tests/jest.setup.ts` installs the browser harness for every unit, build and types test. Real Chrome/Firefox +integrations load no setup. Each test gets real Browser/Storage modules unless it declares an inventoried local mock. +There is no global module mock or per-file setup selection. + +`browser-harness/local-mocks.json` records local `jest.mock`, `jest.doMock`, `jest.setMock` and `jest.unstable_mockModule` +calls for `@addon-core/*` and `@main/env`. The list is currently empty. Any future package mock requires an explicit inventory entry and review. +Passing under harness setup does not prove that a locally mocked dependency was exercised. Review any future exception +against the available Browser/Storage controls and document the dependency boundary it represents. +`npm run test:inventory` rejects missing files and unrecorded or removed module mocks. +Update the inventory alongside test changes so stale exceptions cannot remain unnoticed. + +The harness setup creates a fresh `BrowserTestSession` before every test. Use `getBrowserTest()` from +`@tests/browser-harness/session` inside tests and hooks, not at module scope. The `@tests/*` alias maps to the root `tests/` +directory in TypeScript and Jest. Browser and Storage imports are real unless the file declares a recorded local mock; +build-time virtual modules can still supply explicit test fixtures. `NativeLocale.test.ts` demonstrates API controls +and call history instead of function mocks. + +Locale tests configure the raw i18n API and exercise the real Storage package over harness storage, including namespaced +keys, external changes and failures. `MemoryLocaleStorage` remains a custom-driver fixture for controlled pending +writes and driver-sharing contracts. Build-generated `#adnbn/locale`, `#adnbn/page` and `#adnbn/runtime` retain explicit +fixtures; they are not Browser/Storage replacements. Content node tests inject the styles runtime through that generated +facade and use the real Browser URL helper. + +```ts +import {getBrowserTest} from "@tests/browser-harness/session"; + +const session = getBrowserTest(); +session.harness.configurable.chrome.i18n.getMessage.setResult("en"); +session.addCleanup(unsubscribe); +``` + +The default messaging context is an extension page with the Chrome profile. The session sets `BROWSER=chrome`, +`APP=test` and `MANIFEST_VERSION` from its manifest. Constructor options `profile`, `app` and `manifest` select alternatives. +Teardown restores the previous environment values, including previously absent variables. Framework environment +getters read these values when called. + +Create other contexts through `session.harness.contexts.create()` and install them with `session.useContext(context)`. +Use `session.useContext(context, "firefox")` to switch both browser globals and `BROWSER`; configure the reported browser +version through `harness.runtime.getBrowserInfo.setResult()`. The returned restore function and nested installations +follow reverse order; teardown restores remaining installations automatically. Use context-bound facades from +`harness.messaging.forContext()` for the other endpoints of a conversation. Global context changes are not async-local: +do not use `test.concurrent` or assume that global Browser wrappers remember the context of an earlier callback. +`environment: "preserve"` retains jsdom's window, document, location and navigator; it does not simulate a background DOM. + +`session.createScriptRuntime({clock: true})` binds a persistent guest runtime to the session's document registry and +registers disposal. Create the target tabs/documents first. Advance the guest clock explicitly; host timers and +consumer deadlines remain separate. Runtime and tab message delivery continue under Jest host fake timers without +advancing them. `harness.delays.downloadValidation` uses a host timer: advance it explicitly in tests using fake timers. +Shared teardown restores real host timers even when cleanup fails. Guest-clock checks use real timers inside the guest; +Jest fake timers affect only the host. + +Register consumer subscriptions, observers and other resources with `session.addCleanup()`. Async cleanup is awaited +in reverse order, and a failure does not prevent remaining cleanup, harness reset or global/environment restoration. +Teardown clears Message/Relay/Offscreen/Service/Sandbox managers and Relay permission registration. It resets +NativeLocale, Message, ObservableLocale (default, memory and driver caches), and OffscreenBridge singleton references, +and disposes cached SandboxMessage hosts. Clearing a cache does not dispose every previously returned provider or DOM +bridge: subscriptions, pending DOM work and manually created instances still need explicit cleanup callbacks. +Do not reset the harness underneath a live session; that invalidates contexts and detaches the scripting executor. +Jest mock history and spies are reset centrally. + +`framework-state-inventory.json` records source classes with static fields or `getInstance`, including retained constants +and the CLI TypeScript resolver cache. `test:inventory` compares the source fields with that inventory, checks registration +of the 11 runtime reset owners, and checks all six exported `*GlobalKey` declarations against the reset implementation. +New fields or owners require a reviewed reset policy. After runtime cleanup, any remaining own global property starting +with `adnbn` fails teardown rather than being silently deleted. This source inventory detects missing policies; it does +not prove that every reset implementation is correct, so session lifecycle tests verify the actual behavior too. + +### Remaining test-kit boundaries + +- `hasListeners` is not modeled. Check subscriptions through the context event's `listenerCount()`. +- Offscreen has no post-creation hook. `ProxyOffscreen.test.ts` temporarily intercepts `contexts.create` with a + passthrough spy to register the real transport before the first message is sent. Replace this interception when + the kit provides a hook such as `offscreen.onCreated(context)` that completes before `createDocument` resolves. + `beforeCreate` cannot serve this purpose because the receiving context does not exist yet. +- VM scenarios require uninstrumented injected code. Only `src/relay/adapters/invoke-relay.ts` is excluded from Babel + coverage in every Jest project. Its host adapter remains instrumented; VM tests run the real bundled Relay manager, + and Chrome MV3 plus Firefox MV2/MV3 integrations check the injected function in actual extensions. +- Native Offscreen unit tests attach the real `RegisterOffscreen` transport when the harness creates its context and + dispose it with that context. The kit owns creation, closure and messaging; it does not execute the extension HTML. + `tests/offscreen/MockLockManager.ts` models the Web Locks boundary for scheduling tests. Firefox/MV2 bridge tests use + jsdom frames and explicit ready/error events; real-browser integrations remain the lifecycle check. +- Sandbox messaging uses `postMessage`, not WebExtension runtime messaging. `SandboxMessage.test.ts` uses the real + `SandboxMemory` port pair and real request IDs, with channels registered for session cleanup. Fake-timer tests cover + request deadlines, disposal and responses arriving out of order; DOM/provider and real-browser tests cover frame wiring. +- The session does not emulate Web Locks, DOM inside the guest, or extension HTML execution. Keep explicit external + adapters where needed and real-browser checks for DOM, execution worlds, browser lifecycle and vendor behavior. +- Consult the kit's `RAW_CAPABILITY_COVERAGE` before using another browser API. Extend the kit when a required capability + is missing; an existing modeled capability needs no new global mock. + ## Hooks Pre-commit checks only formatting of the **staged contents**, without changing the index or unstaged work. Format reported files and stage them before retrying. Tests, typechecking and builds run at pre-push and in CI. During development, run `test:unit` or `test:watch` explicitly for earlier feedback. Tests execute the working tree, so CI remains the full validation of the committed revision. -Pre-push runs `test:pre-push`, without browser launches or coverage. It includes both framework and fixture typechecks, compiler and CLI checks, and declaration consumers. Framework typechecking runs alongside the package build; after the build, Jest runs alongside the fixture queue. Jest inherits the terminal and streams its output live; fixture diagnostics remain buffered per command. Each fixture is typechecked as soon as its own build finishes. All tasks finish before failures are reported. Each task has a five-minute deadline; timeout or interruption terminates its complete process tree, including wrappers, Jest workers and child CLI processes. This uses a separate process group on macOS/Linux and `taskkill /T /F` on Windows. Browser checks run in CI and are also available through the explicit commands above. +Pre-push runs `npm run check`, without browser launches or coverage. It includes both framework and fixture typechecks, compiler and CLI checks, and declaration consumers. Framework typechecking runs alongside the package build; after the build, Jest runs alongside the fixture queue. Jest inherits the terminal and streams its output live; fixture diagnostics remain buffered per command. Each fixture is typechecked as soon as its own build finishes. All tasks finish before failures are reported. Each task has a five-minute deadline; timeout or interruption terminates its complete process tree, including wrappers, Jest workers and child CLI processes. This uses a separate process group on macOS/Linux and `taskkill /T /F` on Windows. Browser checks run in CI and are also available through the explicit commands above. ## CI and coverage diff --git a/tests/benchmarks/2026-09-25-override.json b/tests/benchmarks/2026-09-25-override.json new file mode 100644 index 00000000..892e9c6f --- /dev/null +++ b/tests/benchmarks/2026-09-25-override.json @@ -0,0 +1,2561 @@ +{ + "baselineRef": "5fe410b9", + "environment": { + "node": "24.5.0", + "platform": "macOS arm64", + "logicalCpus": 16, + "jestFlag": "--no-sparkplug" + }, + "overrideScenarios": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "summary": { + "before-8": { + "median": 22.506157167023048, + "min": 22.25215541711077, + "max": 22.71305362507701 + }, + "before-2": { + "median": 52.397480624960735, + "min": 51.31795075000264, + "max": 53.63272133283317 + }, + "after-8": { + "median": 16.5209129168652, + "min": 16.205771875102073, + "max": 17.61266070790589 + }, + "after-2": { + "median": 53.16435362491757, + "min": 52.022176374914125, + "max": 54.29423633287661 + } + }, + "runs": [ + { + "phase": "before", + "workers": 8, + "warmup": true, + "run": 0, + "wallSeconds": 26.11322008399293, + "exitCode": 0, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "build", + "types", + "--maxWorkers=8", + "--json", + "--outputFile=/tmp/override-benchmark/before-8-0.json" + ], + "suites": 37, + "tests": 285, + "files": [ + { + "path": "tests/integration/types/relay.integration.test.ts", + "seconds": 6.242 + }, + { + "path": "src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts", + "seconds": 6.298 + }, + { + "path": "tests/integration/types/content.integration.test.ts", + "seconds": 6.407 + }, + { + "path": "tests/integration/build/relay-runtime/relay-runtime.integration.test.ts", + "seconds": 9.189 + }, + { + "path": "src/cli/plugins/content/RelayDeclaration.test.ts", + "seconds": 4.256 + }, + { + "path": "tests/integration/build/options/options.integration.test.ts", + "seconds": 10.802 + }, + { + "path": "tests/integration/build/locale/dynamic.integration.test.ts", + "seconds": 10.976 + }, + { + "path": "tests/integration/build/isolation/isolation-watch.integration.test.ts", + "seconds": 12.289 + }, + { + "path": "tests/integration/build/style-routing/delivery-spike.integration.test.ts", + "seconds": 2.077 + }, + { + "path": "tests/integration/build/locale/chunks.integration.test.ts", + "seconds": 7.656 + }, + { + "path": "tests/integration/types/locale.integration.test.ts", + "seconds": 1.907 + }, + { + "path": "tests/integration/build/locale/locale.integration.test.ts", + "seconds": 8.181 + }, + { + "path": "src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts", + "seconds": 3.697 + }, + { + "path": "tests/integration/build/style-routing/style-routing.integration.test.ts", + "seconds": 1.422 + }, + { + "path": "tests/integration/build/locale/chunks-threshold.integration.test.ts", + "seconds": 6.72 + }, + { + "path": "tests/integration/build/virtual-modules/virtual-modules.integration.test.ts", + "seconds": 1.434 + }, + { + "path": "src/cli/index.test.ts", + "seconds": 3.027 + }, + { + "path": "tests/integration/types/registries.integration.test.ts", + "seconds": 5.985 + }, + { + "path": "src/cli/virtual/virtual.test.ts", + "seconds": 0.598 + }, + { + "path": "tests/integration/build/locale/chunks-disabled.integration.test.ts", + "seconds": 0.879 + }, + { + "path": "tests/integration/build/style-routing/selective-split.integration.test.ts", + "seconds": 0.978 + }, + { + "path": "tests/integration/build/permissions/permissions.integration.test.ts", + "seconds": 2.967 + }, + { + "path": "src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts", + "seconds": 0.264 + }, + { + "path": "src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts", + "seconds": 0.137 + }, + { + "path": "src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts", + "seconds": 0.154 + }, + { + "path": "src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts", + "seconds": 0.047 + }, + { + "path": "src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts", + "seconds": 0.127 + }, + { + "path": "src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts", + "seconds": 1.255 + }, + { + "path": "tests/build-output.test.ts", + "seconds": 0.055 + }, + { + "path": "src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts", + "seconds": 0.055 + }, + { + "path": "src/cli/bundler/plugins/resource-access/utils.test.ts", + "seconds": 0.042 + }, + { + "path": "tests/integration/build/content/content-api.integration.test.ts", + "seconds": 0.067 + }, + { + "path": "src/cli/bundler/plugins/utils/runtime-template.test.ts", + "seconds": 0.036 + }, + { + "path": "tests/integration/build/style-routing/content-chunks.integration.test.ts", + "seconds": 0.363 + }, + { + "path": "tests/integration/build/style-routing/routing-spike.integration.test.ts", + "seconds": 0.551 + }, + { + "path": "tests/integration/types/view.integration.test.ts", + "seconds": 3.177 + }, + { + "path": "tests/integration/build/override/override.integration.test.ts", + "seconds": 25.153 + } + ] + }, + { + "phase": "before", + "workers": 8, + "warmup": false, + "run": 1, + "wallSeconds": 22.71305362507701, + "exitCode": 0, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "build", + "types", + "--maxWorkers=8", + "--json", + "--outputFile=/tmp/override-benchmark/before-8-1.json" + ], + "suites": 37, + "tests": 285, + "files": [ + { + "path": "tests/integration/build/relay-runtime/relay-runtime.integration.test.ts", + "seconds": 5.729 + }, + { + "path": "tests/integration/build/locale/chunks-threshold.integration.test.ts", + "seconds": 6.236 + }, + { + "path": "tests/integration/build/locale/chunks.integration.test.ts", + "seconds": 6.322 + }, + { + "path": "tests/integration/build/locale/locale.integration.test.ts", + "seconds": 7.026 + }, + { + "path": "tests/integration/build/options/options.integration.test.ts", + "seconds": 7.16 + }, + { + "path": "tests/integration/build/locale/dynamic.integration.test.ts", + "seconds": 8.03 + }, + { + "path": "tests/integration/build/isolation/isolation-watch.integration.test.ts", + "seconds": 9.55 + }, + { + "path": "src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts", + "seconds": 3.415 + }, + { + "path": "tests/integration/types/content.integration.test.ts", + "seconds": 4.935 + }, + { + "path": "src/cli/plugins/content/RelayDeclaration.test.ts", + "seconds": 3.82 + }, + { + "path": "tests/integration/types/relay.integration.test.ts", + "seconds": 4.782 + }, + { + "path": "src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts", + "seconds": 4.072 + }, + { + "path": "tests/integration/types/locale.integration.test.ts", + "seconds": 1.863 + }, + { + "path": "src/cli/index.test.ts", + "seconds": 3.387 + }, + { + "path": "tests/integration/build/style-routing/delivery-spike.integration.test.ts", + "seconds": 2.058 + }, + { + "path": "tests/integration/types/view.integration.test.ts", + "seconds": 3.669 + }, + { + "path": "tests/integration/types/registries.integration.test.ts", + "seconds": 6.551 + }, + { + "path": "tests/integration/build/virtual-modules/virtual-modules.integration.test.ts", + "seconds": 1.537 + }, + { + "path": "tests/integration/build/style-routing/selective-split.integration.test.ts", + "seconds": 0.779 + }, + { + "path": "tests/integration/build/permissions/permissions.integration.test.ts", + "seconds": 3.318 + }, + { + "path": "tests/integration/build/locale/chunks-disabled.integration.test.ts", + "seconds": 0.871 + }, + { + "path": "tests/integration/build/style-routing/content-chunks.integration.test.ts", + "seconds": 0.262 + }, + { + "path": "src/cli/virtual/virtual.test.ts", + "seconds": 0.645 + }, + { + "path": "src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts", + "seconds": 0.138 + }, + { + "path": "src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts", + "seconds": 0.148 + }, + { + "path": "tests/integration/build/style-routing/routing-spike.integration.test.ts", + "seconds": 0.612 + }, + { + "path": "src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts", + "seconds": 0.043 + }, + { + "path": "tests/integration/build/content/content-api.integration.test.ts", + "seconds": 0.061 + }, + { + "path": "tests/build-output.test.ts", + "seconds": 0.053 + }, + { + "path": "src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts", + "seconds": 0.323 + }, + { + "path": "src/cli/bundler/plugins/resource-access/utils.test.ts", + "seconds": 0.038 + }, + { + "path": "src/cli/bundler/plugins/utils/runtime-template.test.ts", + "seconds": 0.032 + }, + { + "path": "src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts", + "seconds": 0.054 + }, + { + "path": "tests/integration/build/style-routing/style-routing.integration.test.ts", + "seconds": 1.423 + }, + { + "path": "src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts", + "seconds": 0.164 + }, + { + "path": "src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts", + "seconds": 1.512 + }, + { + "path": "tests/integration/build/override/override.integration.test.ts", + "seconds": 21.935 + } + ] + }, + { + "phase": "before", + "workers": 8, + "warmup": false, + "run": 2, + "wallSeconds": 22.506157167023048, + "exitCode": 0, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "build", + "types", + "--maxWorkers=8", + "--json", + "--outputFile=/tmp/override-benchmark/before-8-2.json" + ], + "suites": 37, + "tests": 285, + "files": [ + { + "path": "tests/integration/build/locale/chunks-threshold.integration.test.ts", + "seconds": 5.786 + }, + { + "path": "tests/integration/build/locale/chunks.integration.test.ts", + "seconds": 5.88 + }, + { + "path": "tests/integration/types/registries.integration.test.ts", + "seconds": 6.023 + }, + { + "path": "tests/integration/build/locale/locale.integration.test.ts", + "seconds": 6.694 + }, + { + "path": "tests/integration/build/options/options.integration.test.ts", + "seconds": 6.77 + }, + { + "path": "tests/integration/build/locale/dynamic.integration.test.ts", + "seconds": 7.552 + }, + { + "path": "tests/integration/build/isolation/isolation-watch.integration.test.ts", + "seconds": 10.04 + }, + { + "path": "src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts", + "seconds": 4.283 + }, + { + "path": "tests/integration/types/relay.integration.test.ts", + "seconds": 5.228 + }, + { + "path": "src/cli/plugins/content/RelayDeclaration.test.ts", + "seconds": 4.504 + }, + { + "path": "tests/integration/types/content.integration.test.ts", + "seconds": 5.584 + }, + { + "path": "tests/integration/types/view.integration.test.ts", + "seconds": 4.173 + }, + { + "path": "tests/integration/build/relay-runtime/relay-runtime.integration.test.ts", + "seconds": 6.535 + }, + { + "path": "tests/integration/types/locale.integration.test.ts", + "seconds": 1.71 + }, + { + "path": "tests/integration/build/style-routing/delivery-spike.integration.test.ts", + "seconds": 1.908 + }, + { + "path": "tests/integration/build/virtual-modules/virtual-modules.integration.test.ts", + "seconds": 1.47 + }, + { + "path": "src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts", + "seconds": 3.16 + }, + { + "path": "src/cli/virtual/virtual.test.ts", + "seconds": 0.587 + }, + { + "path": "src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts", + "seconds": 1.506 + }, + { + "path": "tests/integration/build/locale/chunks-disabled.integration.test.ts", + "seconds": 0.846 + }, + { + "path": "src/cli/index.test.ts", + "seconds": 3.106 + }, + { + "path": "src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts", + "seconds": 0.304 + }, + { + "path": "tests/integration/build/style-routing/selective-split.integration.test.ts", + "seconds": 0.943 + }, + { + "path": "tests/integration/build/style-routing/content-chunks.integration.test.ts", + "seconds": 0.217 + }, + { + "path": "src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts", + "seconds": 0.122 + }, + { + "path": "src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts", + "seconds": 0.165 + }, + { + "path": "src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts", + "seconds": 0.132 + }, + { + "path": "tests/integration/build/content/content-api.integration.test.ts", + "seconds": 0.063 + }, + { + "path": "src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts", + "seconds": 0.055 + }, + { + "path": "src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts", + "seconds": 0.059 + }, + { + "path": "tests/build-output.test.ts", + "seconds": 0.059 + }, + { + "path": "src/cli/bundler/plugins/utils/runtime-template.test.ts", + "seconds": 0.033 + }, + { + "path": "src/cli/bundler/plugins/resource-access/utils.test.ts", + "seconds": 0.045 + }, + { + "path": "tests/integration/build/permissions/permissions.integration.test.ts", + "seconds": 3.124 + }, + { + "path": "tests/integration/build/style-routing/routing-spike.integration.test.ts", + "seconds": 0.583 + }, + { + "path": "tests/integration/build/style-routing/style-routing.integration.test.ts", + "seconds": 1.341 + }, + { + "path": "tests/integration/build/override/override.integration.test.ts", + "seconds": 21.911 + } + ] + }, + { + "phase": "before", + "workers": 8, + "warmup": false, + "run": 3, + "wallSeconds": 22.25215541711077, + "exitCode": 0, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "build", + "types", + "--maxWorkers=8", + "--json", + "--outputFile=/tmp/override-benchmark/before-8-3.json" + ], + "suites": 37, + "tests": 285, + "files": [ + { + "path": "tests/integration/build/relay-runtime/relay-runtime.integration.test.ts", + "seconds": 5.848 + }, + { + "path": "tests/integration/types/registries.integration.test.ts", + "seconds": 6.264 + }, + { + "path": "tests/integration/build/locale/chunks.integration.test.ts", + "seconds": 6.434 + }, + { + "path": "tests/integration/build/locale/locale.integration.test.ts", + "seconds": 7.244 + }, + { + "path": "tests/integration/build/options/options.integration.test.ts", + "seconds": 7.365 + }, + { + "path": "tests/integration/build/locale/dynamic.integration.test.ts", + "seconds": 7.99 + }, + { + "path": "tests/integration/build/isolation/isolation-watch.integration.test.ts", + "seconds": 9.841 + }, + { + "path": "src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts", + "seconds": 3.521 + }, + { + "path": "tests/integration/types/content.integration.test.ts", + "seconds": 4.688 + }, + { + "path": "src/cli/plugins/content/RelayDeclaration.test.ts", + "seconds": 3.76 + }, + { + "path": "tests/integration/types/relay.integration.test.ts", + "seconds": 4.684 + }, + { + "path": "tests/integration/types/view.integration.test.ts", + "seconds": 3.393 + }, + { + "path": "tests/integration/build/locale/chunks-threshold.integration.test.ts", + "seconds": 6.086 + }, + { + "path": "tests/integration/build/style-routing/delivery-spike.integration.test.ts", + "seconds": 1.827 + }, + { + "path": "src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts", + "seconds": 1.448 + }, + { + "path": "src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts", + "seconds": 3.02 + }, + { + "path": "tests/integration/types/locale.integration.test.ts", + "seconds": 1.762 + }, + { + "path": "tests/integration/build/virtual-modules/virtual-modules.integration.test.ts", + "seconds": 1.524 + }, + { + "path": "src/cli/virtual/virtual.test.ts", + "seconds": 0.586 + }, + { + "path": "tests/integration/build/locale/chunks-disabled.integration.test.ts", + "seconds": 0.863 + }, + { + "path": "tests/integration/build/style-routing/selective-split.integration.test.ts", + "seconds": 0.915 + }, + { + "path": "src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts", + "seconds": 0.296 + }, + { + "path": "src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts", + "seconds": 0.125 + }, + { + "path": "src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts", + "seconds": 0.114 + }, + { + "path": "tests/integration/build/content/content-api.integration.test.ts", + "seconds": 0.07 + }, + { + "path": "tests/integration/build/permissions/permissions.integration.test.ts", + "seconds": 3.134 + }, + { + "path": "src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts", + "seconds": 0.055 + }, + { + "path": "tests/integration/build/style-routing/routing-spike.integration.test.ts", + "seconds": 0.578 + }, + { + "path": "src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts", + "seconds": 0.163 + }, + { + "path": "tests/build-output.test.ts", + "seconds": 0.044 + }, + { + "path": "src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts", + "seconds": 0.047 + }, + { + "path": "tests/integration/build/style-routing/style-routing.integration.test.ts", + "seconds": 1.245 + }, + { + "path": "tests/integration/build/style-routing/content-chunks.integration.test.ts", + "seconds": 0.352 + }, + { + "path": "src/cli/bundler/plugins/resource-access/utils.test.ts", + "seconds": 0.04 + }, + { + "path": "src/cli/bundler/plugins/utils/runtime-template.test.ts", + "seconds": 0.031 + }, + { + "path": "src/cli/index.test.ts", + "seconds": 3.173 + }, + { + "path": "tests/integration/build/override/override.integration.test.ts", + "seconds": 21.669 + } + ] + }, + { + "phase": "before", + "workers": 2, + "warmup": false, + "run": 1, + "wallSeconds": 51.31795075000264, + "exitCode": 0, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "build", + "types", + "--maxWorkers=2", + "--json", + "--outputFile=/tmp/override-benchmark/before-2-1.json" + ], + "suites": 37, + "tests": 285, + "files": [ + { + "path": "tests/integration/build/isolation/isolation-watch.integration.test.ts", + "seconds": 8.631 + }, + { + "path": "tests/integration/build/locale/dynamic.integration.test.ts", + "seconds": 6.774 + }, + { + "path": "tests/integration/build/override/override.integration.test.ts", + "seconds": 19.463 + }, + { + "path": "tests/integration/build/options/options.integration.test.ts", + "seconds": 5.91 + }, + { + "path": "tests/integration/build/locale/locale.integration.test.ts", + "seconds": 6.247 + }, + { + "path": "tests/integration/build/locale/chunks.integration.test.ts", + "seconds": 5.221 + }, + { + "path": "tests/integration/types/registries.integration.test.ts", + "seconds": 5.448 + }, + { + "path": "tests/integration/build/locale/chunks-threshold.integration.test.ts", + "seconds": 5.07 + }, + { + "path": "tests/integration/types/content.integration.test.ts", + "seconds": 4.321 + }, + { + "path": "tests/integration/build/relay-runtime/relay-runtime.integration.test.ts", + "seconds": 4.841 + }, + { + "path": "src/cli/plugins/content/RelayDeclaration.test.ts", + "seconds": 3.16 + }, + { + "path": "tests/integration/types/relay.integration.test.ts", + "seconds": 4.043 + }, + { + "path": "src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts", + "seconds": 3.0 + }, + { + "path": "tests/integration/types/view.integration.test.ts", + "seconds": 2.726 + }, + { + "path": "src/cli/index.test.ts", + "seconds": 2.65 + }, + { + "path": "tests/integration/build/permissions/permissions.integration.test.ts", + "seconds": 2.518 + }, + { + "path": "tests/integration/build/style-routing/delivery-spike.integration.test.ts", + "seconds": 1.433 + }, + { + "path": "src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts", + "seconds": 1.846 + }, + { + "path": "tests/integration/build/virtual-modules/virtual-modules.integration.test.ts", + "seconds": 1.279 + }, + { + "path": "tests/integration/types/locale.integration.test.ts", + "seconds": 1.524 + }, + { + "path": "src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts", + "seconds": 1.036 + }, + { + "path": "tests/integration/build/style-routing/style-routing.integration.test.ts", + "seconds": 0.979 + }, + { + "path": "tests/integration/build/style-routing/selective-split.integration.test.ts", + "seconds": 0.728 + }, + { + "path": "tests/integration/build/locale/chunks-disabled.integration.test.ts", + "seconds": 0.706 + }, + { + "path": "tests/integration/build/style-routing/routing-spike.integration.test.ts", + "seconds": 0.326 + }, + { + "path": "src/cli/virtual/virtual.test.ts", + "seconds": 0.519 + }, + { + "path": "tests/integration/build/style-routing/content-chunks.integration.test.ts", + "seconds": 0.181 + }, + { + "path": "src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts", + "seconds": 0.102 + }, + { + "path": "src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts", + "seconds": 0.271 + }, + { + "path": "src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts", + "seconds": 0.093 + }, + { + "path": "src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts", + "seconds": 0.108 + }, + { + "path": "tests/integration/build/content/content-api.integration.test.ts", + "seconds": 0.049 + }, + { + "path": "src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts", + "seconds": 0.035 + }, + { + "path": "src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts", + "seconds": 0.054 + }, + { + "path": "tests/build-output.test.ts", + "seconds": 0.036 + }, + { + "path": "src/cli/bundler/plugins/resource-access/utils.test.ts", + "seconds": 0.043 + }, + { + "path": "src/cli/bundler/plugins/utils/runtime-template.test.ts", + "seconds": 0.024 + } + ] + }, + { + "phase": "after", + "workers": 8, + "warmup": true, + "run": 0, + "wallSeconds": 15.487127125030383, + "exitCode": 0, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "build", + "types", + "--maxWorkers=8", + "--json", + "--outputFile=/tmp/override-benchmark/after-8-0.json" + ], + "suites": 40, + "tests": 285, + "files": [ + { + "path": "tests/integration/types/registries.integration.test.ts", + "seconds": 5.949 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 6.625 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 6.625 + }, + { + "path": "tests/integration/build/locale/locale.integration.test.ts", + "seconds": 6.694 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 6.725 + }, + { + "path": "tests/integration/build/options/options.integration.test.ts", + "seconds": 6.81 + }, + { + "path": "tests/integration/build/locale/dynamic.integration.test.ts", + "seconds": 7.415 + }, + { + "path": "tests/integration/build/isolation/isolation-watch.integration.test.ts", + "seconds": 9.211 + }, + { + "path": "src/cli/plugins/content/RelayDeclaration.test.ts", + "seconds": 3.625 + }, + { + "path": "src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts", + "seconds": 3.397 + }, + { + "path": "tests/integration/types/relay.integration.test.ts", + "seconds": 4.561 + }, + { + "path": "tests/integration/types/content.integration.test.ts", + "seconds": 4.805 + }, + { + "path": "tests/integration/build/locale/chunks.integration.test.ts", + "seconds": 6.036 + }, + { + "path": "tests/integration/build/relay-runtime/relay-runtime.integration.test.ts", + "seconds": 5.465 + }, + { + "path": "tests/integration/types/view.integration.test.ts", + "seconds": 3.437 + }, + { + "path": "tests/integration/build/locale/chunks-threshold.integration.test.ts", + "seconds": 6.098 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 2.245 + }, + { + "path": "src/cli/index.test.ts", + "seconds": 3.195 + }, + { + "path": "tests/integration/types/locale.integration.test.ts", + "seconds": 1.942 + }, + { + "path": "tests/integration/build/permissions/permissions.integration.test.ts", + "seconds": 3.215 + }, + { + "path": "tests/integration/build/style-routing/delivery-spike.integration.test.ts", + "seconds": 2.193 + }, + { + "path": "tests/integration/build/virtual-modules/virtual-modules.integration.test.ts", + "seconds": 1.663 + }, + { + "path": "src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts", + "seconds": 1.733 + }, + { + "path": "src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts", + "seconds": 0.313 + }, + { + "path": "src/cli/virtual/virtual.test.ts", + "seconds": 0.626 + }, + { + "path": "tests/integration/build/style-routing/selective-split.integration.test.ts", + "seconds": 1.075 + }, + { + "path": "src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts", + "seconds": 3.235 + }, + { + "path": "tests/integration/build/style-routing/routing-spike.integration.test.ts", + "seconds": 0.477 + }, + { + "path": "src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts", + "seconds": 0.16 + }, + { + "path": "src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts", + "seconds": 0.068 + }, + { + "path": "tests/integration/build/locale/chunks-disabled.integration.test.ts", + "seconds": 0.876 + }, + { + "path": "tests/integration/build/content/content-api.integration.test.ts", + "seconds": 0.063 + }, + { + "path": "src/cli/bundler/plugins/resource-access/utils.test.ts", + "seconds": 0.037 + }, + { + "path": "src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts", + "seconds": 0.121 + }, + { + "path": "tests/build-output.test.ts", + "seconds": 0.043 + }, + { + "path": "src/cli/bundler/plugins/utils/runtime-template.test.ts", + "seconds": 0.033 + }, + { + "path": "src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts", + "seconds": 0.205 + }, + { + "path": "src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts", + "seconds": 0.073 + }, + { + "path": "tests/integration/build/style-routing/content-chunks.integration.test.ts", + "seconds": 0.445 + }, + { + "path": "tests/integration/build/style-routing/style-routing.integration.test.ts", + "seconds": 1.465 + } + ] + }, + { + "phase": "after", + "workers": 8, + "warmup": false, + "run": 1, + "wallSeconds": 16.5209129168652, + "exitCode": 0, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "build", + "types", + "--maxWorkers=8", + "--json", + "--outputFile=/tmp/override-benchmark/after-8-1.json" + ], + "suites": 40, + "tests": 285, + "files": [ + { + "path": "tests/integration/build/locale/chunks-threshold.integration.test.ts", + "seconds": 5.803 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 6.64 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 6.643 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 6.706 + }, + { + "path": "tests/integration/build/locale/locale.integration.test.ts", + "seconds": 6.732 + }, + { + "path": "tests/integration/build/options/options.integration.test.ts", + "seconds": 6.759 + }, + { + "path": "tests/integration/build/locale/dynamic.integration.test.ts", + "seconds": 7.501 + }, + { + "path": "tests/integration/build/isolation/isolation-watch.integration.test.ts", + "seconds": 9.45 + }, + { + "path": "src/cli/plugins/content/RelayDeclaration.test.ts", + "seconds": 3.706 + }, + { + "path": "tests/integration/types/view.integration.test.ts", + "seconds": 3.363 + }, + { + "path": "tests/integration/types/relay.integration.test.ts", + "seconds": 4.623 + }, + { + "path": "tests/integration/types/content.integration.test.ts", + "seconds": 4.939 + }, + { + "path": "tests/integration/build/locale/chunks.integration.test.ts", + "seconds": 6.269 + }, + { + "path": "tests/integration/build/relay-runtime/relay-runtime.integration.test.ts", + "seconds": 5.623 + }, + { + "path": "tests/integration/types/registries.integration.test.ts", + "seconds": 6.497 + }, + { + "path": "src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts", + "seconds": 4.115 + }, + { + "path": "src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts", + "seconds": 3.96 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 2.861 + }, + { + "path": "tests/integration/types/locale.integration.test.ts", + "seconds": 2.429 + }, + { + "path": "tests/integration/build/permissions/permissions.integration.test.ts", + "seconds": 3.928 + }, + { + "path": "tests/integration/build/style-routing/delivery-spike.integration.test.ts", + "seconds": 2.924 + }, + { + "path": "src/cli/index.test.ts", + "seconds": 3.763 + }, + { + "path": "src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts", + "seconds": 2.016 + }, + { + "path": "tests/integration/build/virtual-modules/virtual-modules.integration.test.ts", + "seconds": 1.737 + }, + { + "path": "src/cli/virtual/virtual.test.ts", + "seconds": 0.657 + }, + { + "path": "tests/integration/build/style-routing/routing-spike.integration.test.ts", + "seconds": 0.473 + }, + { + "path": "tests/integration/build/style-routing/selective-split.integration.test.ts", + "seconds": 0.973 + }, + { + "path": "src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts", + "seconds": 0.186 + }, + { + "path": "src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts", + "seconds": 0.316 + }, + { + "path": "src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts", + "seconds": 0.06 + }, + { + "path": "tests/integration/build/style-routing/content-chunks.integration.test.ts", + "seconds": 0.436 + }, + { + "path": "src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts", + "seconds": 0.066 + }, + { + "path": "tests/integration/build/content/content-api.integration.test.ts", + "seconds": 0.076 + }, + { + "path": "tests/integration/build/locale/chunks-disabled.integration.test.ts", + "seconds": 0.893 + }, + { + "path": "src/cli/bundler/plugins/utils/runtime-template.test.ts", + "seconds": 0.03 + }, + { + "path": "tests/build-output.test.ts", + "seconds": 0.05 + }, + { + "path": "src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts", + "seconds": 0.132 + }, + { + "path": "src/cli/bundler/plugins/resource-access/utils.test.ts", + "seconds": 0.049 + }, + { + "path": "src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts", + "seconds": 0.174 + }, + { + "path": "tests/integration/build/style-routing/style-routing.integration.test.ts", + "seconds": 1.447 + } + ] + }, + { + "phase": "after", + "workers": 8, + "warmup": false, + "run": 2, + "wallSeconds": 16.205771875102073, + "exitCode": 0, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "build", + "types", + "--maxWorkers=8", + "--json", + "--outputFile=/tmp/override-benchmark/after-8-2.json" + ], + "suites": 40, + "tests": 285, + "files": [ + { + "path": "tests/integration/types/registries.integration.test.ts", + "seconds": 6.07 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 6.534 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 6.534 + }, + { + "path": "tests/integration/build/locale/locale.integration.test.ts", + "seconds": 6.653 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 6.661 + }, + { + "path": "tests/integration/build/options/options.integration.test.ts", + "seconds": 6.729 + }, + { + "path": "tests/integration/build/locale/dynamic.integration.test.ts", + "seconds": 7.473 + }, + { + "path": "tests/integration/build/isolation/isolation-watch.integration.test.ts", + "seconds": 9.557 + }, + { + "path": "src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts", + "seconds": 2.737 + }, + { + "path": "src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts", + "seconds": 3.564 + }, + { + "path": "tests/integration/types/relay.integration.test.ts", + "seconds": 4.759 + }, + { + "path": "tests/integration/types/content.integration.test.ts", + "seconds": 5.066 + }, + { + "path": "tests/integration/build/relay-runtime/relay-runtime.integration.test.ts", + "seconds": 6.048 + }, + { + "path": "tests/integration/build/locale/chunks.integration.test.ts", + "seconds": 6.791 + }, + { + "path": "tests/integration/build/permissions/permissions.integration.test.ts", + "seconds": 3.706 + }, + { + "path": "tests/integration/build/locale/chunks-threshold.integration.test.ts", + "seconds": 6.777 + }, + { + "path": "src/cli/index.test.ts", + "seconds": 3.596 + }, + { + "path": "src/cli/plugins/content/RelayDeclaration.test.ts", + "seconds": 3.959 + }, + { + "path": "tests/integration/build/style-routing/delivery-spike.integration.test.ts", + "seconds": 2.552 + }, + { + "path": "tests/integration/types/locale.integration.test.ts", + "seconds": 1.851 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 2.215 + }, + { + "path": "tests/integration/build/virtual-modules/virtual-modules.integration.test.ts", + "seconds": 1.531 + }, + { + "path": "src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts", + "seconds": 1.678 + }, + { + "path": "tests/integration/types/view.integration.test.ts", + "seconds": 3.672 + }, + { + "path": "tests/integration/build/locale/chunks-disabled.integration.test.ts", + "seconds": 0.925 + }, + { + "path": "src/cli/virtual/virtual.test.ts", + "seconds": 0.604 + }, + { + "path": "src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts", + "seconds": 0.128 + }, + { + "path": "tests/integration/build/style-routing/selective-split.integration.test.ts", + "seconds": 1.154 + }, + { + "path": "tests/integration/build/content/content-api.integration.test.ts", + "seconds": 0.066 + }, + { + "path": "src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts", + "seconds": 0.511 + }, + { + "path": "src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts", + "seconds": 0.051 + }, + { + "path": "src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts", + "seconds": 0.371 + }, + { + "path": "tests/integration/build/style-routing/content-chunks.integration.test.ts", + "seconds": 0.644 + }, + { + "path": "src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts", + "seconds": 0.045 + }, + { + "path": "src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts", + "seconds": 0.164 + }, + { + "path": "src/cli/bundler/plugins/utils/runtime-template.test.ts", + "seconds": 0.03 + }, + { + "path": "src/cli/bundler/plugins/resource-access/utils.test.ts", + "seconds": 0.037 + }, + { + "path": "tests/integration/build/style-routing/routing-spike.integration.test.ts", + "seconds": 0.712 + }, + { + "path": "tests/build-output.test.ts", + "seconds": 0.055 + }, + { + "path": "tests/integration/build/style-routing/style-routing.integration.test.ts", + "seconds": 1.798 + } + ] + }, + { + "phase": "after", + "workers": 8, + "warmup": false, + "run": 3, + "wallSeconds": 17.61266070790589, + "exitCode": 0, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "build", + "types", + "--maxWorkers=8", + "--json", + "--outputFile=/tmp/override-benchmark/after-8-3.json" + ], + "suites": 40, + "tests": 285, + "files": [ + { + "path": "tests/integration/build/locale/chunks-threshold.integration.test.ts", + "seconds": 7.642 + }, + { + "path": "tests/integration/build/locale/chunks.integration.test.ts", + "seconds": 7.786 + }, + { + "path": "tests/integration/build/locale/locale.integration.test.ts", + "seconds": 7.826 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 8.734 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 8.835 + }, + { + "path": "tests/integration/build/options/options.integration.test.ts", + "seconds": 8.921 + }, + { + "path": "tests/integration/build/locale/dynamic.integration.test.ts", + "seconds": 9.229 + }, + { + "path": "tests/integration/build/isolation/isolation-watch.integration.test.ts", + "seconds": 10.635 + }, + { + "path": "tests/integration/build/permissions/permissions.integration.test.ts", + "seconds": 2.994 + }, + { + "path": "src/cli/plugins/content/RelayDeclaration.test.ts", + "seconds": 3.651 + }, + { + "path": "tests/integration/types/relay.integration.test.ts", + "seconds": 4.749 + }, + { + "path": "tests/integration/types/content.integration.test.ts", + "seconds": 4.876 + }, + { + "path": "tests/integration/build/relay-runtime/relay-runtime.integration.test.ts", + "seconds": 5.914 + }, + { + "path": "tests/integration/types/view.integration.test.ts", + "seconds": 3.513 + }, + { + "path": "tests/integration/types/registries.integration.test.ts", + "seconds": 6.634 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 7.205 + }, + { + "path": "src/cli/index.test.ts", + "seconds": 3.113 + }, + { + "path": "tests/integration/build/style-routing/delivery-spike.integration.test.ts", + "seconds": 2.106 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 2.252 + }, + { + "path": "tests/integration/types/locale.integration.test.ts", + "seconds": 1.938 + }, + { + "path": "tests/integration/build/style-routing/style-routing.integration.test.ts", + "seconds": 1.684 + }, + { + "path": "src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts", + "seconds": 3.697 + }, + { + "path": "tests/integration/build/style-routing/content-chunks.integration.test.ts", + "seconds": 0.256 + }, + { + "path": "src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts", + "seconds": 1.752 + }, + { + "path": "tests/integration/build/style-routing/routing-spike.integration.test.ts", + "seconds": 0.531 + }, + { + "path": "tests/integration/build/style-routing/selective-split.integration.test.ts", + "seconds": 0.923 + }, + { + "path": "src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts", + "seconds": 0.361 + }, + { + "path": "src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts", + "seconds": 0.153 + }, + { + "path": "src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts", + "seconds": 0.192 + }, + { + "path": "tests/integration/build/content/content-api.integration.test.ts", + "seconds": 0.086 + }, + { + "path": "src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts", + "seconds": 3.259 + }, + { + "path": "src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts", + "seconds": 0.265 + }, + { + "path": "tests/build-output.test.ts", + "seconds": 0.068 + }, + { + "path": "src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts", + "seconds": 0.06 + }, + { + "path": "src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts", + "seconds": 0.061 + }, + { + "path": "src/cli/bundler/plugins/resource-access/utils.test.ts", + "seconds": 0.045 + }, + { + "path": "src/cli/virtual/virtual.test.ts", + "seconds": 0.631 + }, + { + "path": "src/cli/bundler/plugins/utils/runtime-template.test.ts", + "seconds": 0.033 + }, + { + "path": "tests/integration/build/locale/chunks-disabled.integration.test.ts", + "seconds": 0.913 + }, + { + "path": "tests/integration/build/virtual-modules/virtual-modules.integration.test.ts", + "seconds": 1.605 + } + ] + }, + { + "phase": "after", + "workers": 2, + "warmup": false, + "run": 1, + "wallSeconds": 54.29423633287661, + "exitCode": 0, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "build", + "types", + "--maxWorkers=2", + "--json", + "--outputFile=/tmp/override-benchmark/after-2-1.json" + ], + "suites": 40, + "tests": 285, + "files": [ + { + "path": "tests/integration/build/locale/dynamic.integration.test.ts", + "seconds": 7.551 + }, + { + "path": "tests/integration/build/isolation/isolation-watch.integration.test.ts", + "seconds": 8.765 + }, + { + "path": "tests/integration/build/options/options.integration.test.ts", + "seconds": 6.062 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 5.986 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 5.677 + }, + { + "path": "tests/integration/build/locale/locale.integration.test.ts", + "seconds": 6.138 + }, + { + "path": "tests/integration/build/locale/chunks.integration.test.ts", + "seconds": 5.484 + }, + { + "path": "tests/integration/build/locale/chunks-threshold.integration.test.ts", + "seconds": 5.356 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 6.314 + }, + { + "path": "tests/integration/types/registries.integration.test.ts", + "seconds": 5.912 + }, + { + "path": "tests/integration/build/relay-runtime/relay-runtime.integration.test.ts", + "seconds": 5.128 + }, + { + "path": "tests/integration/types/content.integration.test.ts", + "seconds": 4.372 + }, + { + "path": "src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts", + "seconds": 3.094 + }, + { + "path": "tests/integration/types/relay.integration.test.ts", + "seconds": 4.361 + }, + { + "path": "src/cli/plugins/content/RelayDeclaration.test.ts", + "seconds": 3.514 + }, + { + "path": "tests/integration/types/view.integration.test.ts", + "seconds": 3.269 + }, + { + "path": "src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts", + "seconds": 2.483 + }, + { + "path": "src/cli/index.test.ts", + "seconds": 2.901 + }, + { + "path": "tests/integration/build/permissions/permissions.integration.test.ts", + "seconds": 2.703 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 1.893 + }, + { + "path": "tests/integration/build/style-routing/delivery-spike.integration.test.ts", + "seconds": 1.565 + }, + { + "path": "tests/integration/types/locale.integration.test.ts", + "seconds": 1.592 + }, + { + "path": "src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts", + "seconds": 1.198 + }, + { + "path": "tests/integration/build/style-routing/style-routing.integration.test.ts", + "seconds": 1.179 + }, + { + "path": "tests/integration/build/style-routing/selective-split.integration.test.ts", + "seconds": 0.564 + }, + { + "path": "tests/integration/build/virtual-modules/virtual-modules.integration.test.ts", + "seconds": 1.33 + }, + { + "path": "tests/integration/build/locale/chunks-disabled.integration.test.ts", + "seconds": 0.77 + }, + { + "path": "src/cli/virtual/virtual.test.ts", + "seconds": 0.544 + }, + { + "path": "tests/integration/build/style-routing/routing-spike.integration.test.ts", + "seconds": 0.332 + }, + { + "path": "src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts", + "seconds": 0.107 + }, + { + "path": "src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts", + "seconds": 0.258 + }, + { + "path": "src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts", + "seconds": 0.123 + }, + { + "path": "tests/integration/build/style-routing/content-chunks.integration.test.ts", + "seconds": 0.203 + }, + { + "path": "tests/integration/build/content/content-api.integration.test.ts", + "seconds": 0.052 + }, + { + "path": "tests/build-output.test.ts", + "seconds": 0.038 + }, + { + "path": "src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts", + "seconds": 0.128 + }, + { + "path": "src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts", + "seconds": 0.037 + }, + { + "path": "src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts", + "seconds": 0.039 + }, + { + "path": "src/cli/bundler/plugins/resource-access/utils.test.ts", + "seconds": 0.03 + }, + { + "path": "src/cli/bundler/plugins/utils/runtime-template.test.ts", + "seconds": 0.026 + } + ] + }, + { + "phase": "before", + "workers": 2, + "warmup": false, + "run": 2, + "wallSeconds": 52.397480624960735, + "exitCode": 0, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "build", + "types", + "--maxWorkers=2", + "--json", + "--outputFile=/tmp/override-benchmark/before-2-2.json" + ], + "suites": 37, + "tests": 285, + "files": [ + { + "path": "tests/integration/build/isolation/isolation-watch.integration.test.ts", + "seconds": 8.74 + }, + { + "path": "tests/integration/build/locale/dynamic.integration.test.ts", + "seconds": 6.877 + }, + { + "path": "tests/integration/build/override/override.integration.test.ts", + "seconds": 19.988 + }, + { + "path": "tests/integration/build/locale/locale.integration.test.ts", + "seconds": 6.112 + }, + { + "path": "tests/integration/build/options/options.integration.test.ts", + "seconds": 6.292 + }, + { + "path": "tests/integration/types/registries.integration.test.ts", + "seconds": 5.444 + }, + { + "path": "tests/integration/build/locale/chunks.integration.test.ts", + "seconds": 5.278 + }, + { + "path": "tests/integration/build/locale/chunks-threshold.integration.test.ts", + "seconds": 5.192 + }, + { + "path": "tests/integration/build/relay-runtime/relay-runtime.integration.test.ts", + "seconds": 4.961 + }, + { + "path": "tests/integration/types/content.integration.test.ts", + "seconds": 4.244 + }, + { + "path": "src/cli/plugins/content/RelayDeclaration.test.ts", + "seconds": 3.076 + }, + { + "path": "tests/integration/types/relay.integration.test.ts", + "seconds": 4.259 + }, + { + "path": "tests/integration/types/view.integration.test.ts", + "seconds": 2.852 + }, + { + "path": "src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts", + "seconds": 2.987 + }, + { + "path": "src/cli/index.test.ts", + "seconds": 2.658 + }, + { + "path": "tests/integration/build/permissions/permissions.integration.test.ts", + "seconds": 2.557 + }, + { + "path": "src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts", + "seconds": 2.007 + }, + { + "path": "tests/integration/types/locale.integration.test.ts", + "seconds": 1.547 + }, + { + "path": "tests/integration/build/style-routing/delivery-spike.integration.test.ts", + "seconds": 1.454 + }, + { + "path": "tests/integration/build/virtual-modules/virtual-modules.integration.test.ts", + "seconds": 1.278 + }, + { + "path": "src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts", + "seconds": 1.026 + }, + { + "path": "tests/integration/build/style-routing/style-routing.integration.test.ts", + "seconds": 1.076 + }, + { + "path": "tests/integration/build/locale/chunks-disabled.integration.test.ts", + "seconds": 0.751 + }, + { + "path": "tests/integration/build/style-routing/selective-split.integration.test.ts", + "seconds": 0.567 + }, + { + "path": "src/cli/virtual/virtual.test.ts", + "seconds": 0.532 + }, + { + "path": "tests/integration/build/style-routing/routing-spike.integration.test.ts", + "seconds": 0.386 + }, + { + "path": "src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts", + "seconds": 0.378 + }, + { + "path": "tests/integration/build/style-routing/content-chunks.integration.test.ts", + "seconds": 0.217 + }, + { + "path": "src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts", + "seconds": 0.118 + }, + { + "path": "src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts", + "seconds": 0.151 + }, + { + "path": "tests/integration/build/content/content-api.integration.test.ts", + "seconds": 0.065 + }, + { + "path": "src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts", + "seconds": 0.109 + }, + { + "path": "src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts", + "seconds": 0.06 + }, + { + "path": "tests/build-output.test.ts", + "seconds": 0.037 + }, + { + "path": "src/cli/bundler/plugins/resource-access/utils.test.ts", + "seconds": 0.031 + }, + { + "path": "src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts", + "seconds": 0.058 + }, + { + "path": "src/cli/bundler/plugins/utils/runtime-template.test.ts", + "seconds": 0.029 + } + ] + }, + { + "phase": "after", + "workers": 2, + "warmup": false, + "run": 2, + "wallSeconds": 52.022176374914125, + "exitCode": 0, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "build", + "types", + "--maxWorkers=2", + "--json", + "--outputFile=/tmp/override-benchmark/after-2-2.json" + ], + "suites": 40, + "tests": 285, + "files": [ + { + "path": "tests/integration/build/locale/dynamic.integration.test.ts", + "seconds": 7.067 + }, + { + "path": "tests/integration/build/isolation/isolation-watch.integration.test.ts", + "seconds": 8.787 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 5.905 + }, + { + "path": "tests/integration/build/options/options.integration.test.ts", + "seconds": 6.101 + }, + { + "path": "tests/integration/build/locale/locale.integration.test.ts", + "seconds": 6.329 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 6.011 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 5.964 + }, + { + "path": "tests/integration/types/registries.integration.test.ts", + "seconds": 5.275 + }, + { + "path": "tests/integration/build/locale/chunks.integration.test.ts", + "seconds": 5.314 + }, + { + "path": "tests/integration/build/locale/chunks-threshold.integration.test.ts", + "seconds": 5.38 + }, + { + "path": "tests/integration/types/relay.integration.test.ts", + "seconds": 3.869 + }, + { + "path": "tests/integration/build/relay-runtime/relay-runtime.integration.test.ts", + "seconds": 4.924 + }, + { + "path": "src/cli/plugins/content/RelayDeclaration.test.ts", + "seconds": 3.321 + }, + { + "path": "tests/integration/types/content.integration.test.ts", + "seconds": 4.182 + }, + { + "path": "src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts", + "seconds": 2.981 + }, + { + "path": "tests/integration/types/view.integration.test.ts", + "seconds": 2.796 + }, + { + "path": "src/cli/index.test.ts", + "seconds": 2.68 + }, + { + "path": "tests/integration/build/permissions/permissions.integration.test.ts", + "seconds": 2.514 + }, + { + "path": "src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts", + "seconds": 2.064 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 1.87 + }, + { + "path": "tests/integration/types/locale.integration.test.ts", + "seconds": 1.577 + }, + { + "path": "tests/integration/build/style-routing/delivery-spike.integration.test.ts", + "seconds": 1.374 + }, + { + "path": "tests/integration/build/style-routing/style-routing.integration.test.ts", + "seconds": 0.931 + }, + { + "path": "tests/integration/build/virtual-modules/virtual-modules.integration.test.ts", + "seconds": 1.305 + }, + { + "path": "src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts", + "seconds": 0.896 + }, + { + "path": "tests/integration/build/locale/chunks-disabled.integration.test.ts", + "seconds": 0.732 + }, + { + "path": "tests/integration/build/style-routing/selective-split.integration.test.ts", + "seconds": 0.627 + }, + { + "path": "src/cli/virtual/virtual.test.ts", + "seconds": 0.516 + }, + { + "path": "src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts", + "seconds": 0.232 + }, + { + "path": "tests/integration/build/style-routing/routing-spike.integration.test.ts", + "seconds": 0.339 + }, + { + "path": "src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts", + "seconds": 0.102 + }, + { + "path": "src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts", + "seconds": 0.113 + }, + { + "path": "tests/integration/build/style-routing/content-chunks.integration.test.ts", + "seconds": 0.276 + }, + { + "path": "tests/integration/build/content/content-api.integration.test.ts", + "seconds": 0.05 + }, + { + "path": "src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts", + "seconds": 0.04 + }, + { + "path": "src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts", + "seconds": 0.111 + }, + { + "path": "tests/build-output.test.ts", + "seconds": 0.037 + }, + { + "path": "src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts", + "seconds": 0.042 + }, + { + "path": "src/cli/bundler/plugins/utils/runtime-template.test.ts", + "seconds": 0.026 + }, + { + "path": "src/cli/bundler/plugins/resource-access/utils.test.ts", + "seconds": 0.033 + } + ] + }, + { + "phase": "before", + "workers": 2, + "warmup": false, + "run": 3, + "wallSeconds": 53.63272133283317, + "exitCode": 0, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "build", + "types", + "--maxWorkers=2", + "--json", + "--outputFile=/tmp/override-benchmark/before-2-3.json" + ], + "suites": 37, + "tests": 285, + "files": [ + { + "path": "tests/integration/build/isolation/isolation-watch.integration.test.ts", + "seconds": 8.849 + }, + { + "path": "tests/integration/build/locale/dynamic.integration.test.ts", + "seconds": 6.912 + }, + { + "path": "tests/integration/build/override/override.integration.test.ts", + "seconds": 19.621 + }, + { + "path": "tests/integration/build/locale/locale.integration.test.ts", + "seconds": 6.272 + }, + { + "path": "tests/integration/build/options/options.integration.test.ts", + "seconds": 6.481 + }, + { + "path": "tests/integration/build/locale/chunks-threshold.integration.test.ts", + "seconds": 5.492 + }, + { + "path": "tests/integration/build/locale/chunks.integration.test.ts", + "seconds": 6.03 + }, + { + "path": "tests/integration/types/registries.integration.test.ts", + "seconds": 6.002 + }, + { + "path": "tests/integration/build/relay-runtime/relay-runtime.integration.test.ts", + "seconds": 4.959 + }, + { + "path": "tests/integration/types/content.integration.test.ts", + "seconds": 4.269 + }, + { + "path": "src/cli/plugins/content/RelayDeclaration.test.ts", + "seconds": 3.162 + }, + { + "path": "tests/integration/types/relay.integration.test.ts", + "seconds": 4.318 + }, + { + "path": "src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts", + "seconds": 3.098 + }, + { + "path": "tests/integration/types/view.integration.test.ts", + "seconds": 2.967 + }, + { + "path": "src/cli/index.test.ts", + "seconds": 2.739 + }, + { + "path": "tests/integration/build/permissions/permissions.integration.test.ts", + "seconds": 2.597 + }, + { + "path": "tests/integration/types/locale.integration.test.ts", + "seconds": 1.574 + }, + { + "path": "src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts", + "seconds": 2.006 + }, + { + "path": "tests/integration/build/style-routing/delivery-spike.integration.test.ts", + "seconds": 1.404 + }, + { + "path": "tests/integration/build/virtual-modules/virtual-modules.integration.test.ts", + "seconds": 1.308 + }, + { + "path": "tests/integration/build/style-routing/style-routing.integration.test.ts", + "seconds": 1.016 + }, + { + "path": "src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts", + "seconds": 1.042 + }, + { + "path": "tests/integration/build/locale/chunks-disabled.integration.test.ts", + "seconds": 0.743 + }, + { + "path": "tests/integration/build/style-routing/selective-split.integration.test.ts", + "seconds": 0.753 + }, + { + "path": "src/cli/virtual/virtual.test.ts", + "seconds": 0.536 + }, + { + "path": "tests/integration/build/style-routing/routing-spike.integration.test.ts", + "seconds": 0.434 + }, + { + "path": "tests/integration/build/style-routing/content-chunks.integration.test.ts", + "seconds": 0.227 + }, + { + "path": "src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts", + "seconds": 0.118 + }, + { + "path": "src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts", + "seconds": 0.259 + }, + { + "path": "src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts", + "seconds": 0.12 + }, + { + "path": "src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts", + "seconds": 0.116 + }, + { + "path": "tests/integration/build/content/content-api.integration.test.ts", + "seconds": 0.068 + }, + { + "path": "src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts", + "seconds": 0.044 + }, + { + "path": "tests/build-output.test.ts", + "seconds": 0.04 + }, + { + "path": "src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts", + "seconds": 0.06 + }, + { + "path": "src/cli/bundler/plugins/resource-access/utils.test.ts", + "seconds": 0.029 + }, + { + "path": "src/cli/bundler/plugins/utils/runtime-template.test.ts", + "seconds": 0.038 + } + ] + }, + { + "phase": "after", + "workers": 2, + "warmup": false, + "run": 3, + "wallSeconds": 53.16435362491757, + "exitCode": 0, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "build", + "types", + "--maxWorkers=2", + "--json", + "--outputFile=/tmp/override-benchmark/after-2-3.json" + ], + "suites": 40, + "tests": 285, + "files": [ + { + "path": "tests/integration/build/locale/dynamic.integration.test.ts", + "seconds": 7.136 + }, + { + "path": "tests/integration/build/isolation/isolation-watch.integration.test.ts", + "seconds": 8.768 + }, + { + "path": "tests/integration/build/options/options.integration.test.ts", + "seconds": 5.947 + }, + { + "path": "tests/integration/build/locale/locale.integration.test.ts", + "seconds": 6.295 + }, + { + "path": "tests/integration/build/locale/chunks.integration.test.ts", + "seconds": 5.284 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 6.212 + }, + { + "path": "tests/integration/types/registries.integration.test.ts", + "seconds": 5.288 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 5.885 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 5.82 + }, + { + "path": "tests/integration/build/locale/chunks-threshold.integration.test.ts", + "seconds": 5.053 + }, + { + "path": "tests/integration/build/relay-runtime/relay-runtime.integration.test.ts", + "seconds": 5.027 + }, + { + "path": "tests/integration/types/relay.integration.test.ts", + "seconds": 4.478 + }, + { + "path": "tests/integration/types/content.integration.test.ts", + "seconds": 4.526 + }, + { + "path": "src/cli/plugins/content/RelayDeclaration.test.ts", + "seconds": 3.258 + }, + { + "path": "src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts", + "seconds": 3.162 + }, + { + "path": "tests/integration/types/view.integration.test.ts", + "seconds": 2.985 + }, + { + "path": "src/cli/index.test.ts", + "seconds": 2.762 + }, + { + "path": "tests/integration/build/permissions/permissions.integration.test.ts", + "seconds": 2.661 + }, + { + "path": "src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts", + "seconds": 1.913 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 1.921 + }, + { + "path": "tests/integration/types/locale.integration.test.ts", + "seconds": 1.725 + }, + { + "path": "tests/integration/build/style-routing/delivery-spike.integration.test.ts", + "seconds": 1.539 + }, + { + "path": "tests/integration/build/virtual-modules/virtual-modules.integration.test.ts", + "seconds": 1.377 + }, + { + "path": "src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts", + "seconds": 0.952 + }, + { + "path": "tests/integration/build/style-routing/selective-split.integration.test.ts", + "seconds": 0.692 + }, + { + "path": "tests/integration/build/style-routing/style-routing.integration.test.ts", + "seconds": 1.237 + }, + { + "path": "tests/integration/build/locale/chunks-disabled.integration.test.ts", + "seconds": 0.844 + }, + { + "path": "src/cli/virtual/virtual.test.ts", + "seconds": 0.604 + }, + { + "path": "tests/integration/build/style-routing/routing-spike.integration.test.ts", + "seconds": 0.416 + }, + { + "path": "src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts", + "seconds": 0.267 + }, + { + "path": "tests/integration/build/style-routing/content-chunks.integration.test.ts", + "seconds": 0.21 + }, + { + "path": "src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts", + "seconds": 0.121 + }, + { + "path": "src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts", + "seconds": 0.109 + }, + { + "path": "tests/integration/build/content/content-api.integration.test.ts", + "seconds": 0.049 + }, + { + "path": "src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts", + "seconds": 0.109 + }, + { + "path": "src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts", + "seconds": 0.041 + }, + { + "path": "src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts", + "seconds": 0.044 + }, + { + "path": "src/cli/bundler/plugins/utils/runtime-template.test.ts", + "seconds": 0.026 + }, + { + "path": "tests/build-output.test.ts", + "seconds": 0.036 + }, + { + "path": "src/cli/bundler/plugins/resource-access/utils.test.ts", + "seconds": 0.033 + } + ] + } + ], + "validation": [ + { + "name": "check", + "command": [ + "npm", + "run", + "check" + ], + "wallSeconds": 31.93171454197727, + "exitCode": 0 + }, + { + "name": "full", + "command": [ + "npm", + "test", + "--", + "--json", + "--outputFile=/tmp/override-benchmark/full-after.json" + ], + "wallSeconds": 58.474541374947876, + "exitCode": 0 + } + ], + "fullResult": { + "success": true, + "suites": 206, + "tests": 2003 + } +} diff --git a/tests/benchmarks/2026-09-25-override.md b/tests/benchmarks/2026-09-25-override.md new file mode 100644 index 00000000..18a30358 --- /dev/null +++ b/tests/benchmarks/2026-09-25-override.md @@ -0,0 +1,65 @@ +# Override scheduling follow-up — 2026-09-25 + +This follows the [Relay type-checking benchmark](2026-09-25-relay-types.md). The baseline is `5fe410b9`. +Only tests and documentation changed; runtime source, fixtures, worker limits and timeouts are unchanged. + +## Change and preserved coverage + +The single override file became New Tab, Bookmarks, History and competing-entrypoint files. A shared helper +registers the page support checks. Every scenario still creates and disposes its own fixture and launches its own CLI build. +There is no shared build cache, shared mutable application or `test.concurrent`. + +All **33 unique full test names** match the baseline, and AST comparison found the same **17 assertion statements**. +The matrix remains three pages × five browsers × two manifest versions, plus three competing-entrypoint cases. +The isolated override run passed four files / 33 cases in 6.33 seconds of Jest-reported time. + +## Measurements + +Apple M3 Max, 16 logical CPUs, macOS arm64, Node 24.5.0. The existing macOS `--no-sparkplug` workaround remains enabled. +Commands ran sequentially outside the sandbox. Package build time is excluded from group measurements; the package was built +once before the series and production code was not edited afterward. Whole-command time uses Python `time.perf_counter()`. +All group commands include Jest JSON reporting and an explicit worker count. + +At eight workers each state has one warm-up followed by three measured runs. At two workers the first before/after pair +followed those series; two additional alternating before/after pairs checked the initial difference. For these repeats, +a script temporarily restored only the original override test or the four new test files, verifying their contents and +restoring the final split in `finally`. Other sources and dependencies stayed unchanged. + +| Workers | Before: median (range) | After: median (range) | +| ------- | ---------------------- | --------------------- | +| 8 | 22.51 s (22.25–22.71) | 16.52 s (16.21–17.61) | +| 2 | 52.40 s (51.32–53.63) | 53.16 s (52.02–54.29) | + +At eight workers the group is **1.36× faster**. +At two workers the ranges overlap; no reliable speedup is established. The build count remains 33, so splitting removes +the serial tail when workers are available but does not reduce the compilation work. Two workers match the current CI +setting; these local macOS runs are not measurements of the Linux/Windows CI matrix or its coverage-enabled job. + +Build + types passes **40 suites / 285 tests**, versus 37 suites / 285 tests before the split. +The next longest file in the eight-worker runs is the isolation watch integration, around ten seconds; the group also +includes other jobs and scheduling overhead, so its total does not equal that file's duration. + +## Final validation + +- `typecheck`, test inventory and formatting passed; inventory has 206 files, each in exactly one project, and zero local module mocks. +- `npm run check`: 175 suites / 1961 tests plus framework and 24 fixture typechecks, 31.93 seconds for the whole command. +- Full `npm test`: 206 suites / 2003 tests, including Chrome and Firefox, 58.47 seconds for the whole command. + +The full/check timings are single observations, not new before/after series. The preceding Relay report recorded +52.32 seconds for the full command, versus 58.47 seconds here, so these results do **not** establish a full-suite speedup. +The unchanged Chrome override browser file took 16.46 seconds here versus 10.59 seconds in that earlier full run; +this observation does not establish the cause of the difference. + +## Reproduce + +[Raw results](2026-09-25-override.json) include all runs, per-file durations, scenario names, worker counts and exit codes. +At each revision, build once, warm up the selected group, then measure it three times. Use unique JSON output filenames. + +```sh +npm run build +npm run test:run -- --selectProjects build types --maxWorkers=8 --json --outputFile=/tmp/override-8.json +npm run test:run -- --selectProjects build types --maxWorkers=2 --json --outputFile=/tmp/override-2.json +npm run test:run -- --selectProjects build --testPathPatterns=tests/integration/build/override +npm run check +npm test +``` diff --git a/tests/benchmarks/2026-09-25-relay-types.json b/tests/benchmarks/2026-09-25-relay-types.json new file mode 100644 index 00000000..435a1367 --- /dev/null +++ b/tests/benchmarks/2026-09-25-relay-types.json @@ -0,0 +1,1612 @@ +{ + "baselineRef": "7c4e136e", + "environment": { + "node": "24.5.0", + "typescript": "5.9.3", + "machine": "Apple M3 Max, 16 logical CPUs, macOS arm64", + "jestWorkers": 8, + "jestFlag": "--no-sparkplug", + "probeNodeFlags": [] + }, + "probe": { + "script": "const path = require('node:path');\nconst {execFileSync} = require('node:child_process');\nconst ts = require(path.join(process.cwd(), 'node_modules/typescript'));\nconst root = process.cwd();\nconst state = process.argv[2];\nconst options = {...ts.parseJsonConfigFileContent(ts.readConfigFile(path.join(root, 'tsconfig.json'), ts.sys.readFile).config, ts.sys, root).options, noEmit: true, types: ['node', 'chrome']};\nconst filename = path.join(root, 'tests/integration/types/fixtures/relay-probe.ts');\nconst source = `import {defineRelay} from \"adnbn\";\nimport * as entry from \"adnbn/entry/relay\";\nimport {Builder as ContentBuilder} from \"adnbn/entry/content/vanilla\";\nconst definition = defineRelay({name: \"scanner\", init: () => ({scan: (text: string) => text.length})});\nnew entry.Builder(definition, ContentBuilder);`;\nconst before = new Map();\nif (state !== 'after') {\n for (const file of ['src/entry/relay/Builder.ts', 'src/entry/relay/index.ts', 'src/types/content/definition.ts']) {\n before.set(path.join(root, file), execFileSync('git', ['show', `7c4e136e:${file}`], {encoding: 'utf8'}));\n }\n}\nconst host = ts.createCompilerHost(options);\nconst read = host.readFile.bind(host);\nhost.readFile = file => {\n if (file === filename) return source;\n let text = before.has(file) ? before.get(file) : read(file);\n if (state === 'single-tag' && file === path.join(root, 'src/types/content/common.ts')) {\n text = text.replace('Exclude', '\"div\"');\n }\n return text;\n};\nif (process.env.TRACE_DIR) ts.startTracing('project', process.env.TRACE_DIR);\nconst start = performance.now();\nconst program = ts.createProgram([filename, path.join(root, 'src/cli/virtual/virtual.d.ts')], options, host);\nconst created = performance.now();\nconst diagnostics = ts.getPreEmitDiagnostics(program);\nconst result = {state, createSeconds: (created-start)/1000, checkSeconds: (performance.now()-created)/1000, types: program.getTypeCount(), instantiations: program.getInstantiationCount(), diagnostics: diagnostics.map(d => ts.flattenDiagnosticMessageText(d.messageText, '\\n'))};\nif (ts.tracing) ts.tracing.stopTracing();\nconsole.log(JSON.stringify(result));\nprocess.exitCode = diagnostics.length ? 1 : 0;\n", + "runs": [ + { + "state": "before", + "createSeconds": 0.340453375, + "checkSeconds": 4.2901852090000006, + "types": 32827, + "instantiations": 519633, + "diagnostics": [], + "warmup": true + }, + { + "state": "after", + "createSeconds": 0.343325333, + "checkSeconds": 1.229511334, + "types": 32020, + "instantiations": 408563, + "diagnostics": [], + "warmup": true + }, + { + "state": "before", + "createSeconds": 0.344232667, + "checkSeconds": 4.200363708, + "types": 32827, + "instantiations": 519633, + "diagnostics": [], + "warmup": false + }, + { + "state": "after", + "createSeconds": 0.362251417, + "checkSeconds": 1.225805791, + "types": 32020, + "instantiations": 408563, + "diagnostics": [], + "warmup": false + }, + { + "state": "before", + "createSeconds": 0.3462235000000001, + "checkSeconds": 4.053631041, + "types": 32827, + "instantiations": 519633, + "diagnostics": [], + "warmup": false + }, + { + "state": "after", + "createSeconds": 0.349394875, + "checkSeconds": 1.1200603329999999, + "types": 32020, + "instantiations": 408563, + "diagnostics": [], + "warmup": false + }, + { + "state": "before", + "createSeconds": 0.341954542, + "checkSeconds": 4.284888, + "types": 32827, + "instantiations": 519633, + "diagnostics": [], + "warmup": false + }, + { + "state": "after", + "createSeconds": 0.39237950000000005, + "checkSeconds": 1.331300375, + "types": 32020, + "instantiations": 408563, + "diagnostics": [], + "warmup": false + }, + { + "state": "single-tag", + "createSeconds": 0.3891805, + "checkSeconds": 1.082017625, + "types": 30735, + "instantiations": 334384, + "diagnostics": [] + } + ], + "finalTraceExpressions": [] + }, + "testRuns": [ + { + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "build", + "types", + "--json", + "--outputFile=/tmp/relay-test-benchmark/build-types-0.json" + ], + "warmup": true, + "wallSeconds": 23.730027416953817, + "exitCode": 0, + "suites": 37, + "tests": 285, + "files": [ + { + "path": "tests/integration/types/registries.integration.test.ts", + "seconds": 6.833 + }, + { + "path": "tests/integration/build/locale/chunks-threshold.integration.test.ts", + "seconds": 7.035 + }, + { + "path": "tests/integration/build/locale/chunks.integration.test.ts", + "seconds": 7.115 + }, + { + "path": "tests/integration/build/locale/locale.integration.test.ts", + "seconds": 7.479 + }, + { + "path": "tests/integration/build/options/options.integration.test.ts", + "seconds": 8.113 + }, + { + "path": "tests/integration/build/locale/dynamic.integration.test.ts", + "seconds": 8.75 + }, + { + "path": "tests/integration/build/isolation/isolation-watch.integration.test.ts", + "seconds": 10.237 + }, + { + "path": "src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts", + "seconds": 3.554 + }, + { + "path": "src/cli/index.test.ts", + "seconds": 3.185 + }, + { + "path": "tests/integration/types/relay.integration.test.ts", + "seconds": 4.767 + }, + { + "path": "src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts", + "seconds": 3.326 + }, + { + "path": "tests/integration/types/content.integration.test.ts", + "seconds": 5.044 + }, + { + "path": "tests/integration/build/relay-runtime/relay-runtime.integration.test.ts", + "seconds": 5.992 + }, + { + "path": "tests/integration/build/permissions/permissions.integration.test.ts", + "seconds": 3.375 + }, + { + "path": "tests/integration/types/locale.integration.test.ts", + "seconds": 1.795 + }, + { + "path": "tests/integration/build/style-routing/style-routing.integration.test.ts", + "seconds": 1.974 + }, + { + "path": "tests/integration/build/style-routing/delivery-spike.integration.test.ts", + "seconds": 2.013 + }, + { + "path": "src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts", + "seconds": 1.522 + }, + { + "path": "tests/integration/types/view.integration.test.ts", + "seconds": 3.364 + }, + { + "path": "tests/integration/build/style-routing/selective-split.integration.test.ts", + "seconds": 0.861 + }, + { + "path": "src/cli/virtual/virtual.test.ts", + "seconds": 0.635 + }, + { + "path": "tests/integration/build/style-routing/content-chunks.integration.test.ts", + "seconds": 0.386 + }, + { + "path": "src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts", + "seconds": 0.333 + }, + { + "path": "src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts", + "seconds": 0.148 + }, + { + "path": "tests/integration/build/style-routing/routing-spike.integration.test.ts", + "seconds": 0.58 + }, + { + "path": "tests/integration/build/content/content-api.integration.test.ts", + "seconds": 0.059 + }, + { + "path": "src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts", + "seconds": 0.154 + }, + { + "path": "tests/integration/build/locale/chunks-disabled.integration.test.ts", + "seconds": 0.894 + }, + { + "path": "src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts", + "seconds": 0.046 + }, + { + "path": "src/cli/bundler/plugins/resource-access/utils.test.ts", + "seconds": 0.035 + }, + { + "path": "src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts", + "seconds": 0.117 + }, + { + "path": "tests/build-output.test.ts", + "seconds": 0.046 + }, + { + "path": "src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts", + "seconds": 0.044 + }, + { + "path": "src/cli/bundler/plugins/utils/runtime-template.test.ts", + "seconds": 0.029 + }, + { + "path": "src/cli/plugins/content/RelayDeclaration.test.ts", + "seconds": 3.824 + }, + { + "path": "tests/integration/build/virtual-modules/virtual-modules.integration.test.ts", + "seconds": 1.548 + }, + { + "path": "tests/integration/build/override/override.integration.test.ts", + "seconds": 23.016 + } + ] + }, + { + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "build", + "types", + "--json", + "--outputFile=/tmp/relay-test-benchmark/build-types-1.json" + ], + "warmup": false, + "wallSeconds": 23.09723254200071, + "exitCode": 0, + "suites": 37, + "tests": 285, + "files": [ + { + "path": "tests/integration/build/locale/chunks-threshold.integration.test.ts", + "seconds": 5.985 + }, + { + "path": "tests/integration/build/locale/chunks.integration.test.ts", + "seconds": 6.085 + }, + { + "path": "tests/integration/types/registries.integration.test.ts", + "seconds": 6.215 + }, + { + "path": "tests/integration/build/locale/locale.integration.test.ts", + "seconds": 6.788 + }, + { + "path": "tests/integration/build/options/options.integration.test.ts", + "seconds": 7.024 + }, + { + "path": "tests/integration/build/locale/dynamic.integration.test.ts", + "seconds": 7.872 + }, + { + "path": "tests/integration/build/isolation/isolation-watch.integration.test.ts", + "seconds": 10.056 + }, + { + "path": "src/cli/plugins/content/RelayDeclaration.test.ts", + "seconds": 4.038 + }, + { + "path": "src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts", + "seconds": 3.853 + }, + { + "path": "tests/integration/types/relay.integration.test.ts", + "seconds": 4.815 + }, + { + "path": "tests/integration/build/permissions/permissions.integration.test.ts", + "seconds": 3.301 + }, + { + "path": "tests/integration/types/content.integration.test.ts", + "seconds": 5.395 + }, + { + "path": "tests/integration/build/relay-runtime/relay-runtime.integration.test.ts", + "seconds": 6.22 + }, + { + "path": "tests/integration/build/style-routing/style-routing.integration.test.ts", + "seconds": 2.129 + }, + { + "path": "tests/integration/build/style-routing/delivery-spike.integration.test.ts", + "seconds": 2.507 + }, + { + "path": "tests/integration/types/locale.integration.test.ts", + "seconds": 2.124 + }, + { + "path": "tests/integration/build/virtual-modules/virtual-modules.integration.test.ts", + "seconds": 1.751 + }, + { + "path": "tests/integration/types/view.integration.test.ts", + "seconds": 4.123 + }, + { + "path": "src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts", + "seconds": 3.435 + }, + { + "path": "tests/integration/build/locale/chunks-disabled.integration.test.ts", + "seconds": 0.904 + }, + { + "path": "src/cli/index.test.ts", + "seconds": 3.597 + }, + { + "path": "tests/integration/build/style-routing/selective-split.integration.test.ts", + "seconds": 0.927 + }, + { + "path": "src/cli/virtual/virtual.test.ts", + "seconds": 0.693 + }, + { + "path": "tests/integration/build/style-routing/content-chunks.integration.test.ts", + "seconds": 0.401 + }, + { + "path": "src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts", + "seconds": 0.123 + }, + { + "path": "src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts", + "seconds": 0.181 + }, + { + "path": "src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts", + "seconds": 1.408 + }, + { + "path": "src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts", + "seconds": 0.05 + }, + { + "path": "tests/integration/build/content/content-api.integration.test.ts", + "seconds": 0.06 + }, + { + "path": "src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts", + "seconds": 0.286 + }, + { + "path": "tests/build-output.test.ts", + "seconds": 0.059 + }, + { + "path": "tests/integration/build/style-routing/routing-spike.integration.test.ts", + "seconds": 0.566 + }, + { + "path": "src/cli/bundler/plugins/utils/runtime-template.test.ts", + "seconds": 0.029 + }, + { + "path": "src/cli/bundler/plugins/resource-access/utils.test.ts", + "seconds": 0.034 + }, + { + "path": "src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts", + "seconds": 0.048 + }, + { + "path": "src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts", + "seconds": 0.147 + }, + { + "path": "tests/integration/build/override/override.integration.test.ts", + "seconds": 22.53 + } + ] + }, + { + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "build", + "types", + "--json", + "--outputFile=/tmp/relay-test-benchmark/build-types-2.json" + ], + "warmup": false, + "wallSeconds": 22.268417458981276, + "exitCode": 0, + "suites": 37, + "tests": 285, + "files": [ + { + "path": "tests/integration/build/relay-runtime/relay-runtime.integration.test.ts", + "seconds": 5.759 + }, + { + "path": "tests/integration/build/locale/chunks.integration.test.ts", + "seconds": 6.342 + }, + { + "path": "tests/integration/types/registries.integration.test.ts", + "seconds": 6.389 + }, + { + "path": "tests/integration/build/locale/locale.integration.test.ts", + "seconds": 6.961 + }, + { + "path": "tests/integration/build/options/options.integration.test.ts", + "seconds": 7.254 + }, + { + "path": "tests/integration/build/locale/dynamic.integration.test.ts", + "seconds": 8.03 + }, + { + "path": "tests/integration/build/isolation/isolation-watch.integration.test.ts", + "seconds": 9.876 + }, + { + "path": "tests/integration/types/view.integration.test.ts", + "seconds": 3.486 + }, + { + "path": "tests/integration/types/relay.integration.test.ts", + "seconds": 4.685 + }, + { + "path": "src/cli/plugins/content/RelayDeclaration.test.ts", + "seconds": 3.878 + }, + { + "path": "tests/integration/types/content.integration.test.ts", + "seconds": 5.083 + }, + { + "path": "src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts", + "seconds": 3.687 + }, + { + "path": "tests/integration/build/locale/chunks-threshold.integration.test.ts", + "seconds": 6.316 + }, + { + "path": "tests/integration/build/style-routing/delivery-spike.integration.test.ts", + "seconds": 1.875 + }, + { + "path": "src/cli/index.test.ts", + "seconds": 3.158 + }, + { + "path": "tests/integration/build/style-routing/style-routing.integration.test.ts", + "seconds": 1.637 + }, + { + "path": "src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts", + "seconds": 2.756 + }, + { + "path": "tests/integration/types/locale.integration.test.ts", + "seconds": 1.847 + }, + { + "path": "tests/integration/build/virtual-modules/virtual-modules.integration.test.ts", + "seconds": 1.509 + }, + { + "path": "src/cli/virtual/virtual.test.ts", + "seconds": 0.603 + }, + { + "path": "tests/integration/build/locale/chunks-disabled.integration.test.ts", + "seconds": 0.847 + }, + { + "path": "src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts", + "seconds": 0.129 + }, + { + "path": "tests/integration/build/style-routing/content-chunks.integration.test.ts", + "seconds": 0.474 + }, + { + "path": "tests/integration/build/style-routing/selective-split.integration.test.ts", + "seconds": 1.034 + }, + { + "path": "tests/integration/build/style-routing/routing-spike.integration.test.ts", + "seconds": 0.508 + }, + { + "path": "src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts", + "seconds": 0.276 + }, + { + "path": "tests/build-output.test.ts", + "seconds": 0.043 + }, + { + "path": "tests/integration/build/content/content-api.integration.test.ts", + "seconds": 0.067 + }, + { + "path": "src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts", + "seconds": 0.06 + }, + { + "path": "src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts", + "seconds": 0.12 + }, + { + "path": "src/cli/bundler/plugins/resource-access/utils.test.ts", + "seconds": 0.038 + }, + { + "path": "src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts", + "seconds": 0.057 + }, + { + "path": "src/cli/bundler/plugins/utils/runtime-template.test.ts", + "seconds": 0.035 + }, + { + "path": "src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts", + "seconds": 0.136 + }, + { + "path": "tests/integration/build/permissions/permissions.integration.test.ts", + "seconds": 3.137 + }, + { + "path": "src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts", + "seconds": 1.214 + }, + { + "path": "tests/integration/build/override/override.integration.test.ts", + "seconds": 21.686 + } + ] + }, + { + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "build", + "types", + "--json", + "--outputFile=/tmp/relay-test-benchmark/build-types-3.json" + ], + "warmup": false, + "wallSeconds": 22.588490209076554, + "exitCode": 0, + "suites": 37, + "tests": 285, + "files": [ + { + "path": "tests/integration/build/locale/chunks-threshold.integration.test.ts", + "seconds": 6.058 + }, + { + "path": "tests/integration/build/locale/chunks.integration.test.ts", + "seconds": 6.167 + }, + { + "path": "tests/integration/types/registries.integration.test.ts", + "seconds": 6.187 + }, + { + "path": "tests/integration/build/locale/locale.integration.test.ts", + "seconds": 6.936 + }, + { + "path": "tests/integration/build/options/options.integration.test.ts", + "seconds": 7.032 + }, + { + "path": "tests/integration/build/locale/dynamic.integration.test.ts", + "seconds": 7.823 + }, + { + "path": "tests/integration/build/isolation/isolation-watch.integration.test.ts", + "seconds": 9.936 + }, + { + "path": "src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts", + "seconds": 3.503 + }, + { + "path": "tests/integration/types/relay.integration.test.ts", + "seconds": 4.455 + }, + { + "path": "src/cli/plugins/content/RelayDeclaration.test.ts", + "seconds": 3.742 + }, + { + "path": "tests/integration/types/content.integration.test.ts", + "seconds": 5.043 + }, + { + "path": "tests/integration/types/view.integration.test.ts", + "seconds": 3.518 + }, + { + "path": "tests/integration/build/relay-runtime/relay-runtime.integration.test.ts", + "seconds": 5.799 + }, + { + "path": "tests/integration/build/style-routing/delivery-spike.integration.test.ts", + "seconds": 2.165 + }, + { + "path": "tests/integration/build/style-routing/style-routing.integration.test.ts", + "seconds": 1.766 + }, + { + "path": "tests/integration/types/locale.integration.test.ts", + "seconds": 1.998 + }, + { + "path": "src/cli/index.test.ts", + "seconds": 3.356 + }, + { + "path": "tests/integration/build/virtual-modules/virtual-modules.integration.test.ts", + "seconds": 1.677 + }, + { + "path": "src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts", + "seconds": 3.064 + }, + { + "path": "tests/integration/build/permissions/permissions.integration.test.ts", + "seconds": 3.377 + }, + { + "path": "tests/integration/build/style-routing/selective-split.integration.test.ts", + "seconds": 0.826 + }, + { + "path": "src/cli/virtual/virtual.test.ts", + "seconds": 0.745 + }, + { + "path": "src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts", + "seconds": 0.145 + }, + { + "path": "tests/integration/build/locale/chunks-disabled.integration.test.ts", + "seconds": 0.897 + }, + { + "path": "tests/integration/build/style-routing/content-chunks.integration.test.ts", + "seconds": 0.402 + }, + { + "path": "tests/integration/build/style-routing/routing-spike.integration.test.ts", + "seconds": 0.609 + }, + { + "path": "tests/integration/build/content/content-api.integration.test.ts", + "seconds": 0.058 + }, + { + "path": "src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts", + "seconds": 0.053 + }, + { + "path": "src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts", + "seconds": 0.113 + }, + { + "path": "src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts", + "seconds": 0.057 + }, + { + "path": "src/cli/bundler/plugins/resource-access/utils.test.ts", + "seconds": 0.033 + }, + { + "path": "tests/build-output.test.ts", + "seconds": 0.038 + }, + { + "path": "src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts", + "seconds": 0.188 + }, + { + "path": "src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts", + "seconds": 1.406 + }, + { + "path": "src/cli/bundler/plugins/utils/runtime-template.test.ts", + "seconds": 0.041 + }, + { + "path": "src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts", + "seconds": 0.332 + }, + { + "path": "tests/integration/build/override/override.integration.test.ts", + "seconds": 22.009 + } + ] + }, + { + "name": "check", + "command": [ + "npm", + "run", + "check" + ], + "wallSeconds": 42.11238812515512, + "exitCode": 0 + }, + { + "name": "full", + "command": [ + "npm", + "test", + "--", + "--json", + "--outputFile=/tmp/relay-test-benchmark/full.json" + ], + "wallSeconds": 52.32188737508841, + "exitCode": 0 + } + ], + "fullRun": { + "suites": 203, + "tests": 2003, + "passed": true, + "files": [ + { + "path": "tests/integration/build/relay-runtime/relay-runtime.integration.test.ts", + "seconds": 5.953 + }, + { + "path": "tests/integration/build/locale/chunks.integration.test.ts", + "seconds": 6.54 + }, + { + "path": "tests/integration/build/options/options.integration.test.ts", + "seconds": 7.711 + }, + { + "path": "tests/integration/build/locale/dynamic.integration.test.ts", + "seconds": 8.663 + }, + { + "path": "tests/integration/build/isolation/isolation-watch.integration.test.ts", + "seconds": 10.083 + }, + { + "path": "tests/integration/browser/options/options.integration.test.ts", + "seconds": 3.9 + }, + { + "path": "tests/integration/browser/override/override.integration.test.ts", + "seconds": 10.587 + }, + { + "path": "tests/integration/browser/relay/scripting.firefox.integration.test.ts", + "seconds": 11.611 + }, + { + "path": "tests/integration/build/locale/chunks-threshold.integration.test.ts", + "seconds": 8.195 + }, + { + "path": "tests/integration/types/content.integration.test.ts", + "seconds": 6.017 + }, + { + "path": "tests/integration/types/registries.integration.test.ts", + "seconds": 8.216 + }, + { + "path": "tests/integration/browser/content/relay-styles.firefox.integration.test.ts", + "seconds": 8.182 + }, + { + "path": "tests/integration/browser/content/frame-pages.firefox.integration.test.ts", + "seconds": 9.134 + }, + { + "path": "tests/integration/types/relay.integration.test.ts", + "seconds": 5.876 + }, + { + "path": "tests/integration/build/locale/locale.integration.test.ts", + "seconds": 9.449 + }, + { + "path": "tests/integration/browser/content/isolation-iframe.firefox.integration.test.ts", + "seconds": 11.639 + }, + { + "path": "tests/integration/browser/content/frame-pages.integration.test.ts", + "seconds": 5.206 + }, + { + "path": "tests/integration/browser/content/entrypoint-assets.firefox.integration.test.ts", + "seconds": 8.36 + }, + { + "path": "tests/integration/browser/relay/scripting.integration.test.ts", + "seconds": 10.27 + }, + { + "path": "src/cli/entrypoint/parser/ContentParser.test.ts", + "seconds": 5.061 + }, + { + "path": "tests/integration/browser/content/isolation-shadow.integration.test.ts", + "seconds": 4.344 + }, + { + "path": "tests/integration/browser/content/render-props.integration.test.ts", + "seconds": 3.962 + }, + { + "path": "src/cli/plugins/content/RelayDeclaration.test.ts", + "seconds": 5.233 + }, + { + "path": "tests/integration/browser/content/isolation-iframe.integration.test.ts", + "seconds": 4.168 + }, + { + "path": "tests/integration/browser/content/render-props.firefox.integration.test.ts", + "seconds": 5.433 + }, + { + "path": "tests/integration/browser/content/isolation-shadow-mv2.firefox.integration.test.ts", + "seconds": 5.186 + }, + { + "path": "tests/integration/build/override/override.integration.test.ts", + "seconds": 29.868 + }, + { + "path": "src/cli/bundler/plugins/build-assets-map/BuildAssetsMapPlugin.test.ts", + "seconds": 5.422 + }, + { + "path": "tests/integration/browser/content/selective-split.firefox.integration.test.ts", + "seconds": 5.901 + }, + { + "path": "src/cli/plugins/locale/declaration/LocaleDeclaration.test.ts", + "seconds": 4.862 + }, + { + "path": "tests/integration/browser/content/isolation-spike.firefox.integration.test.ts", + "seconds": 5.377 + }, + { + "path": "tests/integration/types/view.integration.test.ts", + "seconds": 3.927 + }, + { + "path": "tests/integration/browser/locale/dynamic.firefox.integration.test.ts", + "seconds": 3.719 + }, + { + "path": "src/cli/entrypoint/file/ExpressionFile.test.ts", + "seconds": 3.673 + }, + { + "path": "tests/integration/build/permissions/permissions.integration.test.ts", + "seconds": 4.759 + }, + { + "path": "tests/integration/browser/locale/chunks.firefox.integration.test.ts", + "seconds": 3.761 + }, + { + "path": "src/cli/index.test.ts", + "seconds": 4.507 + }, + { + "path": "tests/integration/browser/content/isolation-shadow-mv3.firefox.integration.test.ts", + "seconds": 6.769 + }, + { + "path": "tests/integration/browser/offscreen/service.integration.test.ts", + "seconds": 4.385 + }, + { + "path": "tests/integration/browser/locale/chunks.integration.test.ts", + "seconds": 7.521 + }, + { + "path": "tests/integration/browser/locale/dollars.firefox.integration.test.ts", + "seconds": 6.214 + }, + { + "path": "tests/integration/types/locale.integration.test.ts", + "seconds": 5.119 + }, + { + "path": "tests/integration/build/style-routing/delivery-spike.integration.test.ts", + "seconds": 6.45 + }, + { + "path": "tests/integration/browser/locale/dynamic.integration.test.ts", + "seconds": 5.434 + }, + { + "path": "tests/integration/utils/process.test.ts", + "seconds": 2.537 + }, + { + "path": "tests/integration/browser/content/entrypoint-assets.integration.test.ts", + "seconds": 4.694 + }, + { + "path": "tests/integration/browser/content/css-fonts.firefox.integration.test.ts", + "seconds": 8.136 + }, + { + "path": "tests/integration/browser/content/isolation-spike.integration.test.ts", + "seconds": 2.335 + }, + { + "path": "tests/integration/browser/content/selective-split.integration.test.ts", + "seconds": 2.94 + }, + { + "path": "tests/integration/browser/locale/dollars.integration.test.ts", + "seconds": 3.321 + }, + { + "path": "tests/integration/browser/view/render.integration.test.ts", + "seconds": 4.25 + }, + { + "path": "tests/integration/browser/content/css-fonts.integration.test.ts", + "seconds": 1.423 + }, + { + "path": "tests/integration/build/style-routing/style-routing.integration.test.ts", + "seconds": 4.108 + }, + { + "path": "tests/release-it.test.ts", + "seconds": 2.109 + }, + { + "path": "tests/main-source-policy.test.ts", + "seconds": 0.741 + }, + { + "path": "tests/integration/build/style-routing/selective-split.integration.test.ts", + "seconds": 1.647 + }, + { + "path": "tests/integration/build/locale/chunks-disabled.integration.test.ts", + "seconds": 1.609 + }, + { + "path": "tests/integration/build/virtual-modules/virtual-modules.integration.test.ts", + "seconds": 3.14 + }, + { + "path": "tests/integration/browser/content/relay-styles.integration.test.ts", + "seconds": 3.152 + }, + { + "path": "src/cli/virtual/virtual.test.ts", + "seconds": 0.854 + }, + { + "path": "tests/integration/build/style-routing/routing-spike.integration.test.ts", + "seconds": 0.669 + }, + { + "path": "src/cli/builders/manifest/Manifest.test.ts", + "seconds": 0.713 + }, + { + "path": "src/cli/plugins/locale/Locale.test.ts", + "seconds": 0.436 + }, + { + "path": "src/entry/content/adapters/vanilla/Builder.test.ts", + "seconds": 0.511 + }, + { + "path": "tests/integration/build/style-routing/content-chunks.integration.test.ts", + "seconds": 0.273 + }, + { + "path": "src/cli/entrypoint/finder/LocaleFinder.test.ts", + "seconds": 0.515 + }, + { + "path": "src/entry/content/lifecycle/nodes/RenderNode.test.ts", + "seconds": 0.966 + }, + { + "path": "src/cli/bundler/plugins/isolated-styles/IsolatedStylesPlugin.test.ts", + "seconds": 3.44 + }, + { + "path": "src/cli/bundler/plugins/generate-module/GenerateModulePlugin.test.ts", + "seconds": 0.328 + }, + { + "path": "src/relay/providers/RegisterRelay.test.ts", + "seconds": 0.09 + }, + { + "path": "src/cli/plugins/locale/index.test.ts", + "seconds": 0.25 + }, + { + "path": "src/cli/plugins/override/Override.test.ts", + "seconds": 0.263 + }, + { + "path": "src/service/providers/RegisterService.test.ts", + "seconds": 0.06 + }, + { + "path": "src/relay/providers/ProxyRelay.test.ts", + "seconds": 0.255 + }, + { + "path": "src/cli/entrypoint/file/OptionFile.test.ts", + "seconds": 0.297 + }, + { + "path": "src/locale/adapters/react/hooks/use-locale.test.ts", + "seconds": 0.406 + }, + { + "path": "tests/integration/build/content/content-api.integration.test.ts", + "seconds": 0.092 + }, + { + "path": "src/relay/adapters/RelayScriptingAdapter.test.ts", + "seconds": 0.175 + }, + { + "path": "src/entry/content/lifecycle/nodes/FrameNode.test.ts", + "seconds": 0.132 + }, + { + "path": "tests/release-automation.test.ts", + "seconds": 0.216 + }, + { + "path": "src/entry/sandbox/index.test.ts", + "seconds": 0.123 + }, + { + "path": "src/cli/bundler/plugins/entrypoint/EntrypointPlugin.test.ts", + "seconds": 0.055 + }, + { + "path": "src/cli/entrypoint/finder/PluginFinder.test.ts", + "seconds": 0.067 + }, + { + "path": "src/cli/bundler/plugins/manifest/ManifestPlugin.test.ts", + "seconds": 0.236 + }, + { + "path": "src/offscreen/OffscreenBridge.test.ts", + "seconds": 0.168 + }, + { + "path": "src/locale/adapters/react/hooks/use-native-locale.test.ts", + "seconds": 0.233 + }, + { + "path": "src/message/adapters/react/use-message-handler.test.ts", + "seconds": 0.157 + }, + { + "path": "src/message/message-response.test.ts", + "seconds": 0.206 + }, + { + "path": "src/entry/relay/Builder.test.ts", + "seconds": 0.1 + }, + { + "path": "src/cli/bundler/plugins/resource-access/ResourceAccessPlugin.test.ts", + "seconds": 0.128 + }, + { + "path": "src/entry/view/adapters/react/Builder.test.ts", + "seconds": 0.112 + }, + { + "path": "src/cli/entrypoint/finder/AbstractOverrideFinder.test.ts", + "seconds": 0.248 + }, + { + "path": "src/cli/entrypoint/parser/SandboxParser.test.ts", + "seconds": 0.157 + }, + { + "path": "src/entry/content/adapters/react/Builder.test.ts", + "seconds": 0.122 + }, + { + "path": "src/entry/content/lifecycle/MountBuilder.test.ts", + "seconds": 0.154 + }, + { + "path": "src/message/providers/Message.test.ts", + "seconds": 0.174 + }, + { + "path": "src/cli/bundler/plugins/generate-json/GenerateJsonPlugin.test.ts", + "seconds": 0.148 + }, + { + "path": "src/sandbox/providers/Sandbox.test.ts", + "seconds": 0.053 + }, + { + "path": "src/cli/entrypoint/parser/override-parsers.test.ts", + "seconds": 0.126 + }, + { + "path": "src/entry/content/lifecycle/context/ContainerRegistry.test.ts", + "seconds": 0.068 + }, + { + "path": "src/entry/content/resolvers/watch.test.ts", + "seconds": 0.135 + }, + { + "path": "src/cli/entrypoint/parser/RelayParser.test.ts", + "seconds": 0.151 + }, + { + "path": "src/cli/builders/manifest/utils/host-patterns.test.ts", + "seconds": 0.114 + }, + { + "path": "src/entry/content/lifecycle/markers/marker.test.ts", + "seconds": 0.156 + }, + { + "path": "src/cli/plugins/style/utils.test.ts", + "seconds": 0.241 + }, + { + "path": "src/cli/entrypoint/parser/BackgroundParser.test.ts", + "seconds": 0.097 + }, + { + "path": "tests/browser-harness.test.ts", + "seconds": 0.169 + }, + { + "path": "src/cli/entrypoint/finder/CommandFinder.test.ts", + "seconds": 0.111 + }, + { + "path": "src/entry/content/lifecycle/nodes/MountNode.test.ts", + "seconds": 0.085 + }, + { + "path": "src/cli/builders/manifest/ManifestV3.test.ts", + "seconds": 0.138 + }, + { + "path": "src/cli/entrypoint/name/NameGenerator.test.ts", + "seconds": 0.084 + }, + { + "path": "src/cli/entrypoint/parser/PopupParser.test.ts", + "seconds": 0.121 + }, + { + "path": "src/cli/entrypoint/parser/AbstractParser.test.ts", + "seconds": 0.156 + }, + { + "path": "src/cli/entrypoint/parser/SidebarParser.test.ts", + "seconds": 0.125 + }, + { + "path": "src/locale/observers/ObservableLocale.test.ts", + "seconds": 0.146 + }, + { + "path": "src/cli/entrypoint/parser/ServiceParser.test.ts", + "seconds": 0.122 + }, + { + "path": "src/offscreen/providers/ProxyOffscreen.test.ts", + "seconds": 0.119 + }, + { + "path": "src/cli/entrypoint/parser/OptionsParser.test.ts", + "seconds": 0.128 + }, + { + "path": "src/cli/entrypoint/parser/OffscreenParser.test.ts", + "seconds": 0.129 + }, + { + "path": "src/cli/plugins/content/ContentManager.test.ts", + "seconds": 0.256 + }, + { + "path": "src/entry/core/react.test.ts", + "seconds": 0.081 + }, + { + "path": "src/service/providers/ProxyService.test.ts", + "seconds": 0.096 + }, + { + "path": "src/cli/plugins/meta/Author.test.ts", + "seconds": 0.112 + }, + { + "path": "src/locale/adapters/react/ReactLocale.test.ts", + "seconds": 0.084 + }, + { + "path": "src/cli/plugins/content/RelayDriver.test.ts", + "seconds": 0.14 + }, + { + "path": "src/entry/content/lifecycle/IsolationSetup.test.ts", + "seconds": 0.079 + }, + { + "path": "src/cli/plugins/meta/SpecificSettings.test.ts", + "seconds": 0.109 + }, + { + "path": "src/cli/builders/manifest/ManifestV2.test.ts", + "seconds": 0.104 + }, + { + "path": "src/locale/providers/DynamicLocale.test.ts", + "seconds": 0.141 + }, + { + "path": "src/entry/content/lifecycle/nodes/ShadowNode.test.ts", + "seconds": 0.097 + }, + { + "path": "src/cli/entrypoint/parser/PageParser.test.ts", + "seconds": 0.091 + }, + { + "path": "src/cli/plugins/meta/Incognito.test.ts", + "seconds": 0.099 + }, + { + "path": "src/cli/plugins/view/View.test.ts", + "seconds": 0.155 + }, + { + "path": "src/cli/resolvers/config.test.ts", + "seconds": 0.08 + }, + { + "path": "src/locale/adapters/react/hooks/use-locale-attributes.test.ts", + "seconds": 0.123 + }, + { + "path": "src/cli/plugins/meta/Homepage.test.ts", + "seconds": 0.095 + }, + { + "path": "src/shared/content/isolation.test.ts", + "seconds": 0.135 + }, + { + "path": "src/cli/builders/manifest/utils/web-accessible-resources.test.ts", + "seconds": 0.15 + }, + { + "path": "src/cli/entrypoint/finder/OptionsFinder.test.ts", + "seconds": 0.089 + }, + { + "path": "src/cli/entrypoint/finder/ViewPermissionsFinder.test.ts", + "seconds": 0.203 + }, + { + "path": "src/cli/plugins/options/Options.test.ts", + "seconds": 0.085 + }, + { + "path": "src/cli/builders/manifest/utils/data-collection-permissions.test.ts", + "seconds": 0.055 + }, + { + "path": "src/frame/ReadyFrame.test.ts", + "seconds": 0.089 + }, + { + "path": "src/cli/entrypoint/finder/PageFinder.test.ts", + "seconds": 0.082 + }, + { + "path": "src/locale/providers/NativeLocale.test.ts", + "seconds": 0.05 + }, + { + "path": "src/cli/entrypoint/parser/CommandParser.test.ts", + "seconds": 0.09 + }, + { + "path": "src/entry/relay/index.test.ts", + "seconds": 0.072 + }, + { + "path": "src/locale/storage/LocaleStorage.test.ts", + "seconds": 0.079 + }, + { + "path": "src/locale/providers/CustomLocale.test.ts", + "seconds": 0.083 + }, + { + "path": "src/entry/offscreen/index.test.ts", + "seconds": 0.11 + }, + { + "path": "src/entry/view/resolvers/definition.test.ts", + "seconds": 0.067 + }, + { + "path": "src/entry/transport/resolvers/definition.test.ts", + "seconds": 0.053 + }, + { + "path": "src/entry/view/adapters/vanilla/Builder.test.ts", + "seconds": 0.057 + }, + { + "path": "src/entry/core/render.test.ts", + "seconds": 0.074 + }, + { + "path": "src/entry/command/resolvers/definition.test.ts", + "seconds": 0.056 + }, + { + "path": "src/cli/entrypoint/finder/AbstractEntrypointFinder.test.ts", + "seconds": 0.047 + }, + { + "path": "src/shared/locale/messages.test.ts", + "seconds": 0.053 + }, + { + "path": "src/cli/plugins/dotenv/utils.test.ts", + "seconds": 0.044 + }, + { + "path": "src/entry/background/resolvers/definition.test.ts", + "seconds": 0.047 + }, + { + "path": "src/cli/entrypoint/finder/AbstractViewFinder.test.ts", + "seconds": 0.06 + }, + { + "path": "src/entry/content/adapters/vanilla/definition.test.ts", + "seconds": 0.038 + }, + { + "path": "src/sandbox/SandboxMessage.test.ts", + "seconds": 0.057 + }, + { + "path": "tests/integration/browser/utils/browser.test.ts", + "seconds": 0.052 + }, + { + "path": "src/message/error.test.ts", + "seconds": 0.045 + }, + { + "path": "src/cli/builders/locale/LocaleBuilder.test.ts", + "seconds": 0.069 + }, + { + "path": "src/locale/helpers.test.ts", + "seconds": 0.047 + }, + { + "path": "tests/build-output.test.ts", + "seconds": 0.054 + }, + { + "path": "src/entry/view/adapters/vanilla/definition.test.ts", + "seconds": 0.046 + }, + { + "path": "src/cli/plugins/content/utils.test.ts", + "seconds": 0.07 + }, + { + "path": "src/cli/plugins/typescript/declaration/transport/TransportDeclaration.test.ts", + "seconds": 0.05 + }, + { + "path": "src/cli/entrypoint/finder/PopupFinder.test.ts", + "seconds": 0.053 + }, + { + "path": "src/cli/bundler/plugins/chunk-loader/ChunkLoaderPlugin.test.ts", + "seconds": 0.053 + }, + { + "path": "src/entry/content/resolvers/target.test.ts", + "seconds": 0.136 + }, + { + "path": "src/cli/entrypoint/finder/ViewCspFinder.test.ts", + "seconds": 0.038 + }, + { + "path": "src/cli/builders/locale/LocaleStructureValidator.test.ts", + "seconds": 0.069 + }, + { + "path": "src/entry/content/adapters/react/definition.test.ts", + "seconds": 0.06 + }, + { + "path": "src/entry/content/resolvers/definition.test.ts", + "seconds": 0.056 + }, + { + "path": "src/relay/discovery/RelayDiscovery.test.ts", + "seconds": 0.046 + }, + { + "path": "src/cli/entrypoint/parser/schemas/permissions.test.ts", + "seconds": 0.056 + }, + { + "path": "src/main/relay.test.ts", + "seconds": 0.05 + }, + { + "path": "src/cli/bundler/plugins/resource-access/utils.test.ts", + "seconds": 0.045 + }, + { + "path": "tests/integration/browser/utils/site.test.ts", + "seconds": 0.038 + }, + { + "path": "src/transport/RegisterTransport.test.ts", + "seconds": 0.04 + }, + { + "path": "src/offscreen/providers/RegisterOffscreen.test.ts", + "seconds": 0.048 + }, + { + "path": "src/entry/view/adapters/react/definition.test.ts", + "seconds": 0.062 + }, + { + "path": "src/cli/builders/manifest/utils/permissions.test.ts", + "seconds": 0.046 + }, + { + "path": "src/entry/content/adapters/react/Node.test.ts", + "seconds": 0.083 + }, + { + "path": "src/cli/bundler/plugins/utils/runtime-template.test.ts", + "seconds": 0.036 + }, + { + "path": "src/entry/content/lifecycle/markers/AttributeMarker.test.ts", + "seconds": 0.04 + }, + { + "path": "src/relay/RelayPermission.test.ts", + "seconds": 0.038 + }, + { + "path": "src/cli/entrypoint/name/InlineNameGenerator.test.ts", + "seconds": 0.042 + }, + { + "path": "src/cli/builders/csp/SandboxCsp.test.ts", + "seconds": 0.037 + }, + { + "path": "src/entry/content/adapters/vanilla/Node.test.ts", + "seconds": 0.034 + }, + { + "path": "src/cli/bundler/utils/app-filename.test.ts", + "seconds": 0.041 + }, + { + "path": "src/cli/builders/csp/Csp.test.ts", + "seconds": 0.033 + }, + { + "path": "src/cli/plugins/content/bundler.test.ts", + "seconds": 0.035 + }, + { + "path": "src/entry/content/lifecycle/context/Context.test.ts", + "seconds": 0.039 + }, + { + "path": "src/entry/content/lifecycle/nodes/isolated-styles.test.ts", + "seconds": 0.043 + }, + { + "path": "src/entry/relay/resolvers/definition.test.ts", + "seconds": 0.036 + }, + { + "path": "src/entry/content/lifecycle/markers/WeakMarker.test.ts", + "seconds": 0.067 + }, + { + "path": "tests/integration/utils/queue.test.ts", + "seconds": 0.031 + }, + { + "path": "tests/browser-harness-dom.test.ts", + "seconds": 0.023 + }, + { + "path": "src/locale/tests/language-names.test.ts", + "seconds": 0.037 + } + ] + }, + "beforeOverrideSeconds": [ + 22.815, 21.953, 22.327 + ], + "afterSourceHashes": { + "src/entry/relay/Builder.ts": "97bb31d1c95db5f867771466be8f4db7ef4f1f5faa3c15c26f2952fda7f99baf", + "src/entry/relay/index.ts": "70e76b237fb09a828a66051b879bf72f5304fe7f60b7817f5566d4c0dd29e0b5", + "src/types/content/definition.ts": "b7c5d5e69f1ffbdde58743657f0b45e68102bff9bf715d43c9402c55e049b88a" + } +} diff --git a/tests/benchmarks/2026-09-25-relay-types.md b/tests/benchmarks/2026-09-25-relay-types.md new file mode 100644 index 00000000..cf232cb8 --- /dev/null +++ b/tests/benchmarks/2026-09-25-relay-types.md @@ -0,0 +1,105 @@ +# Relay type-checking follow-up — 2026-09-25 + +This follows the [migration benchmark](2026-09-25.md). Measurements use the same Apple M3 Max, 16 logical CPUs, +macOS arm64, Node 24.5.0 and TypeScript 5.9.3. Jest retains eight workers and `--no-sparkplug`. +All reported test runs passed. These are local measurements, not Linux/Windows or Node 22 CI results. + +The subsequent [override scheduling follow-up](2026-09-25-override.md) records the split of the remaining long build file. +The measurements here describe the state before that split. + +## Diagnosis and change + +An independent compiler-host probe confirmed that narrowing `ContentScriptContainerTag` to `"div"` in memory reduces +checking of a minimal Relay constructor consumer from about 4.2 seconds to 1.08 seconds. The repository's container type +was not narrowed. The baseline trace placed 3.12 seconds in `checkExpression` for `new entry.Builder(...)`; CPU profiling +also identified type inference and relation checks. Instantiation counts alone do not explain elapsed time. + +The expensive boundary accepted a full `RelayDefinition` only through `RelayUnresolvedDefinition`, a partial mapped type. +`Builder` and the Relay bootstrap function now try a full-definition overload first. This preserves the named definition +for inference; the existing partial-input signature remains last, including its parameter-reflection behavior. +`ContentScriptBuilderConstructor` names the constructor contract shared by these signatures. + +Container tags, tag-specific attributes, callback data and isolation restrictions remain unchanged. TypeScript transpilation +with comments removed produces identical JavaScript for all three changed implementation/contract files. No JIT flags, +worker settings or runtime implementation were changed. + +The declaration test retains source/package × POSIX/Windows coverage for registry augmentation, selectors and exports. +Constructors and bootstrap moved to `types/relay.integration.test.ts`, with source and package modes. It preserves the +original complete, prepared, unresolved and resolved-input calls and source virtual-entry checks. Additional checks cover +React adapters, inferred prepared data, specialized adapters, invalid data/isolation and invalid container options. + +Failed overload resolution with fully inferred incompatible adapters can still be expensive: four negative calls took +about 4.4 seconds each in a diagnostic probe. Their rejection was verified. The permanent rejection checks supply explicit +type arguments; inference is checked separately by positive calls and an exact inferred Builder type assertion. This change +optimizes valid named definitions, not every possible error-reporting path. + +## Minimal consumer comparison + +The probe uses the same virtual consumer file, compiler options and installed dependencies in both states. Before each +baseline program, the compiler host reads only the three changed production files from `7c4e136e`; the fixed state reads +the working tree. It does not check out files or modify the repository. Both use source aliases and ordinary Node without +Jest or a JIT flag. Each state has one warm-up and three measured runs, alternating before/after in separate Node processes. + +| Metric | Before | After | +| ---------------------------- | ------------- | ------------- | +| Median diagnostic/check time | 4.200 s | 1.226 s | +| Check-time range | 4.054–4.285 s | 1.120–1.331 s | +| Type instantiations | 519,633 | 408,563 | + +Checking this complete source program is **3.43× faster**. Program construction is excluded from that figure and is recorded +separately in the raw results. The separate single-tag control is a diagnostic experiment, not a proposed API change. + +## Test-suite comparison + +Build + types ran sequentially with one warm-up and three measured runs. These commands reuse an existing package build +and include Jest JSON reporting, matching the earlier benchmark's group measurement. The new suite has one additional +file and two additional Jest cases; existing constructor scenarios were moved rather than discarded. + +| Measurement | Earlier benchmark | After this change | +| ------------------------------------- | ---------------------------- | ----------------- | +| Build + types median wall | 54.48 s | 22.59 s | +| Build + types range | 52.89–54.48 s | 22.27–23.10 s | +| Suites / tests | 36 / 283 | 37 / 285 | +| RelayDeclaration file median | 53.91 s | 3.88 s | +| New Relay constructor file median | Included in declaration file | 4.69 s | +| Override build file median | 22.33 s | 22.01 s | +| Whole pre-push/check command, one run | 64.03 s | 42.11 s | +| Whole npm test, one run | 66.16 s | 52.32 s | + +Build + types is **2.41× faster**. The remaining longest file is the override build integration, with 33 CLI build scenarios. +Its timing is effectively unchanged; these measurements do not establish a type-checking bottleneck in View. +The two Relay files can now run concurrently, but the group remains bounded by the override file and available workers. + +`check` passed **172 suites / 1961 tests**, framework typechecking and preparation/typechecking of 24 fixture applications. +The full run passed **203 suites / 2003 tests**, including Chrome and Firefox. The whole-command figures are single +observations, not medians or guarantees: pre-push/check improved about 1.52× and the full command about 1.26× in these runs. +The earlier pre-push command was renamed to `check` without changing its stages. + +## Raw results and reproduction + +The [raw results](2026-09-25-relay-types.json) contain all probe runs, the exact probe script, group warm-up and measured runs, +per-file timings, full-suite results and command exit codes. The embedded probe script pins the baseline source revision. +Restore it into a temporary file and run it from the repository root: + +```sh +node -e "require('node:fs').writeFileSync('/tmp/relay-type-comparison.cjs', require('./tests/benchmarks/2026-09-25-relay-types.json').probe.script)" +node /tmp/relay-type-comparison.cjs before +node /tmp/relay-type-comparison.cjs after +node /tmp/relay-type-comparison.cjs single-tag +``` + +Set `TRACE_DIR` to an empty temporary directory to record the compiler trace for a probe. The source consumer exists only +in the compiler host. It instantiates `defineRelay({name: 'scanner', init: () => ({scan: (text: string) => text.length})})` +through `new entry.Builder(definition, ContentBuilder)` using the vanilla adapter. + +For the suite measurements, build once, warm up the group once, then repeat it three times with unique report filenames: + +```sh +npm run build +npm run test:run -- --selectProjects build types --json --outputFile=/tmp/relay-build-types.json +npm run check +npm test -- --json --outputFile=/tmp/relay-full.json +``` + +Measure whole commands with an external monotonic timer. Do not compare diagnostic-only probe time with Jest wall time, +or per-file sums with CPU time. The full/check commands each include their own single package build. diff --git a/tests/benchmarks/2026-09-25.json b/tests/benchmarks/2026-09-25.json new file mode 100644 index 00000000..06138dee --- /dev/null +++ b/tests/benchmarks/2026-09-25.json @@ -0,0 +1,621 @@ +{ + "environment": { + "platform": "macOS-26.6.2-arm64-arm-64bit-Mach-O", + "node": "v24.5.0", + "logicalCpus": 16, + "cpu": "Apple M3 Max", + "revision": "7c4e136ec013b979fa34310ba418fb0d2f596da5", + "dirty": true, + "workers": 8 + }, + "runs": [ + { + "group": "build", + "run": 0, + "warmup": true, + "command": [ + "npm", + "run", + "build" + ], + "seconds": 3.335339958081022, + "exitCode": 0 + }, + { + "group": "build", + "run": 1, + "warmup": false, + "command": [ + "npm", + "run", + "build" + ], + "seconds": 3.304855166003108, + "exitCode": 0 + }, + { + "group": "build", + "run": 2, + "warmup": false, + "command": [ + "npm", + "run", + "build" + ], + "seconds": 3.349509875057265, + "exitCode": 0 + }, + { + "group": "build", + "run": 3, + "warmup": false, + "command": [ + "npm", + "run", + "build" + ], + "seconds": 3.2570682920049876, + "exitCode": 0 + }, + { + "group": "unit", + "run": 0, + "warmup": true, + "command": [ + "npm", + "run", + "test:unit", + "--", + "--json", + "--outputFile=/tmp/addon-bone-final-bench/unit-0.jest.json" + ], + "seconds": 5.005515957949683, + "exitCode": 0, + "suites": 135, + "tests": 1676, + "passed": 1676, + "sumFileSeconds": 28.503999999999998, + "jestSeconds": 4.518 + }, + { + "group": "unit", + "run": 1, + "warmup": false, + "command": [ + "npm", + "run", + "test:unit", + "--", + "--json", + "--outputFile=/tmp/addon-bone-final-bench/unit-1.jest.json" + ], + "seconds": 5.151686375029385, + "exitCode": 0, + "suites": 135, + "tests": 1676, + "passed": 1676, + "sumFileSeconds": 30.551, + "jestSeconds": 4.708 + }, + { + "group": "unit", + "run": 2, + "warmup": false, + "command": [ + "npm", + "run", + "test:unit", + "--", + "--json", + "--outputFile=/tmp/addon-bone-final-bench/unit-2.jest.json" + ], + "seconds": 5.455427624983713, + "exitCode": 0, + "suites": 135, + "tests": 1676, + "passed": 1676, + "sumFileSeconds": 30.965, + "jestSeconds": 4.942 + }, + { + "group": "unit", + "run": 3, + "warmup": false, + "command": [ + "npm", + "run", + "test:unit", + "--", + "--json", + "--outputFile=/tmp/addon-bone-final-bench/unit-3.jest.json" + ], + "seconds": 4.92035145778209, + "exitCode": 0, + "suites": 135, + "tests": 1676, + "passed": 1676, + "sumFileSeconds": 26.595, + "jestSeconds": 4.318 + }, + { + "group": "unit-coverage", + "run": 0, + "warmup": true, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "unit-node", + "unit-dom", + "--coverage", + "--coverageProvider=babel", + "--json", + "--outputFile=/tmp/addon-bone-final-bench/unit-coverage-0.jest.json" + ], + "seconds": 8.700660167029127, + "exitCode": 0, + "suites": 135, + "tests": 1676, + "passed": 1676, + "sumFileSeconds": 34.702, + "jestSeconds": 8.131 + }, + { + "group": "unit-coverage", + "run": 1, + "warmup": false, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "unit-node", + "unit-dom", + "--coverage", + "--coverageProvider=babel", + "--json", + "--outputFile=/tmp/addon-bone-final-bench/unit-coverage-1.jest.json" + ], + "seconds": 8.086944292066619, + "exitCode": 0, + "suites": 135, + "tests": 1676, + "passed": 1676, + "sumFileSeconds": 31.598, + "jestSeconds": 7.543 + }, + { + "group": "unit-coverage", + "run": 2, + "warmup": false, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "unit-node", + "unit-dom", + "--coverage", + "--coverageProvider=babel", + "--json", + "--outputFile=/tmp/addon-bone-final-bench/unit-coverage-2.jest.json" + ], + "seconds": 8.519587750080973, + "exitCode": 0, + "suites": 135, + "tests": 1676, + "passed": 1676, + "sumFileSeconds": 32.342999999999996, + "jestSeconds": 7.938 + }, + { + "group": "unit-coverage", + "run": 3, + "warmup": false, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "unit-node", + "unit-dom", + "--coverage", + "--coverageProvider=babel", + "--json", + "--outputFile=/tmp/addon-bone-final-bench/unit-coverage-3.jest.json" + ], + "seconds": 11.017556832870468, + "exitCode": 0, + "suites": 135, + "tests": 1676, + "passed": 1676, + "sumFileSeconds": 45.572, + "jestSeconds": 10.32 + }, + { + "group": "build-types", + "run": 0, + "warmup": true, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "build", + "types", + "--json", + "--outputFile=/tmp/addon-bone-final-bench/build-types-0.jest.json" + ], + "seconds": 54.95127954194322, + "exitCode": 0, + "suites": 36, + "tests": 283, + "passed": 283, + "sumFileSeconds": 177.17000000000002, + "jestSeconds": 54.513 + }, + { + "group": "build-types", + "run": 1, + "warmup": false, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "build", + "types", + "--json", + "--outputFile=/tmp/addon-bone-final-bench/build-types-1.jest.json" + ], + "seconds": 52.88551312498748, + "exitCode": 0, + "suites": 36, + "tests": 283, + "passed": 283, + "sumFileSeconds": 167.33, + "jestSeconds": 52.486 + }, + { + "group": "build-types", + "run": 2, + "warmup": false, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "build", + "types", + "--json", + "--outputFile=/tmp/addon-bone-final-bench/build-types-2.jest.json" + ], + "seconds": 54.48285354208201, + "exitCode": 0, + "suites": 36, + "tests": 283, + "passed": 283, + "sumFileSeconds": 163.808, + "jestSeconds": 54.134 + }, + { + "group": "build-types", + "run": 3, + "warmup": false, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "build", + "types", + "--json", + "--outputFile=/tmp/addon-bone-final-bench/build-types-3.jest.json" + ], + "seconds": 54.48391433409415, + "exitCode": 0, + "suites": 36, + "tests": 283, + "passed": 283, + "sumFileSeconds": 166.48, + "jestSeconds": 54.129 + }, + { + "group": "chrome", + "run": 0, + "warmup": true, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "chrome", + "--json", + "--outputFile=/tmp/addon-bone-final-bench/chrome-0.jest.json" + ], + "seconds": 14.210940917022526, + "exitCode": 0, + "suites": 17, + "tests": 20, + "passed": 20, + "sumFileSeconds": 104.067, + "jestSeconds": 13.744 + }, + { + "group": "chrome", + "run": 1, + "warmup": false, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "chrome", + "--json", + "--outputFile=/tmp/addon-bone-final-bench/chrome-1.jest.json" + ], + "seconds": 8.6537630839739, + "exitCode": 0, + "suites": 17, + "tests": 20, + "passed": 20, + "sumFileSeconds": 60.949, + "jestSeconds": 8.259 + }, + { + "group": "chrome", + "run": 2, + "warmup": false, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "chrome", + "--json", + "--outputFile=/tmp/addon-bone-final-bench/chrome-2.jest.json" + ], + "seconds": 8.633810291998088, + "exitCode": 0, + "suites": 17, + "tests": 20, + "passed": 20, + "sumFileSeconds": 60.577, + "jestSeconds": 8.309 + }, + { + "group": "chrome", + "run": 3, + "warmup": false, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "chrome", + "--json", + "--outputFile=/tmp/addon-bone-final-bench/chrome-3.jest.json" + ], + "seconds": 8.815473000053316, + "exitCode": 0, + "suites": 17, + "tests": 20, + "passed": 20, + "sumFileSeconds": 61.57, + "jestSeconds": 8.363 + }, + { + "group": "firefox", + "run": 0, + "warmup": true, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "firefox", + "--json", + "--outputFile=/tmp/addon-bone-final-bench/firefox-0.jest.json" + ], + "seconds": 14.199823916889727, + "exitCode": 0, + "suites": 14, + "tests": 22, + "passed": 22, + "sumFileSeconds": 95.919, + "jestSeconds": 13.734 + }, + { + "group": "firefox", + "run": 1, + "warmup": false, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "firefox", + "--json", + "--outputFile=/tmp/addon-bone-final-bench/firefox-1.jest.json" + ], + "seconds": 12.811804458033293, + "exitCode": 0, + "suites": 14, + "tests": 22, + "passed": 22, + "sumFileSeconds": 91.847, + "jestSeconds": 12.407 + }, + { + "group": "firefox", + "run": 2, + "warmup": false, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "firefox", + "--json", + "--outputFile=/tmp/addon-bone-final-bench/firefox-2.jest.json" + ], + "seconds": 13.957261291798204, + "exitCode": 0, + "suites": 14, + "tests": 22, + "passed": 22, + "sumFileSeconds": 92.166, + "jestSeconds": 13.574 + }, + { + "group": "firefox", + "run": 3, + "warmup": false, + "command": [ + "node", + "scripts/test-run.mjs", + "--selectProjects", + "firefox", + "--json", + "--outputFile=/tmp/addon-bone-final-bench/firefox-3.jest.json" + ], + "seconds": 13.349818667164072, + "exitCode": 0, + "suites": 14, + "tests": 22, + "passed": 22, + "sumFileSeconds": 92.161, + "jestSeconds": 12.916 + }, + { + "group": "fixture-typecheck", + "run": 0, + "warmup": true, + "command": [ + "node", + "node_modules/tsx/dist/cli.mjs", + "tests/integration/typecheck.ts", + "--prepare" + ], + "seconds": 9.378492458024994, + "exitCode": 0 + }, + { + "group": "fixture-typecheck", + "run": 1, + "warmup": false, + "command": [ + "node", + "node_modules/tsx/dist/cli.mjs", + "tests/integration/typecheck.ts", + "--prepare" + ], + "seconds": 9.46627854090184, + "exitCode": 0 + }, + { + "group": "fixture-typecheck", + "run": 2, + "warmup": false, + "command": [ + "node", + "node_modules/tsx/dist/cli.mjs", + "tests/integration/typecheck.ts", + "--prepare" + ], + "seconds": 8.924615582916886, + "exitCode": 0 + }, + { + "group": "fixture-typecheck", + "run": 3, + "warmup": false, + "command": [ + "node", + "node_modules/tsx/dist/cli.mjs", + "tests/integration/typecheck.ts", + "--prepare" + ], + "seconds": 8.819596541812643, + "exitCode": 0 + }, + { + "group": "full", + "run": 0, + "warmup": false, + "command": [ + "npm", + "run", + "test", + "--", + "--json", + "--outputFile=/tmp/addon-bone-final-bench/full-0.jest.json" + ], + "seconds": 66.16412891703658, + "exitCode": 0, + "suites": 202, + "tests": 2001, + "passed": 2001, + "sumFileSeconds": 357.851, + "jestSeconds": 61.758 + }, + { + "group": "pre-push", + "run": 0, + "warmup": false, + "command": [ + "npm", + "run", + "test:pre-push" + ], + "seconds": 64.03328733285889, + "exitCode": 0, + "jestSeconds": 58.354 + } + ], + "matchedUnitComparison": { + "command": [ + "npm", + "run", + "test:unit" + ], + "before": [ + { + "run": 0, + "warmup": true, + "seconds": 4.9167931659612805, + "exitCode": 0 + }, + { + "run": 1, + "warmup": false, + "seconds": 4.4468214579392225, + "exitCode": 0 + }, + { + "run": 2, + "warmup": false, + "seconds": 4.580718582961708, + "exitCode": 0 + }, + { + "run": 3, + "warmup": false, + "seconds": 4.678294291952625, + "exitCode": 0 + } + ], + "after": [ + { + "run": 0, + "warmup": true, + "seconds": 4.722068292088807, + "exitCode": 0 + }, + { + "run": 1, + "warmup": false, + "seconds": 4.354938208125532, + "exitCode": 0 + }, + { + "run": 2, + "warmup": false, + "seconds": 4.3520949999801815, + "exitCode": 0 + }, + { + "run": 3, + "warmup": false, + "seconds": 4.317166750086471, + "exitCode": 0 + } + ] + } +} diff --git a/tests/benchmarks/2026-09-25.md b/tests/benchmarks/2026-09-25.md new file mode 100644 index 00000000..8f62c856 --- /dev/null +++ b/tests/benchmarks/2026-09-25.md @@ -0,0 +1,103 @@ +# Test benchmark — 2026-09-25 + +Measured locally on Apple M3 Max, 16 logical CPUs, macOS 26.6.2 arm64, Node 24.5.0. +Base revision: `7c4e136e` plus the local eight-file mock migration. Runtime source was unchanged. +Browser 0.9.0 and Storage 0.8.0; Jest uses eight workers, fixture preparation/typechecking uses four processes. +The shared macOS arm64 runner enables `--no-sparkplug`. Browser launches and child CLI processes keep their normal flags. + +## Method + +Commands ran sequentially, outside the filesystem/network sandbox so HTTP fixtures could bind localhost. +One warm-up preceded three measured runs for each individual group. Caches stayed warm; fixture applications were rebuilt +where their commands require it. Full `npm test` and pre-push each ran once after the individual groups. +Wall time uses Python `time.perf_counter()` around the entire command, including startup, reporting and process exit. +Jest groups in the main series include `--json --outputFile=...`; their JSON/report overhead is included. Builds are excluded +from group-only Jest commands and included only in the build/full/pre-push rows. Coverage uses the existing Babel provider. + +The raw [results](2026-09-25.json) include every command, warm-up, exit code, measured wall time, Jest-reported time, +and sum of per-file elapsed time where available. Per-file elapsed sums are **not CPU time**. All reported runs passed. +An initial restricted unit warm-up failed with `listen EPERM`; it was excluded and the entire before-series rerun outside +that sandbox. No test failures were retried within the reported benchmark series. + +## Final measurements + +| Command/group | Measured runs | Median wall, s | Range, s | Suites / tests | +| ---------------------------------------- | ------------- | -------------- | ----------- | -------------- | +| Package build | 3 | 3.30 | 3.26–3.35 | — | +| Units with JSON report | 3 | 5.15 | 4.92–5.46 | 135 / 1676 | +| Units + Babel coverage + JSON report | 3 | 8.52 | 8.09–11.02 | 135 / 1676 | +| Build + types | 3 | 54.48 | 52.89–54.48 | 36 / 283 | +| Chrome | 3 | 8.65 | 8.63–8.82 | 17 / 20 | +| Firefox | 3 | 13.35 | 12.81–13.96 | 14 / 22 | +| Prepare + typecheck 24 fixtures | 3 | 8.92 | 8.82–9.47 | — | +| Full npm test, including package build | 1 | 66.16 | 66.16–66.16 | 202 / 2001 | +| Pre-push, including build and typechecks | 1 | 64.03 | 64.03–64.03 | — | + +The full run passed **202 suites / 2001 tests** across all six projects. Pre-push passed **171 suites / 1959 tests**, +framework typechecking and preparation/typechecking of **24 fixture applications**. These are local results, not proof +of the Linux/Windows × Node 22/24 CI matrix. + +## Cost of the last eight-file migration + +For a like-for-like check, the exact command `npm run test:unit` ran before and after these edits, without JSON reporting. +Both versions contain **135 suites / 1676 tests**. Each series has one warm-up and three measured runs: + +| State | Median wall, s | Range, s | +| --------------------------- | -------------- | --------- | +| Before local-mock migration | 4.58 | 4.45–4.68 | +| After local-mock migration | 4.35 | 4.32–4.35 | + +The observed change is -5.0%. Three runs do not establish a statistically significant speedup; they show +no observed slowdown for this migration. The separate JSON-report series was slower; do not mix those timings when +estimating the cost of the code changes or attribute the entire difference to the reporter. + +## Historical comparison + +The original user-provided measurements used a smaller test set and did not fully specify the command/reporting settings. +They are reference points, not a controlled A/B benchmark. The initial unit suite had 98 files / 1197 tests, while the final +suite has 135 files / 1676 tests. Build + types grew from 10 to 36 files; Chrome grew from 12 to 17 files. + +| Group | Original wall, s | Final wall, s | Interpretation | +| --------------------------- | ---------------- | ------------- | --------------------------------------------------------------- | +| Package build | 3.4 | 3.30 | Essentially unchanged | +| Units, ordinary npm command | 8.3 | 4.35 | About 1.9× faster, with ~40% more tests | +| Units + Babel coverage | 7.9 | 8.52 | No demonstrated speedup; final series includes JSON reporting | +| Build + types | 14.3 | 54.48 | Slower; expanded suite and current Relay declaration bottleneck | +| Chrome | 17.4 | 8.65 | About 2.0× faster with more files | +| Firefox | Not run | 13.35 | No initial local baseline | + +Do not compare the old CI fixture step (54–60 s) directly with the new local 8.92 s: hardware, application count and build +inclusion differ. The old ~5-minute CI job also cannot be compared directly with local full-suite time. No current CI +performance measurement was made here. The initial report gives no comparable whole-hook timing. Pre-commit now checks +staged formatting only; timing it with an empty index would not represent a real commit, so no pre-commit figure is claimed. + +## Remaining bottleneck + +The later [Relay type-checking follow-up](2026-09-25-relay-types.md) isolates the expensive constructor inference, +records its fix and splits consumer responsibilities. The measurements below describe the pre-fix state. + +In the three measured build/types runs, `RelayDeclaration.test.ts` took **52.26, 53.91 and 53.92 s**. The group finished +in **52.89–54.48 s**, so this file almost entirely determines its wall time. It constructs separate TypeScript programs +for source/package consumers and POSIX/Windows declaration paths. The next largest file, `override.integration.test.ts`, +took **21.95–22.82 s** and ran alongside it. More workers cannot shorten the longest file. + +This benchmark does not establish the cause of the difference from the historical 6.5 s Relay declaration figure. +A separate optimization should profile program construction/type checking and investigate safe reuse or splitting of those +four consumer checks while preserving both public surfaces and path variants. This migration did not change that test. + +## Reproduce + +From the repository root, run each individual command once to warm up and then three times, sequentially. Use an external +monotonic timer for wall time; append `--json --outputFile=` to Jest commands to reproduce the main series. + +```sh +npm run build +npm run test:unit +node scripts/test-run.mjs --selectProjects unit-node unit-dom --coverage --coverageProvider=babel +node scripts/test-run.mjs --selectProjects build types +node scripts/test-run.mjs --selectProjects chrome +node scripts/test-run.mjs --selectProjects firefox +node node_modules/tsx/dist/cli.mjs tests/integration/typecheck.ts --prepare +npm run test +npm run test:pre-push +``` diff --git a/tests/benchmarks/2026-09-28-override-pilot.json b/tests/benchmarks/2026-09-28-override-pilot.json new file mode 100644 index 00000000..919da75b --- /dev/null +++ b/tests/benchmarks/2026-09-28-override-pilot.json @@ -0,0 +1,14185 @@ +{ + "node": "v24.5.0", + "platform": "darwin", + "arch": "arm64", + "cpus": 16, + "flags": [ + "--expose-gc", + "--no-sparkplug" + ], + "runs": [ + { + "id": "1-warmup-2-cli", + "phase": "warmup", + "mode": "cli", + "workers": 2, + "wallSeconds": 13.979217416, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 39250, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 115834880, + "heapTotal": 60964864, + "heapUsed": 24928304, + "external": 5717870, + "arrayBuffers": 2056770, + "maxRssKiB": 113248 + }, + { + "pid": 39251, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116965376, + "heapTotal": 60964864, + "heapUsed": 24829416, + "external": 5717582, + "arrayBuffers": 2056770, + "maxRssKiB": 114384 + }, + { + "pid": 39250, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 116719616, + "heapTotal": 61489152, + "heapUsed": 25962624, + "external": 5699073, + "arrayBuffers": 2064962, + "maxRssKiB": 113984 + }, + { + "pid": 39251, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 117702656, + "heapTotal": 61227008, + "heapUsed": 25861952, + "external": 5698819, + "arrayBuffers": 2064962, + "maxRssKiB": 114944 + }, + { + "pid": 39250, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 117325824, + "heapTotal": 62537728, + "heapUsed": 27032792, + "external": 5699678, + "arrayBuffers": 2064962, + "maxRssKiB": 114576 + }, + { + "pid": 39251, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 118128640, + "heapTotal": 62537728, + "heapUsed": 26924680, + "external": 5706980, + "arrayBuffers": 2064962, + "maxRssKiB": 115360 + }, + { + "pid": 39250, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 125485056, + "heapTotal": 66781184, + "heapUsed": 29280384, + "external": 7763345, + "arrayBuffers": 2073154, + "maxRssKiB": 122672 + }, + { + "pid": 39251, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 118603776, + "heapTotal": 63586304, + "heapUsed": 28000944, + "external": 5698817, + "arrayBuffers": 2064962, + "maxRssKiB": 115824 + }, + { + "pid": 39250, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 125714432, + "heapTotal": 67305472, + "heapUsed": 30317280, + "external": 5715239, + "arrayBuffers": 2081346, + "maxRssKiB": 122768 + }, + { + "pid": 39251, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 119422976, + "heapTotal": 64897024, + "heapUsed": 29059840, + "external": 5707138, + "arrayBuffers": 2064962, + "maxRssKiB": 116624 + }, + { + "pid": 39251, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 120094720, + "heapTotal": 66207744, + "heapUsed": 30091160, + "external": 5707167, + "arrayBuffers": 2064962, + "maxRssKiB": 117280 + }, + { + "pid": 39250, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 125878272, + "heapTotal": 67829760, + "heapUsed": 31361056, + "external": 5723400, + "arrayBuffers": 2081346, + "maxRssKiB": 122928 + }, + { + "pid": 39251, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 120750080, + "heapTotal": 67256320, + "heapUsed": 31112968, + "external": 5698790, + "arrayBuffers": 2064962, + "maxRssKiB": 117920 + }, + { + "pid": 39250, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 128794624, + "heapTotal": 102432768, + "heapUsed": 32424984, + "external": 5715237, + "arrayBuffers": 2081346, + "maxRssKiB": 125776 + }, + { + "pid": 39250, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 129220608, + "heapTotal": 103481344, + "heapUsed": 33470328, + "external": 5722276, + "arrayBuffers": 2081346, + "maxRssKiB": 126192 + }, + { + "pid": 39251, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 121520128, + "heapTotal": 68304896, + "heapUsed": 32137320, + "external": 5707011, + "arrayBuffers": 2064962, + "maxRssKiB": 118672 + }, + { + "pid": 39250, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 129925120, + "heapTotal": 104529920, + "heapUsed": 34517968, + "external": 5714084, + "arrayBuffers": 2081346, + "maxRssKiB": 126880 + }, + { + "pid": 39251, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 122273792, + "heapTotal": 69615616, + "heapUsed": 33216728, + "external": 5697533, + "arrayBuffers": 2064962, + "maxRssKiB": 119408 + }, + { + "pid": 39250, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 130662400, + "heapTotal": 105578496, + "heapUsed": 35544760, + "external": 5722120, + "arrayBuffers": 2081346, + "maxRssKiB": 127600 + }, + { + "pid": 39251, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122994688, + "heapTotal": 70664192, + "heapUsed": 34247776, + "external": 5705725, + "arrayBuffers": 2064962, + "maxRssKiB": 120112 + }, + { + "pid": 39250, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 131383296, + "heapTotal": 106889216, + "heapUsed": 36566664, + "external": 5713928, + "arrayBuffers": 2081346, + "maxRssKiB": 128304 + }, + { + "pid": 39251, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 139624448, + "heapTotal": 106364928, + "heapUsed": 29495824, + "external": 7755125, + "arrayBuffers": 2073154, + "maxRssKiB": 136480 + }, + { + "pid": 39250, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 132104192, + "heapTotal": 107937792, + "heapUsed": 37619664, + "external": 5722119, + "arrayBuffers": 2081346, + "maxRssKiB": 129008 + }, + { + "pid": 39251, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 140361728, + "heapTotal": 100859904, + "heapUsed": 30483776, + "external": 5715213, + "arrayBuffers": 2081346, + "maxRssKiB": 137072 + }, + { + "pid": 39250, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 132775936, + "heapTotal": 109248512, + "heapUsed": 38647208, + "external": 5722119, + "arrayBuffers": 2081346, + "maxRssKiB": 129664 + }, + { + "pid": 39251, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 140541952, + "heapTotal": 101908480, + "heapUsed": 31522240, + "external": 5723374, + "arrayBuffers": 2081346, + "maxRssKiB": 137248 + }, + { + "pid": 39251, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 140607488, + "heapTotal": 103219200, + "heapUsed": 32564272, + "external": 5715211, + "arrayBuffers": 2081346, + "maxRssKiB": 137312 + }, + { + "pid": 39251, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 140623872, + "heapTotal": 104267776, + "heapUsed": 33619072, + "external": 5722268, + "arrayBuffers": 2081346, + "maxRssKiB": 137328 + }, + { + "pid": 39251, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 140623872, + "heapTotal": 105316352, + "heapUsed": 34648040, + "external": 5714076, + "arrayBuffers": 2081346, + "maxRssKiB": 137328 + }, + { + "pid": 39251, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 140656640, + "heapTotal": 106364928, + "heapUsed": 35669440, + "external": 5722112, + "arrayBuffers": 2081346, + "maxRssKiB": 137360 + }, + { + "pid": 39251, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 140689408, + "heapTotal": 107413504, + "heapUsed": 36694712, + "external": 5713920, + "arrayBuffers": 2081346, + "maxRssKiB": 137392 + }, + { + "pid": 39251, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 141213696, + "heapTotal": 108724224, + "heapUsed": 37734672, + "external": 5722111, + "arrayBuffers": 2081346, + "maxRssKiB": 137904 + }, + { + "pid": 39251, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 141852672, + "heapTotal": 109772800, + "heapUsed": 38762664, + "external": 5722111, + "arrayBuffers": 2081346, + "maxRssKiB": 138528 + } + ], + "files": [ + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 2.305 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 6.924 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 6.499 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 6.37 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/competing.integration.test.ts (25 MB heap size)", + "PASS build tests/integration/build/override/newtab.integration.test.ts (6.924 s, 32 MB heap size)", + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (6.499 s, 36 MB heap size)", + "PASS build tests/integration/build/override/history.integration.test.ts (6.37 s, 36 MB heap size)" + ] + }, + { + "id": "2-warmup-2-in-process", + "phase": "warmup", + "mode": "in-process", + "workers": 2, + "wallSeconds": 3.6898622499999982, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 39496, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 357105664, + "heapTotal": 231440384, + "heapUsed": 92241440, + "external": 6002413, + "arrayBuffers": 2175250, + "maxRssKiB": 348864 + }, + { + "pid": 39495, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 356433920, + "heapTotal": 231178240, + "heapUsed": 92235192, + "external": 6002361, + "arrayBuffers": 2175250, + "maxRssKiB": 348208 + }, + { + "pid": 39496, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 367755264, + "heapTotal": 233799680, + "heapUsed": 94973216, + "external": 6027477, + "arrayBuffers": 2183442, + "maxRssKiB": 359264 + }, + { + "pid": 39495, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 367706112, + "heapTotal": 233275392, + "heapUsed": 94968400, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 359216 + }, + { + "pid": 39496, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 378421248, + "heapTotal": 235896832, + "heapUsed": 97693200, + "external": 6027417, + "arrayBuffers": 2183442, + "maxRssKiB": 369680 + }, + { + "pid": 39495, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 379092992, + "heapTotal": 235896832, + "heapUsed": 97690144, + "external": 6027362, + "arrayBuffers": 2183442, + "maxRssKiB": 370336 + }, + { + "pid": 39495, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 391593984, + "heapTotal": 238256128, + "heapUsed": 100284688, + "external": 6035612, + "arrayBuffers": 2183442, + "maxRssKiB": 382544 + }, + { + "pid": 39496, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 388726784, + "heapTotal": 238256128, + "heapUsed": 100285312, + "external": 6035667, + "arrayBuffers": 2183442, + "maxRssKiB": 379744 + }, + { + "pid": 39496, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 390496256, + "heapTotal": 239828992, + "heapUsed": 101897472, + "external": 6024046, + "arrayBuffers": 2183442, + "maxRssKiB": 381472 + }, + { + "pid": 39495, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 402718720, + "heapTotal": 240877568, + "heapUsed": 102921872, + "external": 6035867, + "arrayBuffers": 2183442, + "maxRssKiB": 393408 + }, + { + "pid": 39496, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 392347648, + "heapTotal": 241664000, + "heapUsed": 103463880, + "external": 6032238, + "arrayBuffers": 2183442, + "maxRssKiB": 383280 + }, + { + "pid": 39495, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 413384704, + "heapTotal": 243499008, + "heapUsed": 105528312, + "external": 6027733, + "arrayBuffers": 2183442, + "maxRssKiB": 403824 + }, + { + "pid": 39496, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 394231808, + "heapTotal": 242974720, + "heapUsed": 104948088, + "external": 6023735, + "arrayBuffers": 2183442, + "maxRssKiB": 385120 + }, + { + "pid": 39496, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 395837440, + "heapTotal": 244547584, + "heapUsed": 106507776, + "external": 6031927, + "arrayBuffers": 2183442, + "maxRssKiB": 386688 + }, + { + "pid": 39495, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 423460864, + "heapTotal": 246120448, + "heapUsed": 108031336, + "external": 6035556, + "arrayBuffers": 2183442, + "maxRssKiB": 413664 + }, + { + "pid": 39496, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 398082048, + "heapTotal": 246644736, + "heapUsed": 108272816, + "external": 6023734, + "arrayBuffers": 2183442, + "maxRssKiB": 388880 + }, + { + "pid": 39495, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 431931392, + "heapTotal": 248479744, + "heapUsed": 110642528, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 421936 + }, + { + "pid": 39496, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 399998976, + "heapTotal": 248479744, + "heapUsed": 109919384, + "external": 6031926, + "arrayBuffers": 2183442, + "maxRssKiB": 390752 + }, + { + "pid": 39495, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 433799168, + "heapTotal": 250314752, + "heapUsed": 112418560, + "external": 6031912, + "arrayBuffers": 2183442, + "maxRssKiB": 423760 + }, + { + "pid": 39495, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 435781632, + "heapTotal": 252411904, + "heapUsed": 114070056, + "external": 6023720, + "arrayBuffers": 2183442, + "maxRssKiB": 425696 + }, + { + "pid": 39496, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 413515776, + "heapTotal": 252461056, + "heapUsed": 106721568, + "external": 8083708, + "arrayBuffers": 2191634, + "maxRssKiB": 403952 + }, + { + "pid": 39496, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 422543360, + "heapTotal": 248004608, + "heapUsed": 109270968, + "external": 6043819, + "arrayBuffers": 2199826, + "maxRssKiB": 412768 + }, + { + "pid": 39496, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 431177728, + "heapTotal": 250363904, + "heapUsed": 111833080, + "external": 6043759, + "arrayBuffers": 2199826, + "maxRssKiB": 421200 + }, + { + "pid": 39496, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 439812096, + "heapTotal": 252198912, + "heapUsed": 114355672, + "external": 6052009, + "arrayBuffers": 2199826, + "maxRssKiB": 429632 + }, + { + "pid": 39496, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 440860672, + "heapTotal": 254033920, + "heapUsed": 115861528, + "external": 6040418, + "arrayBuffers": 2199826, + "maxRssKiB": 430656 + }, + { + "pid": 39496, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 442449920, + "heapTotal": 255868928, + "heapUsed": 117389904, + "external": 6048610, + "arrayBuffers": 2199826, + "maxRssKiB": 432208 + }, + { + "pid": 39496, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 444186624, + "heapTotal": 257703936, + "heapUsed": 118910456, + "external": 6040107, + "arrayBuffers": 2199826, + "maxRssKiB": 433904 + }, + { + "pid": 39496, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 445661184, + "heapTotal": 259014656, + "heapUsed": 120399496, + "external": 6048299, + "arrayBuffers": 2199826, + "maxRssKiB": 435344 + }, + { + "pid": 39496, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 447528960, + "heapTotal": 260849664, + "heapUsed": 121948016, + "external": 6040106, + "arrayBuffers": 2199826, + "maxRssKiB": 437168 + }, + { + "pid": 39496, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 449200128, + "heapTotal": 262422528, + "heapUsed": 123476336, + "external": 6048298, + "arrayBuffers": 2199826, + "maxRssKiB": 438800 + }, + { + "pid": 39495, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 439500800, + "heapTotal": 254820352, + "heapUsed": 109306048, + "external": 8082211, + "arrayBuffers": 2199826, + "maxRssKiB": 429328 + }, + { + "pid": 39495, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 439779328, + "heapTotal": 250626048, + "heapUsed": 110313824, + "external": 6042260, + "arrayBuffers": 2208018, + "maxRssKiB": 429472 + }, + { + "pid": 39495, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 439910400, + "heapTotal": 251412480, + "heapUsed": 111359752, + "external": 6042865, + "arrayBuffers": 2208018, + "maxRssKiB": 429600 + } + ], + "files": [ + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 1.189 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 1.267 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 0.49 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 1.943 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (104 MB heap size)", + "PASS build tests/integration/build/override/newtab.integration.test.ts (108 MB heap size)", + "PASS build tests/integration/build/override/history.integration.test.ts (117 MB heap size)", + "PASS build tests/integration/build/override/competing.integration.test.ts (106 MB heap size)" + ] + }, + { + "id": "3-measured-2-cli", + "phase": "measured", + "mode": "cli", + "workers": 2, + "wallSeconds": 13.297347042000002, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 39609, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 115752960, + "heapTotal": 60702720, + "heapUsed": 24930800, + "external": 5717871, + "arrayBuffers": 2056770, + "maxRssKiB": 113168 + }, + { + "pid": 39610, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116719616, + "heapTotal": 60440576, + "heapUsed": 24831192, + "external": 5717583, + "arrayBuffers": 2056770, + "maxRssKiB": 114192 + }, + { + "pid": 39609, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 116572160, + "heapTotal": 61751296, + "heapUsed": 25989072, + "external": 5699073, + "arrayBuffers": 2064962, + "maxRssKiB": 113840 + }, + { + "pid": 39610, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 117407744, + "heapTotal": 61227008, + "heapUsed": 25861552, + "external": 5698819, + "arrayBuffers": 2064962, + "maxRssKiB": 114656 + }, + { + "pid": 39609, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 117112832, + "heapTotal": 62799872, + "heapUsed": 27056560, + "external": 5699678, + "arrayBuffers": 2064962, + "maxRssKiB": 114368 + }, + { + "pid": 39610, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 118013952, + "heapTotal": 62275584, + "heapUsed": 26924536, + "external": 5706980, + "arrayBuffers": 2064962, + "maxRssKiB": 115248 + }, + { + "pid": 39609, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 125042688, + "heapTotal": 67043328, + "heapUsed": 29302632, + "external": 7763346, + "arrayBuffers": 2073154, + "maxRssKiB": 122240 + }, + { + "pid": 39610, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 118767616, + "heapTotal": 63586304, + "heapUsed": 28003056, + "external": 5698817, + "arrayBuffers": 2064962, + "maxRssKiB": 115984 + }, + { + "pid": 39609, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 125353984, + "heapTotal": 67567616, + "heapUsed": 30339264, + "external": 5715239, + "arrayBuffers": 2081346, + "maxRssKiB": 122416 + }, + { + "pid": 39610, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 119504896, + "heapTotal": 64897024, + "heapUsed": 29061280, + "external": 5707138, + "arrayBuffers": 2064962, + "maxRssKiB": 116704 + }, + { + "pid": 39609, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 125665280, + "heapTotal": 68616192, + "heapUsed": 31380784, + "external": 5723400, + "arrayBuffers": 2081346, + "maxRssKiB": 122720 + }, + { + "pid": 39610, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 120225792, + "heapTotal": 66207744, + "heapUsed": 30092184, + "external": 5707167, + "arrayBuffers": 2064962, + "maxRssKiB": 117408 + }, + { + "pid": 39609, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 128712704, + "heapTotal": 102694912, + "heapUsed": 32442776, + "external": 5715237, + "arrayBuffers": 2081346, + "maxRssKiB": 125696 + }, + { + "pid": 39610, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 120963072, + "heapTotal": 67256320, + "heapUsed": 31115864, + "external": 5698790, + "arrayBuffers": 2064962, + "maxRssKiB": 118128 + }, + { + "pid": 39609, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 129318912, + "heapTotal": 103481344, + "heapUsed": 33492984, + "external": 5722276, + "arrayBuffers": 2081346, + "maxRssKiB": 126288 + }, + { + "pid": 39610, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 121700352, + "heapTotal": 68567040, + "heapUsed": 32138600, + "external": 5707011, + "arrayBuffers": 2064962, + "maxRssKiB": 118848 + }, + { + "pid": 39609, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 129826816, + "heapTotal": 104529920, + "heapUsed": 34540240, + "external": 5714084, + "arrayBuffers": 2081346, + "maxRssKiB": 126784 + }, + { + "pid": 39610, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 122470400, + "heapTotal": 69615616, + "heapUsed": 33216968, + "external": 5697533, + "arrayBuffers": 2064962, + "maxRssKiB": 119600 + }, + { + "pid": 39609, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 130482176, + "heapTotal": 105578496, + "heapUsed": 35566344, + "external": 5722120, + "arrayBuffers": 2081346, + "maxRssKiB": 127424 + }, + { + "pid": 39610, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 123191296, + "heapTotal": 70926336, + "heapUsed": 34247968, + "external": 5705725, + "arrayBuffers": 2064962, + "maxRssKiB": 120304 + }, + { + "pid": 39609, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 131219456, + "heapTotal": 106889216, + "heapUsed": 36587480, + "external": 5713928, + "arrayBuffers": 2081346, + "maxRssKiB": 128144 + }, + { + "pid": 39610, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 139837440, + "heapTotal": 105316352, + "heapUsed": 29494936, + "external": 7755126, + "arrayBuffers": 2073154, + "maxRssKiB": 136688 + }, + { + "pid": 39609, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 131973120, + "heapTotal": 108199936, + "heapUsed": 37639984, + "external": 5722119, + "arrayBuffers": 2081346, + "maxRssKiB": 128880 + }, + { + "pid": 39610, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 140623872, + "heapTotal": 101122048, + "heapUsed": 30483896, + "external": 5715213, + "arrayBuffers": 2081346, + "maxRssKiB": 137328 + }, + { + "pid": 39609, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 132677632, + "heapTotal": 109248512, + "heapUsed": 38669448, + "external": 5722119, + "arrayBuffers": 2081346, + "maxRssKiB": 129568 + }, + { + "pid": 39610, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 140738560, + "heapTotal": 101908480, + "heapUsed": 31521272, + "external": 5723374, + "arrayBuffers": 2081346, + "maxRssKiB": 137440 + }, + { + "pid": 39610, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 140738560, + "heapTotal": 102694912, + "heapUsed": 32566616, + "external": 5715211, + "arrayBuffers": 2081346, + "maxRssKiB": 137440 + }, + { + "pid": 39610, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 140754944, + "heapTotal": 104005632, + "heapUsed": 33619928, + "external": 5722268, + "arrayBuffers": 2081346, + "maxRssKiB": 137456 + }, + { + "pid": 39610, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 140771328, + "heapTotal": 105054208, + "heapUsed": 34646480, + "external": 5714076, + "arrayBuffers": 2081346, + "maxRssKiB": 137472 + }, + { + "pid": 39610, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 140787712, + "heapTotal": 106102784, + "heapUsed": 35667976, + "external": 5722112, + "arrayBuffers": 2081346, + "maxRssKiB": 137488 + }, + { + "pid": 39610, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 141066240, + "heapTotal": 107413504, + "heapUsed": 36694464, + "external": 5713920, + "arrayBuffers": 2081346, + "maxRssKiB": 137760 + }, + { + "pid": 39610, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 141639680, + "heapTotal": 108462080, + "heapUsed": 37734552, + "external": 5722111, + "arrayBuffers": 2081346, + "maxRssKiB": 138320 + }, + { + "pid": 39610, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 142262272, + "heapTotal": 109510656, + "heapUsed": 38764064, + "external": 5722111, + "arrayBuffers": 2081346, + "maxRssKiB": 138928 + } + ], + "files": [ + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 2.005 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 6.443 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 6.246 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 6.379 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/competing.integration.test.ts (25 MB heap size)", + "PASS build tests/integration/build/override/newtab.integration.test.ts (6.443 s, 32 MB heap size)", + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (6.246 s, 36 MB heap size)", + "PASS build tests/integration/build/override/history.integration.test.ts (6.379 s, 36 MB heap size)" + ] + }, + { + "id": "4-measured-2-in-process", + "phase": "measured", + "mode": "in-process", + "workers": 2, + "wallSeconds": 3.6261648330000025, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 39860, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 358400000, + "heapTotal": 231440384, + "heapUsed": 92239232, + "external": 6002361, + "arrayBuffers": 2175250, + "maxRssKiB": 350128 + }, + { + "pid": 39861, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 358514688, + "heapTotal": 231702528, + "heapUsed": 92240304, + "external": 6002373, + "arrayBuffers": 2175250, + "maxRssKiB": 350240 + }, + { + "pid": 39860, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 371097600, + "heapTotal": 233275392, + "heapUsed": 94968344, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 362528 + }, + { + "pid": 39861, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 371965952, + "heapTotal": 233799680, + "heapUsed": 94975928, + "external": 6027435, + "arrayBuffers": 2183442, + "maxRssKiB": 363376 + }, + { + "pid": 39861, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 382713856, + "heapTotal": 235896832, + "heapUsed": 97694104, + "external": 6027375, + "arrayBuffers": 2183442, + "maxRssKiB": 373872 + }, + { + "pid": 39860, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 381976576, + "heapTotal": 235896832, + "heapUsed": 97691592, + "external": 6027362, + "arrayBuffers": 2183442, + "maxRssKiB": 373152 + }, + { + "pid": 39861, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 393805824, + "heapTotal": 238256128, + "heapUsed": 100286128, + "external": 6035625, + "arrayBuffers": 2183442, + "maxRssKiB": 384704 + }, + { + "pid": 39860, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 393576448, + "heapTotal": 238518272, + "heapUsed": 100285320, + "external": 6035612, + "arrayBuffers": 2183442, + "maxRssKiB": 384480 + }, + { + "pid": 39861, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 395821056, + "heapTotal": 240091136, + "heapUsed": 101898224, + "external": 6024034, + "arrayBuffers": 2183442, + "maxRssKiB": 386672 + }, + { + "pid": 39860, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 402718720, + "heapTotal": 240615424, + "heapUsed": 102922728, + "external": 6035867, + "arrayBuffers": 2183442, + "maxRssKiB": 393408 + }, + { + "pid": 39861, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 397426688, + "heapTotal": 241926144, + "heapUsed": 103464168, + "external": 6032226, + "arrayBuffers": 2183442, + "maxRssKiB": 388240 + }, + { + "pid": 39861, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 399458304, + "heapTotal": 243499008, + "heapUsed": 104937512, + "external": 6023723, + "arrayBuffers": 2183442, + "maxRssKiB": 390224 + }, + { + "pid": 39860, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 412368896, + "heapTotal": 243236864, + "heapUsed": 105528888, + "external": 6027733, + "arrayBuffers": 2183442, + "maxRssKiB": 402832 + }, + { + "pid": 39861, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 401276928, + "heapTotal": 245071872, + "heapUsed": 106502096, + "external": 6031915, + "arrayBuffers": 2183442, + "maxRssKiB": 392000 + }, + { + "pid": 39860, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 421773312, + "heapTotal": 245858304, + "heapUsed": 108037056, + "external": 6035556, + "arrayBuffers": 2183442, + "maxRssKiB": 412016 + }, + { + "pid": 39861, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 403308544, + "heapTotal": 246382592, + "heapUsed": 108270096, + "external": 6023722, + "arrayBuffers": 2183442, + "maxRssKiB": 393984 + }, + { + "pid": 39861, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 405127168, + "heapTotal": 248479744, + "heapUsed": 109921744, + "external": 6031914, + "arrayBuffers": 2183442, + "maxRssKiB": 395760 + }, + { + "pid": 39860, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 431783936, + "heapTotal": 248479744, + "heapUsed": 110643800, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 421792 + }, + { + "pid": 39860, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 434061312, + "heapTotal": 250314752, + "heapUsed": 112419688, + "external": 6031912, + "arrayBuffers": 2183442, + "maxRssKiB": 424016 + }, + { + "pid": 39860, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 436142080, + "heapTotal": 251887616, + "heapUsed": 114072784, + "external": 6023720, + "arrayBuffers": 2183442, + "maxRssKiB": 426096 + }, + { + "pid": 39861, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 418234368, + "heapTotal": 251936768, + "heapUsed": 106716320, + "external": 8083756, + "arrayBuffers": 2191634, + "maxRssKiB": 408560 + }, + { + "pid": 39861, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 425181184, + "heapTotal": 248528896, + "heapUsed": 109270072, + "external": 6043861, + "arrayBuffers": 2199826, + "maxRssKiB": 415344 + }, + { + "pid": 39861, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 434274304, + "heapTotal": 250363904, + "heapUsed": 111827480, + "external": 6043801, + "arrayBuffers": 2199826, + "maxRssKiB": 424224 + }, + { + "pid": 39861, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 442482688, + "heapTotal": 252723200, + "heapUsed": 114356632, + "external": 6052051, + "arrayBuffers": 2199826, + "maxRssKiB": 432240 + }, + { + "pid": 39861, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 443809792, + "heapTotal": 254558208, + "heapUsed": 115859752, + "external": 6040430, + "arrayBuffers": 2199826, + "maxRssKiB": 433536 + }, + { + "pid": 39861, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 445644800, + "heapTotal": 256131072, + "heapUsed": 117389264, + "external": 6048622, + "arrayBuffers": 2199826, + "maxRssKiB": 435328 + }, + { + "pid": 39861, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 447430656, + "heapTotal": 257703936, + "heapUsed": 118910312, + "external": 6040119, + "arrayBuffers": 2199826, + "maxRssKiB": 437072 + }, + { + "pid": 39861, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 449249280, + "heapTotal": 259538944, + "heapUsed": 120399816, + "external": 6048311, + "arrayBuffers": 2199826, + "maxRssKiB": 438848 + }, + { + "pid": 39861, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 451117056, + "heapTotal": 260587520, + "heapUsed": 121947592, + "external": 6040118, + "arrayBuffers": 2199826, + "maxRssKiB": 440672 + }, + { + "pid": 39861, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 452870144, + "heapTotal": 262422528, + "heapUsed": 123476544, + "external": 6048310, + "arrayBuffers": 2199826, + "maxRssKiB": 442384 + }, + { + "pid": 39860, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 439730176, + "heapTotal": 254558208, + "heapUsed": 109305704, + "external": 8082215, + "arrayBuffers": 2199826, + "maxRssKiB": 429552 + }, + { + "pid": 39860, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 440090624, + "heapTotal": 249315328, + "heapUsed": 110315880, + "external": 6042260, + "arrayBuffers": 2208018, + "maxRssKiB": 429776 + }, + { + "pid": 39860, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 440107008, + "heapTotal": 249839616, + "heapUsed": 111357776, + "external": 6042865, + "arrayBuffers": 2208018, + "maxRssKiB": 429792 + } + ], + "files": [ + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 1.188 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 1.268 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 0.478 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 1.871 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/history.integration.test.ts (104 MB heap size)", + "PASS build tests/integration/build/override/newtab.integration.test.ts (108 MB heap size)", + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (117 MB heap size)", + "PASS build tests/integration/build/override/competing.integration.test.ts (106 MB heap size)" + ] + }, + { + "id": "5-measured-2-in-process", + "phase": "measured", + "mode": "in-process", + "workers": 2, + "wallSeconds": 2.6574647909999984, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 39906, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 116703232, + "heapTotal": 61227008, + "heapUsed": 24933096, + "external": 5717879, + "arrayBuffers": 2056770, + "maxRssKiB": 114096 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 356696064, + "heapTotal": 231702528, + "heapUsed": 92234480, + "external": 6002361, + "arrayBuffers": 2175250, + "maxRssKiB": 348464 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 369999872, + "heapTotal": 233275392, + "heapUsed": 94965016, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 361456 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 379781120, + "heapTotal": 235372544, + "heapUsed": 97697808, + "external": 6027362, + "arrayBuffers": 2183442, + "maxRssKiB": 371008 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 391348224, + "heapTotal": 237993984, + "heapUsed": 100282584, + "external": 6035612, + "arrayBuffers": 2183442, + "maxRssKiB": 382304 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 400752640, + "heapTotal": 240615424, + "heapUsed": 102921392, + "external": 6035867, + "arrayBuffers": 2183442, + "maxRssKiB": 391488 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 409812992, + "heapTotal": 243236864, + "heapUsed": 105528200, + "external": 6027733, + "arrayBuffers": 2183442, + "maxRssKiB": 400336 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 419889152, + "heapTotal": 245858304, + "heapUsed": 108035624, + "external": 6035556, + "arrayBuffers": 2183442, + "maxRssKiB": 410176 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 429752320, + "heapTotal": 248479744, + "heapUsed": 110647920, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 419808 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 431685632, + "heapTotal": 250314752, + "heapUsed": 112418896, + "external": 6031912, + "arrayBuffers": 2183442, + "maxRssKiB": 421696 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 433717248, + "heapTotal": 252674048, + "heapUsed": 114070512, + "external": 6023720, + "arrayBuffers": 2183442, + "maxRssKiB": 423680 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 446889984, + "heapTotal": 256393216, + "heapUsed": 110868184, + "external": 8091903, + "arrayBuffers": 2199826, + "maxRssKiB": 436544 + }, + { + "pid": 39906, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 117571584, + "heapTotal": 61751296, + "heapUsed": 25988664, + "external": 5699073, + "arrayBuffers": 2064962, + "maxRssKiB": 114976 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 455049216, + "heapTotal": 252985344, + "heapUsed": 113417992, + "external": 6052011, + "arrayBuffers": 2208018, + "maxRssKiB": 444512 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 461815808, + "heapTotal": 254820352, + "heapUsed": 115969608, + "external": 6051951, + "arrayBuffers": 2208018, + "maxRssKiB": 451120 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 469221376, + "heapTotal": 256393216, + "heapUsed": 118493184, + "external": 6060201, + "arrayBuffers": 2208018, + "maxRssKiB": 458352 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 470319104, + "heapTotal": 257966080, + "heapUsed": 120006400, + "external": 6048610, + "arrayBuffers": 2208018, + "maxRssKiB": 459424 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 472055808, + "heapTotal": 259538944, + "heapUsed": 121532064, + "external": 6056802, + "arrayBuffers": 2208018, + "maxRssKiB": 461120 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 473858048, + "heapTotal": 261373952, + "heapUsed": 123052064, + "external": 6048299, + "arrayBuffers": 2208018, + "maxRssKiB": 462864 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 475693056, + "heapTotal": 263208960, + "heapUsed": 124542528, + "external": 6056491, + "arrayBuffers": 2208018, + "maxRssKiB": 464672 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 477593600, + "heapTotal": 265043968, + "heapUsed": 126095120, + "external": 6048298, + "arrayBuffers": 2208018, + "maxRssKiB": 466528 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 479395840, + "heapTotal": 266092544, + "heapUsed": 127613288, + "external": 6056490, + "arrayBuffers": 2208018, + "maxRssKiB": 468288 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 488521728, + "heapTotal": 267665408, + "heapUsed": 119923752, + "external": 8108331, + "arrayBuffers": 2191634, + "maxRssKiB": 477648 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 495763456, + "heapTotal": 261373952, + "heapUsed": 122438808, + "external": 6043861, + "arrayBuffers": 2199826, + "maxRssKiB": 484272 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 502530048, + "heapTotal": 263471104, + "heapUsed": 125000928, + "external": 6043801, + "arrayBuffers": 2199826, + "maxRssKiB": 490880 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 508411904, + "heapTotal": 265830400, + "heapUsed": 127517752, + "external": 6052051, + "arrayBuffers": 2199826, + "maxRssKiB": 496624 + }, + { + "pid": 39906, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 118112256, + "heapTotal": 62799872, + "heapUsed": 27053528, + "external": 5699678, + "arrayBuffers": 2064962, + "maxRssKiB": 115344 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 509247488, + "heapTotal": 267403264, + "heapUsed": 129022216, + "external": 6040430, + "arrayBuffers": 2199826, + "maxRssKiB": 497440 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 509984768, + "heapTotal": 268976128, + "heapUsed": 130522920, + "external": 6048622, + "arrayBuffers": 2199826, + "maxRssKiB": 498160 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 511148032, + "heapTotal": 270811136, + "heapUsed": 132003456, + "external": 6040119, + "arrayBuffers": 2199826, + "maxRssKiB": 499296 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 513097728, + "heapTotal": 271859712, + "heapUsed": 133480552, + "external": 6048311, + "arrayBuffers": 2199826, + "maxRssKiB": 501200 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 514834432, + "heapTotal": 273432576, + "heapUsed": 134988704, + "external": 6040118, + "arrayBuffers": 2199826, + "maxRssKiB": 502896 + }, + { + "pid": 39907, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 516456448, + "heapTotal": 275005440, + "heapUsed": 136486200, + "external": 6048310, + "arrayBuffers": 2199826, + "maxRssKiB": 504480 + } + ], + "files": [ + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 1.21 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 0.483 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 1.999 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 0.505 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/newtab.integration.test.ts (108 MB heap size)", + "PASS build tests/integration/build/override/history.integration.test.ts (121 MB heap size)", + "PASS build tests/integration/build/override/competing.integration.test.ts (25 MB heap size)", + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (130 MB heap size)" + ] + }, + { + "id": "6-measured-2-cli", + "phase": "measured", + "mode": "cli", + "workers": 2, + "wallSeconds": 13.360745750000001, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 40018, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 116768768, + "heapTotal": 60702720, + "heapUsed": 24956880, + "external": 5717873, + "arrayBuffers": 2056770, + "maxRssKiB": 114272 + }, + { + "pid": 40019, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116129792, + "heapTotal": 60440576, + "heapUsed": 24827304, + "external": 5717585, + "arrayBuffers": 2056770, + "maxRssKiB": 113536 + }, + { + "pid": 40018, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 117489664, + "heapTotal": 61751296, + "heapUsed": 25983440, + "external": 5699073, + "arrayBuffers": 2064962, + "maxRssKiB": 114736 + }, + { + "pid": 40019, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116916224, + "heapTotal": 61227008, + "heapUsed": 25859552, + "external": 5698819, + "arrayBuffers": 2064962, + "maxRssKiB": 114176 + }, + { + "pid": 40018, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 118128640, + "heapTotal": 62537728, + "heapUsed": 27056768, + "external": 5699678, + "arrayBuffers": 2064962, + "maxRssKiB": 115360 + }, + { + "pid": 40019, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117407744, + "heapTotal": 62537728, + "heapUsed": 26924088, + "external": 5706980, + "arrayBuffers": 2064962, + "maxRssKiB": 114656 + }, + { + "pid": 40018, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 124944384, + "heapTotal": 66519040, + "heapUsed": 29300264, + "external": 7763348, + "arrayBuffers": 2073154, + "maxRssKiB": 122144 + }, + { + "pid": 40019, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 118161408, + "heapTotal": 63586304, + "heapUsed": 28004336, + "external": 5698817, + "arrayBuffers": 2064962, + "maxRssKiB": 115392 + }, + { + "pid": 40018, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 125599744, + "heapTotal": 67567616, + "heapUsed": 30337656, + "external": 5715239, + "arrayBuffers": 2081346, + "maxRssKiB": 122656 + }, + { + "pid": 40019, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 118816768, + "heapTotal": 65159168, + "heapUsed": 29061296, + "external": 5707138, + "arrayBuffers": 2064962, + "maxRssKiB": 116032 + }, + { + "pid": 40018, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 125992960, + "heapTotal": 68616192, + "heapUsed": 31382288, + "external": 5723400, + "arrayBuffers": 2081346, + "maxRssKiB": 123040 + }, + { + "pid": 40019, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 119439360, + "heapTotal": 66207744, + "heapUsed": 30089096, + "external": 5707167, + "arrayBuffers": 2064962, + "maxRssKiB": 116640 + }, + { + "pid": 40018, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 129171456, + "heapTotal": 103219200, + "heapUsed": 32444944, + "external": 5715237, + "arrayBuffers": 2081346, + "maxRssKiB": 126144 + }, + { + "pid": 40019, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 120242176, + "heapTotal": 67256320, + "heapUsed": 31112584, + "external": 5698790, + "arrayBuffers": 2064962, + "maxRssKiB": 117424 + }, + { + "pid": 40018, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 129531904, + "heapTotal": 104005632, + "heapUsed": 33491216, + "external": 5722276, + "arrayBuffers": 2081346, + "maxRssKiB": 126496 + }, + { + "pid": 40019, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 120946688, + "heapTotal": 68567040, + "heapUsed": 32138120, + "external": 5707011, + "arrayBuffers": 2064962, + "maxRssKiB": 118112 + }, + { + "pid": 40018, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 130088960, + "heapTotal": 105054208, + "heapUsed": 34538168, + "external": 5714084, + "arrayBuffers": 2081346, + "maxRssKiB": 127040 + }, + { + "pid": 40019, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121716736, + "heapTotal": 69615616, + "heapUsed": 33217352, + "external": 5697533, + "arrayBuffers": 2064962, + "maxRssKiB": 118864 + }, + { + "pid": 40018, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 130744320, + "heapTotal": 106102784, + "heapUsed": 35564176, + "external": 5722120, + "arrayBuffers": 2081346, + "maxRssKiB": 127680 + }, + { + "pid": 40019, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122437632, + "heapTotal": 70926336, + "heapUsed": 34247392, + "external": 5705725, + "arrayBuffers": 2064962, + "maxRssKiB": 119568 + }, + { + "pid": 40018, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 131481600, + "heapTotal": 107413504, + "heapUsed": 36587616, + "external": 5713928, + "arrayBuffers": 2081346, + "maxRssKiB": 128400 + }, + { + "pid": 40019, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 139591680, + "heapTotal": 103743488, + "heapUsed": 29496160, + "external": 7755128, + "arrayBuffers": 2073154, + "maxRssKiB": 136448 + }, + { + "pid": 40018, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 132218880, + "heapTotal": 108724224, + "heapUsed": 37639560, + "external": 5722119, + "arrayBuffers": 2081346, + "maxRssKiB": 129120 + }, + { + "pid": 40019, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 139837440, + "heapTotal": 100859904, + "heapUsed": 30485536, + "external": 5715213, + "arrayBuffers": 2081346, + "maxRssKiB": 136560 + }, + { + "pid": 40018, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 132841472, + "heapTotal": 109772800, + "heapUsed": 38669104, + "external": 5722119, + "arrayBuffers": 2081346, + "maxRssKiB": 129728 + }, + { + "pid": 40019, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 139853824, + "heapTotal": 101908480, + "heapUsed": 31521256, + "external": 5723374, + "arrayBuffers": 2081346, + "maxRssKiB": 136576 + }, + { + "pid": 40019, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 139853824, + "heapTotal": 102170624, + "heapUsed": 32565976, + "external": 5715211, + "arrayBuffers": 2081346, + "maxRssKiB": 136576 + }, + { + "pid": 40019, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 139870208, + "heapTotal": 103481344, + "heapUsed": 33619376, + "external": 5722268, + "arrayBuffers": 2081346, + "maxRssKiB": 136592 + }, + { + "pid": 40019, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 139919360, + "heapTotal": 104529920, + "heapUsed": 34648152, + "external": 5714076, + "arrayBuffers": 2081346, + "maxRssKiB": 136656 + }, + { + "pid": 40019, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 140378112, + "heapTotal": 105840640, + "heapUsed": 35668144, + "external": 5722112, + "arrayBuffers": 2081346, + "maxRssKiB": 137088 + }, + { + "pid": 40019, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 141131776, + "heapTotal": 106889216, + "heapUsed": 36694488, + "external": 5713920, + "arrayBuffers": 2081346, + "maxRssKiB": 137824 + }, + { + "pid": 40019, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 141836288, + "heapTotal": 107937792, + "heapUsed": 37734736, + "external": 5722111, + "arrayBuffers": 2081346, + "maxRssKiB": 138512 + }, + { + "pid": 40019, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 142540800, + "heapTotal": 109248512, + "heapUsed": 38764728, + "external": 5722111, + "arrayBuffers": 2081346, + "maxRssKiB": 139200 + } + ], + "files": [ + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 1.963 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 6.566 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 6.453 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 6.336 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/competing.integration.test.ts (25 MB heap size)", + "PASS build tests/integration/build/override/newtab.integration.test.ts (6.566 s, 32 MB heap size)", + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (6.453 s, 36 MB heap size)", + "PASS build tests/integration/build/override/history.integration.test.ts (6.336 s, 36 MB heap size)" + ] + }, + { + "id": "7-measured-2-cli", + "phase": "measured", + "mode": "cli", + "workers": 2, + "wallSeconds": 13.366349958000006, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 40254, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116834304, + "heapTotal": 60440576, + "heapUsed": 24831016, + "external": 5717585, + "arrayBuffers": 2056770, + "maxRssKiB": 114288 + }, + { + "pid": 40255, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115916800, + "heapTotal": 60178432, + "heapUsed": 24826624, + "external": 5717624, + "arrayBuffers": 2056770, + "maxRssKiB": 113440 + }, + { + "pid": 40255, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116572160, + "heapTotal": 60964864, + "heapUsed": 25861208, + "external": 5698855, + "arrayBuffers": 2064962, + "maxRssKiB": 113840 + }, + { + "pid": 40254, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 117506048, + "heapTotal": 61227008, + "heapUsed": 25862080, + "external": 5698819, + "arrayBuffers": 2064962, + "maxRssKiB": 114752 + }, + { + "pid": 40254, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 118194176, + "heapTotal": 62537728, + "heapUsed": 26924472, + "external": 5706980, + "arrayBuffers": 2064962, + "maxRssKiB": 115424 + }, + { + "pid": 40255, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117112832, + "heapTotal": 62537728, + "heapUsed": 26925168, + "external": 5707016, + "arrayBuffers": 2064962, + "maxRssKiB": 114368 + }, + { + "pid": 40255, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117702656, + "heapTotal": 63324160, + "heapUsed": 28000552, + "external": 5698853, + "arrayBuffers": 2064962, + "maxRssKiB": 114944 + }, + { + "pid": 40254, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 118898688, + "heapTotal": 63848448, + "heapUsed": 28002448, + "external": 5698817, + "arrayBuffers": 2064962, + "maxRssKiB": 116112 + }, + { + "pid": 40255, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 118472704, + "heapTotal": 64634880, + "heapUsed": 29071544, + "external": 5705892, + "arrayBuffers": 2064962, + "maxRssKiB": 115696 + }, + { + "pid": 40254, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 119586816, + "heapTotal": 65159168, + "heapUsed": 29059712, + "external": 5707138, + "arrayBuffers": 2064962, + "maxRssKiB": 116784 + }, + { + "pid": 40255, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 119128064, + "heapTotal": 65683456, + "heapUsed": 30098392, + "external": 5697700, + "arrayBuffers": 2064962, + "maxRssKiB": 116336 + }, + { + "pid": 40254, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 120307712, + "heapTotal": 66207744, + "heapUsed": 30092120, + "external": 5707167, + "arrayBuffers": 2064962, + "maxRssKiB": 117488 + }, + { + "pid": 40255, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 119865344, + "heapTotal": 66994176, + "heapUsed": 31123224, + "external": 5705736, + "arrayBuffers": 2064962, + "maxRssKiB": 117056 + }, + { + "pid": 40254, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 120979456, + "heapTotal": 67256320, + "heapUsed": 31115032, + "external": 5698790, + "arrayBuffers": 2064962, + "maxRssKiB": 118144 + }, + { + "pid": 40255, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 120487936, + "heapTotal": 68042752, + "heapUsed": 32146848, + "external": 5697544, + "arrayBuffers": 2064962, + "maxRssKiB": 117664 + }, + { + "pid": 40254, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 121733120, + "heapTotal": 68567040, + "heapUsed": 32139064, + "external": 5707011, + "arrayBuffers": 2064962, + "maxRssKiB": 118880 + }, + { + "pid": 40255, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121307136, + "heapTotal": 69091328, + "heapUsed": 33211728, + "external": 5705735, + "arrayBuffers": 2064962, + "maxRssKiB": 118464 + }, + { + "pid": 40254, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 122454016, + "heapTotal": 69615616, + "heapUsed": 33217880, + "external": 5697533, + "arrayBuffers": 2064962, + "maxRssKiB": 119584 + }, + { + "pid": 40255, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 121995264, + "heapTotal": 70402048, + "heapUsed": 34246144, + "external": 5705735, + "arrayBuffers": 2064962, + "maxRssKiB": 119136 + }, + { + "pid": 40254, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 123158528, + "heapTotal": 70926336, + "heapUsed": 34247936, + "external": 5705725, + "arrayBuffers": 2064962, + "maxRssKiB": 120272 + }, + { + "pid": 40254, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 139902976, + "heapTotal": 105840640, + "heapUsed": 29633512, + "external": 7755405, + "arrayBuffers": 2073154, + "maxRssKiB": 136752 + }, + { + "pid": 40255, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 138559488, + "heapTotal": 105578496, + "heapUsed": 29494720, + "external": 7755128, + "arrayBuffers": 2073154, + "maxRssKiB": 135440 + }, + { + "pid": 40254, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 140410880, + "heapTotal": 100597760, + "heapUsed": 30613048, + "external": 5715457, + "arrayBuffers": 2093433, + "maxRssKiB": 137120 + }, + { + "pid": 40255, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 139313152, + "heapTotal": 100597760, + "heapUsed": 30482664, + "external": 5715213, + "arrayBuffers": 2081346, + "maxRssKiB": 136048 + }, + { + "pid": 40254, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 140886016, + "heapTotal": 101384192, + "heapUsed": 31645656, + "external": 5716062, + "arrayBuffers": 2081346, + "maxRssKiB": 137584 + }, + { + "pid": 40255, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 139575296, + "heapTotal": 101384192, + "heapUsed": 31521264, + "external": 5723374, + "arrayBuffers": 2081346, + "maxRssKiB": 136304 + }, + { + "pid": 40255, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 139591680, + "heapTotal": 101908480, + "heapUsed": 32563848, + "external": 5715211, + "arrayBuffers": 2081346, + "maxRssKiB": 136320 + }, + { + "pid": 40255, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 139640832, + "heapTotal": 103219200, + "heapUsed": 33617984, + "external": 5722268, + "arrayBuffers": 2081346, + "maxRssKiB": 136368 + }, + { + "pid": 40255, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 139640832, + "heapTotal": 104529920, + "heapUsed": 34644984, + "external": 5714076, + "arrayBuffers": 2081346, + "maxRssKiB": 136432 + }, + { + "pid": 40255, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 139673600, + "heapTotal": 105578496, + "heapUsed": 35667208, + "external": 5722112, + "arrayBuffers": 2081346, + "maxRssKiB": 136432 + }, + { + "pid": 40255, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 139706368, + "heapTotal": 106627072, + "heapUsed": 36687584, + "external": 5713920, + "arrayBuffers": 2081346, + "maxRssKiB": 136432 + }, + { + "pid": 40255, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 139902976, + "heapTotal": 107675648, + "heapUsed": 37732392, + "external": 5722111, + "arrayBuffers": 2081346, + "maxRssKiB": 136624 + }, + { + "pid": 40255, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 140558336, + "heapTotal": 108724224, + "heapUsed": 38761920, + "external": 5722111, + "arrayBuffers": 2081346, + "maxRssKiB": 137280 + } + ], + "files": [ + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 6.397 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 6.464 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 1.988 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 6.584 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (6.397 s, 32 MB heap size)", + "PASS build tests/integration/build/override/newtab.integration.test.ts (6.464 s, 32 MB heap size)", + "PASS build tests/integration/build/override/competing.integration.test.ts (30 MB heap size)", + "PASS build tests/integration/build/override/history.integration.test.ts (6.584 s, 36 MB heap size)" + ] + }, + { + "id": "8-measured-2-in-process", + "phase": "measured", + "mode": "in-process", + "workers": 2, + "wallSeconds": 3.6841440409999997, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 40508, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 358400000, + "heapTotal": 230916096, + "heapUsed": 92235168, + "external": 6002373, + "arrayBuffers": 2175250, + "maxRssKiB": 350128 + }, + { + "pid": 40509, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 357629952, + "heapTotal": 231702528, + "heapUsed": 92239360, + "external": 6002361, + "arrayBuffers": 2177217, + "maxRssKiB": 349376 + }, + { + "pid": 40509, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 370524160, + "heapTotal": 233275392, + "heapUsed": 94968480, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 361968 + }, + { + "pid": 40508, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 370933760, + "heapTotal": 233275392, + "heapUsed": 94964032, + "external": 6027435, + "arrayBuffers": 2183442, + "maxRssKiB": 362368 + }, + { + "pid": 40509, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 383516672, + "heapTotal": 235634688, + "heapUsed": 97697000, + "external": 6027362, + "arrayBuffers": 2183442, + "maxRssKiB": 374656 + }, + { + "pid": 40508, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 384925696, + "heapTotal": 235372544, + "heapUsed": 97690352, + "external": 6027375, + "arrayBuffers": 2183442, + "maxRssKiB": 376032 + }, + { + "pid": 40508, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 396279808, + "heapTotal": 237993984, + "heapUsed": 100283088, + "external": 6035625, + "arrayBuffers": 2183442, + "maxRssKiB": 387120 + }, + { + "pid": 40509, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 393543680, + "heapTotal": 238256128, + "heapUsed": 100283144, + "external": 6035612, + "arrayBuffers": 2183442, + "maxRssKiB": 384448 + }, + { + "pid": 40508, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 398065664, + "heapTotal": 239566848, + "heapUsed": 101894968, + "external": 6024034, + "arrayBuffers": 2183442, + "maxRssKiB": 388864 + }, + { + "pid": 40509, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 402391040, + "heapTotal": 240877568, + "heapUsed": 102922728, + "external": 6035867, + "arrayBuffers": 2183442, + "maxRssKiB": 393088 + }, + { + "pid": 40508, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 399982592, + "heapTotal": 241139712, + "heapUsed": 103462304, + "external": 6032226, + "arrayBuffers": 2183442, + "maxRssKiB": 390736 + }, + { + "pid": 40509, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 412106752, + "heapTotal": 243499008, + "heapUsed": 105529304, + "external": 6027733, + "arrayBuffers": 2183442, + "maxRssKiB": 402576 + }, + { + "pid": 40508, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 401899520, + "heapTotal": 242974720, + "heapUsed": 104935776, + "external": 6023723, + "arrayBuffers": 2183442, + "maxRssKiB": 392608 + }, + { + "pid": 40508, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 403750912, + "heapTotal": 244285440, + "heapUsed": 106505720, + "external": 6031915, + "arrayBuffers": 2183442, + "maxRssKiB": 394416 + }, + { + "pid": 40509, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 423608320, + "heapTotal": 246120448, + "heapUsed": 108036896, + "external": 6035556, + "arrayBuffers": 2183442, + "maxRssKiB": 413808 + }, + { + "pid": 40508, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 405864448, + "heapTotal": 246382592, + "heapUsed": 108270688, + "external": 6023722, + "arrayBuffers": 2183442, + "maxRssKiB": 396480 + }, + { + "pid": 40509, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 432111616, + "heapTotal": 248741888, + "heapUsed": 110648976, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 422112 + }, + { + "pid": 40508, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 407699456, + "heapTotal": 247955456, + "heapUsed": 109919032, + "external": 6031914, + "arrayBuffers": 2183442, + "maxRssKiB": 398272 + }, + { + "pid": 40509, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 434208768, + "heapTotal": 250576896, + "heapUsed": 112420032, + "external": 6031912, + "arrayBuffers": 2183442, + "maxRssKiB": 424160 + }, + { + "pid": 40509, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 436224000, + "heapTotal": 252411904, + "heapUsed": 114073656, + "external": 6023720, + "arrayBuffers": 2183442, + "maxRssKiB": 426128 + }, + { + "pid": 40508, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 421773312, + "heapTotal": 251150336, + "heapUsed": 106719656, + "external": 8083756, + "arrayBuffers": 2191634, + "maxRssKiB": 412016 + }, + { + "pid": 40508, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 430080000, + "heapTotal": 247218176, + "heapUsed": 109276016, + "external": 6043861, + "arrayBuffers": 2199826, + "maxRssKiB": 420128 + }, + { + "pid": 40508, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 436928512, + "heapTotal": 249577472, + "heapUsed": 111830208, + "external": 6043801, + "arrayBuffers": 2199826, + "maxRssKiB": 426816 + }, + { + "pid": 40508, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 443875328, + "heapTotal": 252198912, + "heapUsed": 114353936, + "external": 6052051, + "arrayBuffers": 2199826, + "maxRssKiB": 433600 + }, + { + "pid": 40508, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 445464576, + "heapTotal": 253509632, + "heapUsed": 115859120, + "external": 6040430, + "arrayBuffers": 2199826, + "maxRssKiB": 435152 + }, + { + "pid": 40508, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 447234048, + "heapTotal": 255344640, + "heapUsed": 117389016, + "external": 6048622, + "arrayBuffers": 2199826, + "maxRssKiB": 436880 + }, + { + "pid": 40508, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 448970752, + "heapTotal": 256917504, + "heapUsed": 118910032, + "external": 6040119, + "arrayBuffers": 2199826, + "maxRssKiB": 438576 + }, + { + "pid": 40508, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 450691072, + "heapTotal": 258490368, + "heapUsed": 120399584, + "external": 6048311, + "arrayBuffers": 2199826, + "maxRssKiB": 440256 + }, + { + "pid": 40508, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 452591616, + "heapTotal": 260325376, + "heapUsed": 121946992, + "external": 6040118, + "arrayBuffers": 2199826, + "maxRssKiB": 442112 + }, + { + "pid": 40508, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 454443008, + "heapTotal": 261898240, + "heapUsed": 123472160, + "external": 6048310, + "arrayBuffers": 2199826, + "maxRssKiB": 443920 + }, + { + "pid": 40509, + "worker": "2", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 440025088, + "heapTotal": 255082496, + "heapUsed": 109329312, + "external": 8082215, + "arrayBuffers": 2199826, + "maxRssKiB": 429840 + }, + { + "pid": 40509, + "worker": "2", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 440631296, + "heapTotal": 249315328, + "heapUsed": 110314600, + "external": 6042260, + "arrayBuffers": 2208018, + "maxRssKiB": 430304 + }, + { + "pid": 40509, + "worker": "2", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 440713216, + "heapTotal": 250101760, + "heapUsed": 111361232, + "external": 6042865, + "arrayBuffers": 2208018, + "maxRssKiB": 430384 + } + ], + "files": [ + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 1.208 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 1.292 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 0.486 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 1.908 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/history.integration.test.ts (104 MB heap size)", + "PASS build tests/integration/build/override/newtab.integration.test.ts (108 MB heap size)", + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (117 MB heap size)", + "PASS build tests/integration/build/override/competing.integration.test.ts (106 MB heap size)" + ] + }, + { + "id": "9-warmup-8-cli", + "phase": "warmup", + "mode": "cli", + "workers": 8, + "wallSeconds": 7.281006332999998, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 40605, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 116490240, + "heapTotal": 60964864, + "heapUsed": 24955328, + "external": 5717870, + "arrayBuffers": 2056770, + "maxRssKiB": 113888 + }, + { + "pid": 40606, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115425280, + "heapTotal": 60702720, + "heapUsed": 24826928, + "external": 5717582, + "arrayBuffers": 2056770, + "maxRssKiB": 112848 + }, + { + "pid": 40607, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 117030912, + "heapTotal": 60702720, + "heapUsed": 24831088, + "external": 5717593, + "arrayBuffers": 2056770, + "maxRssKiB": 114464 + }, + { + "pid": 40608, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116686848, + "heapTotal": 60964864, + "heapUsed": 24830688, + "external": 5717621, + "arrayBuffers": 2056770, + "maxRssKiB": 114144 + }, + { + "pid": 40605, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 117260288, + "heapTotal": 62013440, + "heapUsed": 25982504, + "external": 5699073, + "arrayBuffers": 2064962, + "maxRssKiB": 114512 + }, + { + "pid": 40606, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116178944, + "heapTotal": 61227008, + "heapUsed": 25861624, + "external": 5698819, + "arrayBuffers": 2064962, + "maxRssKiB": 113456 + }, + { + "pid": 40607, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 117735424, + "heapTotal": 61227008, + "heapUsed": 25862800, + "external": 5698829, + "arrayBuffers": 2064962, + "maxRssKiB": 114976 + }, + { + "pid": 40608, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 117260288, + "heapTotal": 61227008, + "heapUsed": 25861304, + "external": 5698855, + "arrayBuffers": 2064962, + "maxRssKiB": 114512 + }, + { + "pid": 40605, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 117882880, + "heapTotal": 63062016, + "heapUsed": 27053456, + "external": 5699678, + "arrayBuffers": 2064962, + "maxRssKiB": 115120 + }, + { + "pid": 40607, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 118292480, + "heapTotal": 62275584, + "heapUsed": 26925536, + "external": 5706990, + "arrayBuffers": 2064962, + "maxRssKiB": 115520 + }, + { + "pid": 40608, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117784576, + "heapTotal": 62275584, + "heapUsed": 26924392, + "external": 5707016, + "arrayBuffers": 2064962, + "maxRssKiB": 115024 + }, + { + "pid": 40606, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 116948992, + "heapTotal": 62275584, + "heapUsed": 26924296, + "external": 5706980, + "arrayBuffers": 2064962, + "maxRssKiB": 114208 + }, + { + "pid": 40607, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 118865920, + "heapTotal": 63586304, + "heapUsed": 28001840, + "external": 5698827, + "arrayBuffers": 2064962, + "maxRssKiB": 116080 + }, + { + "pid": 40608, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 118439936, + "heapTotal": 63324160, + "heapUsed": 28001256, + "external": 5698853, + "arrayBuffers": 2064962, + "maxRssKiB": 115664 + }, + { + "pid": 40606, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117538816, + "heapTotal": 63324160, + "heapUsed": 28001920, + "external": 5698817, + "arrayBuffers": 2064962, + "maxRssKiB": 114784 + }, + { + "pid": 40608, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 119226368, + "heapTotal": 64634880, + "heapUsed": 29070360, + "external": 5705892, + "arrayBuffers": 2064962, + "maxRssKiB": 116432 + }, + { + "pid": 40607, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 119586816, + "heapTotal": 64634880, + "heapUsed": 29071152, + "external": 5705884, + "arrayBuffers": 2064962, + "maxRssKiB": 116784 + }, + { + "pid": 40606, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 118243328, + "heapTotal": 64897024, + "heapUsed": 29061824, + "external": 5707138, + "arrayBuffers": 2064962, + "maxRssKiB": 115472 + }, + { + "pid": 40607, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 120160256, + "heapTotal": 66207744, + "heapUsed": 30101776, + "external": 5697692, + "arrayBuffers": 2064962, + "maxRssKiB": 117344 + }, + { + "pid": 40608, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 119799808, + "heapTotal": 65421312, + "heapUsed": 30101336, + "external": 5697700, + "arrayBuffers": 2064962, + "maxRssKiB": 116992 + }, + { + "pid": 40606, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 118849536, + "heapTotal": 65945600, + "heapUsed": 30091384, + "external": 5707167, + "arrayBuffers": 2064962, + "maxRssKiB": 116064 + }, + { + "pid": 40608, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 120520704, + "heapTotal": 66732032, + "heapUsed": 31124120, + "external": 5705736, + "arrayBuffers": 2083712, + "maxRssKiB": 117696 + }, + { + "pid": 40607, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 120897536, + "heapTotal": 67256320, + "heapUsed": 31126480, + "external": 5705728, + "arrayBuffers": 2064962, + "maxRssKiB": 118064 + }, + { + "pid": 40606, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 119619584, + "heapTotal": 66994176, + "heapUsed": 31114760, + "external": 5698790, + "arrayBuffers": 2064962, + "maxRssKiB": 116816 + }, + { + "pid": 40607, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 121536512, + "heapTotal": 68042752, + "heapUsed": 32146696, + "external": 5697536, + "arrayBuffers": 2064962, + "maxRssKiB": 118704 + }, + { + "pid": 40608, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 121307136, + "heapTotal": 68042752, + "heapUsed": 32148112, + "external": 5697544, + "arrayBuffers": 2064962, + "maxRssKiB": 118464 + }, + { + "pid": 40606, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 120406016, + "heapTotal": 68304896, + "heapUsed": 32138968, + "external": 5707011, + "arrayBuffers": 2064962, + "maxRssKiB": 117584 + }, + { + "pid": 40608, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 122109952, + "heapTotal": 69091328, + "heapUsed": 33211856, + "external": 5705735, + "arrayBuffers": 2064962, + "maxRssKiB": 119248 + }, + { + "pid": 40607, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 122290176, + "heapTotal": 69353472, + "heapUsed": 33213592, + "external": 5705727, + "arrayBuffers": 2064962, + "maxRssKiB": 119424 + }, + { + "pid": 40606, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121176064, + "heapTotal": 69615616, + "heapUsed": 33216440, + "external": 5697533, + "arrayBuffers": 2064962, + "maxRssKiB": 118336 + }, + { + "pid": 40608, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122667008, + "heapTotal": 70139904, + "heapUsed": 34246144, + "external": 5705735, + "arrayBuffers": 2064962, + "maxRssKiB": 119792 + }, + { + "pid": 40607, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 123060224, + "heapTotal": 70664192, + "heapUsed": 34247912, + "external": 5705727, + "arrayBuffers": 2064962, + "maxRssKiB": 120176 + }, + { + "pid": 40606, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 121896960, + "heapTotal": 70664192, + "heapUsed": 34246992, + "external": 5705725, + "arrayBuffers": 2064962, + "maxRssKiB": 119040 + } + ], + "files": [ + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 2.087 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 6.786 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 6.804 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 6.863 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/competing.integration.test.ts (25 MB heap size)", + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (6.786 s, 32 MB heap size)", + "PASS build tests/integration/build/override/history.integration.test.ts (6.804 s, 32 MB heap size)", + "PASS build tests/integration/build/override/newtab.integration.test.ts (6.863 s, 32 MB heap size)" + ] + }, + { + "id": "10-warmup-8-in-process", + "phase": "warmup", + "mode": "in-process", + "workers": 8, + "wallSeconds": 2.600871750000006, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 40792, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 116015104, + "heapTotal": 60702720, + "heapUsed": 24930392, + "external": 5717878, + "arrayBuffers": 2056770, + "maxRssKiB": 113424 + }, + { + "pid": 40790, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 359546880, + "heapTotal": 231702528, + "heapUsed": 92237624, + "external": 6002373, + "arrayBuffers": 2175250, + "maxRssKiB": 351248 + }, + { + "pid": 40791, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 355565568, + "heapTotal": 231702528, + "heapUsed": 92236704, + "external": 6002413, + "arrayBuffers": 2175250, + "maxRssKiB": 347360 + }, + { + "pid": 40789, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 357761024, + "heapTotal": 231440384, + "heapUsed": 92240200, + "external": 6002361, + "arrayBuffers": 2175250, + "maxRssKiB": 349504 + }, + { + "pid": 40789, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 372228096, + "heapTotal": 233537536, + "heapUsed": 94970112, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 363632 + }, + { + "pid": 40791, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 368476160, + "heapTotal": 233799680, + "heapUsed": 94966624, + "external": 6027477, + "arrayBuffers": 2183442, + "maxRssKiB": 359968 + }, + { + "pid": 40790, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 373424128, + "heapTotal": 233275392, + "heapUsed": 94965472, + "external": 6027435, + "arrayBuffers": 2183442, + "maxRssKiB": 364800 + }, + { + "pid": 40789, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 384335872, + "heapTotal": 235896832, + "heapUsed": 97693088, + "external": 6027362, + "arrayBuffers": 2183442, + "maxRssKiB": 375456 + }, + { + "pid": 40790, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 384253952, + "heapTotal": 235372544, + "heapUsed": 97689760, + "external": 6027375, + "arrayBuffers": 2183442, + "maxRssKiB": 375376 + }, + { + "pid": 40791, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 378159104, + "heapTotal": 236158976, + "heapUsed": 97692848, + "external": 6027417, + "arrayBuffers": 2183442, + "maxRssKiB": 369424 + }, + { + "pid": 40789, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 394592256, + "heapTotal": 238518272, + "heapUsed": 100286344, + "external": 6035612, + "arrayBuffers": 2183442, + "maxRssKiB": 385472 + }, + { + "pid": 40790, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 394952704, + "heapTotal": 238518272, + "heapUsed": 100283656, + "external": 6035625, + "arrayBuffers": 2183442, + "maxRssKiB": 385824 + }, + { + "pid": 40791, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 390316032, + "heapTotal": 238518272, + "heapUsed": 100285552, + "external": 6035667, + "arrayBuffers": 2183442, + "maxRssKiB": 381296 + }, + { + "pid": 40791, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 392167424, + "heapTotal": 240091136, + "heapUsed": 101895744, + "external": 6024046, + "arrayBuffers": 2183442, + "maxRssKiB": 383104 + }, + { + "pid": 40790, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 396836864, + "heapTotal": 240091136, + "heapUsed": 101894072, + "external": 6024034, + "arrayBuffers": 2183442, + "maxRssKiB": 387664 + }, + { + "pid": 40789, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 404160512, + "heapTotal": 241139712, + "heapUsed": 102924800, + "external": 6035867, + "arrayBuffers": 2183442, + "maxRssKiB": 394816 + }, + { + "pid": 40790, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 398622720, + "heapTotal": 241664000, + "heapUsed": 103461312, + "external": 6032226, + "arrayBuffers": 2183442, + "maxRssKiB": 389408 + }, + { + "pid": 40791, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 393936896, + "heapTotal": 241664000, + "heapUsed": 103462664, + "external": 6032238, + "arrayBuffers": 2183442, + "maxRssKiB": 384832 + }, + { + "pid": 40791, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 396017664, + "heapTotal": 243236864, + "heapUsed": 104951296, + "external": 6023735, + "arrayBuffers": 2183442, + "maxRssKiB": 386864 + }, + { + "pid": 40789, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 411975680, + "heapTotal": 243761152, + "heapUsed": 105530616, + "external": 6027733, + "arrayBuffers": 2183442, + "maxRssKiB": 402448 + }, + { + "pid": 40790, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 400687104, + "heapTotal": 243499008, + "heapUsed": 104937584, + "external": 6023723, + "arrayBuffers": 2183442, + "maxRssKiB": 391424 + }, + { + "pid": 40791, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 397770752, + "heapTotal": 245071872, + "heapUsed": 106507728, + "external": 6031927, + "arrayBuffers": 2183442, + "maxRssKiB": 388576 + }, + { + "pid": 40790, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 402505728, + "heapTotal": 245071872, + "heapUsed": 106504784, + "external": 6031915, + "arrayBuffers": 2183442, + "maxRssKiB": 393200 + }, + { + "pid": 40789, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 422395904, + "heapTotal": 246120448, + "heapUsed": 108043656, + "external": 6035556, + "arrayBuffers": 2183442, + "maxRssKiB": 412624 + }, + { + "pid": 40791, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 399753216, + "heapTotal": 246644736, + "heapUsed": 108271360, + "external": 6023734, + "arrayBuffers": 2183442, + "maxRssKiB": 390512 + }, + { + "pid": 40790, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 404586496, + "heapTotal": 247169024, + "heapUsed": 108268208, + "external": 6023722, + "arrayBuffers": 2183442, + "maxRssKiB": 395232 + }, + { + "pid": 40789, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 431865856, + "heapTotal": 248741888, + "heapUsed": 110650096, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 421872 + }, + { + "pid": 40791, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 401604608, + "heapTotal": 248479744, + "heapUsed": 109918888, + "external": 6031926, + "arrayBuffers": 2183442, + "maxRssKiB": 392320 + }, + { + "pid": 40790, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 406552576, + "heapTotal": 248741888, + "heapUsed": 109917112, + "external": 6031914, + "arrayBuffers": 2183442, + "maxRssKiB": 397152 + }, + { + "pid": 40789, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 433848320, + "heapTotal": 250576896, + "heapUsed": 112421176, + "external": 6031912, + "arrayBuffers": 2183442, + "maxRssKiB": 423808 + }, + { + "pid": 40789, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 435814400, + "heapTotal": 252411904, + "heapUsed": 114074872, + "external": 6023720, + "arrayBuffers": 2183442, + "maxRssKiB": 425728 + }, + { + "pid": 40792, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 116785152, + "heapTotal": 61751296, + "heapUsed": 25962472, + "external": 5699073, + "arrayBuffers": 2064962, + "maxRssKiB": 114048 + }, + { + "pid": 40792, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 117358592, + "heapTotal": 62537728, + "heapUsed": 27033456, + "external": 5699678, + "arrayBuffers": 2064962, + "maxRssKiB": 114608 + } + ], + "files": [ + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 1.262 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 1.269 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 1.346 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 2.069 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (104 MB heap size)", + "PASS build tests/integration/build/override/history.integration.test.ts (104 MB heap size)", + "PASS build tests/integration/build/override/newtab.integration.test.ts (108 MB heap size)", + "PASS build tests/integration/build/override/competing.integration.test.ts (25 MB heap size)" + ] + }, + { + "id": "11-measured-8-cli", + "phase": "measured", + "mode": "cli", + "workers": 8, + "wallSeconds": 7.272388999999996, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 40839, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 115392512, + "heapTotal": 60964864, + "heapUsed": 24929496, + "external": 5717874, + "arrayBuffers": 2056770, + "maxRssKiB": 112816 + }, + { + "pid": 40842, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 114884608, + "heapTotal": 60440576, + "heapUsed": 24825728, + "external": 5717625, + "arrayBuffers": 2056770, + "maxRssKiB": 112416 + }, + { + "pid": 40841, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116359168, + "heapTotal": 60440576, + "heapUsed": 24830264, + "external": 5717597, + "arrayBuffers": 2056770, + "maxRssKiB": 113856 + }, + { + "pid": 40840, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116047872, + "heapTotal": 60702720, + "heapUsed": 24827744, + "external": 5717586, + "arrayBuffers": 2056770, + "maxRssKiB": 113456 + }, + { + "pid": 40839, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 116047872, + "heapTotal": 62013440, + "heapUsed": 25961816, + "external": 5699073, + "arrayBuffers": 2064962, + "maxRssKiB": 113328 + }, + { + "pid": 40842, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 115638272, + "heapTotal": 61489152, + "heapUsed": 25859544, + "external": 5698855, + "arrayBuffers": 2064962, + "maxRssKiB": 112944 + }, + { + "pid": 40841, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 117080064, + "heapTotal": 61489152, + "heapUsed": 25861256, + "external": 5698829, + "arrayBuffers": 2064962, + "maxRssKiB": 114336 + }, + { + "pid": 40840, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116572160, + "heapTotal": 61227008, + "heapUsed": 25861352, + "external": 5698819, + "arrayBuffers": 2064962, + "maxRssKiB": 113840 + }, + { + "pid": 40839, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 116686848, + "heapTotal": 62799872, + "heapUsed": 27032672, + "external": 5699678, + "arrayBuffers": 2064962, + "maxRssKiB": 113952 + }, + { + "pid": 40840, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117014528, + "heapTotal": 62275584, + "heapUsed": 26924360, + "external": 5706980, + "arrayBuffers": 2064962, + "maxRssKiB": 114272 + }, + { + "pid": 40841, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117604352, + "heapTotal": 62275584, + "heapUsed": 26924328, + "external": 5706990, + "arrayBuffers": 2064962, + "maxRssKiB": 114848 + }, + { + "pid": 40842, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 116064256, + "heapTotal": 62013440, + "heapUsed": 26924072, + "external": 5707016, + "arrayBuffers": 2064962, + "maxRssKiB": 113344 + }, + { + "pid": 40840, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117768192, + "heapTotal": 63586304, + "heapUsed": 28000976, + "external": 5698817, + "arrayBuffers": 2064962, + "maxRssKiB": 115008 + }, + { + "pid": 40841, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 118308864, + "heapTotal": 63586304, + "heapUsed": 28000736, + "external": 5698827, + "arrayBuffers": 2064962, + "maxRssKiB": 115536 + }, + { + "pid": 40842, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 116768768, + "heapTotal": 63324160, + "heapUsed": 28000936, + "external": 5698853, + "arrayBuffers": 2064962, + "maxRssKiB": 114032 + }, + { + "pid": 40842, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 117571584, + "heapTotal": 64372736, + "heapUsed": 29071608, + "external": 5705892, + "arrayBuffers": 2064962, + "maxRssKiB": 114816 + }, + { + "pid": 40841, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 119029760, + "heapTotal": 64634880, + "heapUsed": 29070048, + "external": 5705884, + "arrayBuffers": 2064962, + "maxRssKiB": 116240 + }, + { + "pid": 40840, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 118358016, + "heapTotal": 64897024, + "heapUsed": 29059456, + "external": 5707138, + "arrayBuffers": 2064962, + "maxRssKiB": 115584 + }, + { + "pid": 40841, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 119717888, + "heapTotal": 65683456, + "heapUsed": 30099024, + "external": 5697692, + "arrayBuffers": 2064962, + "maxRssKiB": 116912 + }, + { + "pid": 40842, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 118243328, + "heapTotal": 65683456, + "heapUsed": 30101624, + "external": 5697700, + "arrayBuffers": 2064962, + "maxRssKiB": 115472 + }, + { + "pid": 40840, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 119111680, + "heapTotal": 65945600, + "heapUsed": 30089256, + "external": 5707167, + "arrayBuffers": 2064962, + "maxRssKiB": 116320 + }, + { + "pid": 40842, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 118996992, + "heapTotal": 66994176, + "heapUsed": 31124792, + "external": 5705736, + "arrayBuffers": 2064962, + "maxRssKiB": 116208 + }, + { + "pid": 40841, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 120373248, + "heapTotal": 66469888, + "heapUsed": 31123024, + "external": 5705728, + "arrayBuffers": 2064962, + "maxRssKiB": 117552 + }, + { + "pid": 40840, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 119767040, + "heapTotal": 67256320, + "heapUsed": 31114344, + "external": 5698790, + "arrayBuffers": 2064962, + "maxRssKiB": 116960 + }, + { + "pid": 40842, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 119652352, + "heapTotal": 68042752, + "heapUsed": 32147424, + "external": 5697544, + "arrayBuffers": 2064962, + "maxRssKiB": 116848 + }, + { + "pid": 40841, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 121094144, + "heapTotal": 67780608, + "heapUsed": 32148440, + "external": 5697536, + "arrayBuffers": 2064962, + "maxRssKiB": 118256 + }, + { + "pid": 40840, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 120389632, + "heapTotal": 68304896, + "heapUsed": 32138488, + "external": 5707011, + "arrayBuffers": 2064962, + "maxRssKiB": 117568 + }, + { + "pid": 40841, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121880576, + "heapTotal": 69091328, + "heapUsed": 33211720, + "external": 5705727, + "arrayBuffers": 2064962, + "maxRssKiB": 119024 + }, + { + "pid": 40842, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 120455168, + "heapTotal": 69091328, + "heapUsed": 33211936, + "external": 5705735, + "arrayBuffers": 2064962, + "maxRssKiB": 117632 + }, + { + "pid": 40840, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121208832, + "heapTotal": 69353472, + "heapUsed": 33217624, + "external": 5697533, + "arrayBuffers": 2064962, + "maxRssKiB": 118368 + }, + { + "pid": 40841, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122585088, + "heapTotal": 70402048, + "heapUsed": 34246280, + "external": 5705727, + "arrayBuffers": 2064962, + "maxRssKiB": 119712 + }, + { + "pid": 40842, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 121176064, + "heapTotal": 70402048, + "heapUsed": 34246016, + "external": 5705735, + "arrayBuffers": 2064962, + "maxRssKiB": 118336 + }, + { + "pid": 40840, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 121929728, + "heapTotal": 70664192, + "heapUsed": 34247792, + "external": 5705725, + "arrayBuffers": 2064962, + "maxRssKiB": 119072 + } + ], + "files": [ + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 2.192 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 6.815 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 6.815 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 6.892 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/competing.integration.test.ts (25 MB heap size)", + "PASS build tests/integration/build/override/history.integration.test.ts (6.815 s, 32 MB heap size)", + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (6.815 s, 32 MB heap size)", + "PASS build tests/integration/build/override/newtab.integration.test.ts (6.892 s, 32 MB heap size)" + ] + }, + { + "id": "12-measured-8-in-process", + "phase": "measured", + "mode": "in-process", + "workers": 8, + "wallSeconds": 2.561761750000005, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 41056, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 115720192, + "heapTotal": 61489152, + "heapUsed": 24950064, + "external": 5717880, + "arrayBuffers": 2056770, + "maxRssKiB": 113136 + }, + { + "pid": 41053, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 361103360, + "heapTotal": 230653952, + "heapUsed": 92238072, + "external": 6002361, + "arrayBuffers": 2175250, + "maxRssKiB": 352768 + }, + { + "pid": 41054, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 358793216, + "heapTotal": 230653952, + "heapUsed": 92236968, + "external": 6002373, + "arrayBuffers": 2175250, + "maxRssKiB": 350512 + }, + { + "pid": 41055, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 355434496, + "heapTotal": 232488960, + "heapUsed": 92237736, + "external": 6002413, + "arrayBuffers": 2175250, + "maxRssKiB": 347232 + }, + { + "pid": 41053, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 374177792, + "heapTotal": 232751104, + "heapUsed": 94964680, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 365536 + }, + { + "pid": 41055, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 368836608, + "heapTotal": 233799680, + "heapUsed": 94968808, + "external": 6027477, + "arrayBuffers": 2183442, + "maxRssKiB": 360320 + }, + { + "pid": 41054, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 371064832, + "heapTotal": 232751104, + "heapUsed": 94969136, + "external": 6027435, + "arrayBuffers": 2183442, + "maxRssKiB": 362496 + }, + { + "pid": 41053, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 387022848, + "heapTotal": 234848256, + "heapUsed": 97690784, + "external": 6027362, + "arrayBuffers": 2183442, + "maxRssKiB": 378080 + }, + { + "pid": 41055, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 380059648, + "heapTotal": 235634688, + "heapUsed": 97694448, + "external": 6027417, + "arrayBuffers": 2183442, + "maxRssKiB": 371280 + }, + { + "pid": 41054, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 382894080, + "heapTotal": 235634688, + "heapUsed": 97691184, + "external": 6027375, + "arrayBuffers": 2183442, + "maxRssKiB": 374048 + }, + { + "pid": 41053, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 397737984, + "heapTotal": 237731840, + "heapUsed": 100283352, + "external": 6035612, + "arrayBuffers": 2183442, + "maxRssKiB": 388544 + }, + { + "pid": 41055, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 392019968, + "heapTotal": 238256128, + "heapUsed": 100284560, + "external": 6035667, + "arrayBuffers": 2183442, + "maxRssKiB": 382960 + }, + { + "pid": 41054, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 394838016, + "heapTotal": 238256128, + "heapUsed": 100283048, + "external": 6035625, + "arrayBuffers": 2183442, + "maxRssKiB": 385712 + }, + { + "pid": 41054, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 396722176, + "heapTotal": 239828992, + "heapUsed": 101895576, + "external": 6024034, + "arrayBuffers": 2183442, + "maxRssKiB": 387552 + }, + { + "pid": 41055, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 393986048, + "heapTotal": 240091136, + "heapUsed": 101895768, + "external": 6024046, + "arrayBuffers": 2183442, + "maxRssKiB": 384880 + }, + { + "pid": 41053, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 407633920, + "heapTotal": 240615424, + "heapUsed": 102922552, + "external": 6035867, + "arrayBuffers": 2183442, + "maxRssKiB": 398208 + }, + { + "pid": 41054, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 398524416, + "heapTotal": 241664000, + "heapUsed": 103462368, + "external": 6032226, + "arrayBuffers": 2183442, + "maxRssKiB": 389312 + }, + { + "pid": 41055, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 395788288, + "heapTotal": 241926144, + "heapUsed": 103461920, + "external": 6032238, + "arrayBuffers": 2183442, + "maxRssKiB": 386640 + }, + { + "pid": 41054, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 400490496, + "heapTotal": 242974720, + "heapUsed": 104940152, + "external": 6023723, + "arrayBuffers": 2183442, + "maxRssKiB": 391232 + }, + { + "pid": 41053, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 417120256, + "heapTotal": 242974720, + "heapUsed": 105529584, + "external": 6027733, + "arrayBuffers": 2183442, + "maxRssKiB": 407472 + }, + { + "pid": 41055, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 397885440, + "heapTotal": 243236864, + "heapUsed": 104951160, + "external": 6023735, + "arrayBuffers": 2196071, + "maxRssKiB": 388688 + }, + { + "pid": 41054, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 402309120, + "heapTotal": 244285440, + "heapUsed": 106505672, + "external": 6031915, + "arrayBuffers": 2183442, + "maxRssKiB": 393008 + }, + { + "pid": 41055, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 399638528, + "heapTotal": 244547584, + "heapUsed": 106502768, + "external": 6031927, + "arrayBuffers": 2183442, + "maxRssKiB": 390400 + }, + { + "pid": 41053, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 427130880, + "heapTotal": 245596160, + "heapUsed": 108042136, + "external": 6035556, + "arrayBuffers": 2183442, + "maxRssKiB": 417248 + }, + { + "pid": 41054, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 404619264, + "heapTotal": 246382592, + "heapUsed": 108269240, + "external": 6023722, + "arrayBuffers": 2183442, + "maxRssKiB": 395264 + }, + { + "pid": 41055, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 401833984, + "heapTotal": 246644736, + "heapUsed": 108267160, + "external": 6023734, + "arrayBuffers": 2183442, + "maxRssKiB": 392544 + }, + { + "pid": 41055, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 403701760, + "heapTotal": 248479744, + "heapUsed": 109916208, + "external": 6031926, + "arrayBuffers": 2183442, + "maxRssKiB": 394368 + }, + { + "pid": 41054, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 406421504, + "heapTotal": 248479744, + "heapUsed": 109918608, + "external": 6031914, + "arrayBuffers": 2183442, + "maxRssKiB": 397024 + }, + { + "pid": 41053, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 438140928, + "heapTotal": 247955456, + "heapUsed": 110649128, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 428000 + }, + { + "pid": 41053, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 440156160, + "heapTotal": 249790464, + "heapUsed": 112419664, + "external": 6031912, + "arrayBuffers": 2183442, + "maxRssKiB": 429968 + }, + { + "pid": 41053, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 442335232, + "heapTotal": 251887616, + "heapUsed": 114073504, + "external": 6023720, + "arrayBuffers": 2183442, + "maxRssKiB": 432096 + }, + { + "pid": 41056, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 116342784, + "heapTotal": 61751296, + "heapUsed": 25981424, + "external": 5699073, + "arrayBuffers": 2064962, + "maxRssKiB": 113616 + }, + { + "pid": 41056, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 116867072, + "heapTotal": 63062016, + "heapUsed": 27052640, + "external": 5699678, + "arrayBuffers": 2064962, + "maxRssKiB": 114128 + } + ], + "files": [ + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 1.292 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 1.289 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 1.387 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 2.096 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/history.integration.test.ts (104 MB heap size)", + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (104 MB heap size)", + "PASS build tests/integration/build/override/newtab.integration.test.ts (108 MB heap size)", + "PASS build tests/integration/build/override/competing.integration.test.ts (25 MB heap size)" + ] + }, + { + "id": "13-measured-8-in-process", + "phase": "measured", + "mode": "in-process", + "workers": 8, + "wallSeconds": 2.809627999999997, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 41103, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 115212288, + "heapTotal": 60964864, + "heapUsed": 24929440, + "external": 5717880, + "arrayBuffers": 2056770, + "maxRssKiB": 112640 + }, + { + "pid": 41104, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 358989824, + "heapTotal": 231440384, + "heapUsed": 92239000, + "external": 6002361, + "arrayBuffers": 2175250, + "maxRssKiB": 350704 + }, + { + "pid": 41105, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 352567296, + "heapTotal": 230916096, + "heapUsed": 92239000, + "external": 6002373, + "arrayBuffers": 2175250, + "maxRssKiB": 344432 + }, + { + "pid": 41106, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 359612416, + "heapTotal": 231964672, + "heapUsed": 92224664, + "external": 6002413, + "arrayBuffers": 2175250, + "maxRssKiB": 351312 + }, + { + "pid": 41106, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 372293632, + "heapTotal": 233275392, + "heapUsed": 94950792, + "external": 6027477, + "arrayBuffers": 2183442, + "maxRssKiB": 363696 + }, + { + "pid": 41105, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 365346816, + "heapTotal": 233275392, + "heapUsed": 94968528, + "external": 6027435, + "arrayBuffers": 2183442, + "maxRssKiB": 356912 + }, + { + "pid": 41104, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 370999296, + "heapTotal": 233537536, + "heapUsed": 94974024, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 362432 + }, + { + "pid": 41105, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 375226368, + "heapTotal": 235896832, + "heapUsed": 97692232, + "external": 6027375, + "arrayBuffers": 2183442, + "maxRssKiB": 366560 + }, + { + "pid": 41106, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 383270912, + "heapTotal": 235634688, + "heapUsed": 97682904, + "external": 6027417, + "arrayBuffers": 2183442, + "maxRssKiB": 374416 + }, + { + "pid": 41104, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 383025152, + "heapTotal": 235896832, + "heapUsed": 97692472, + "external": 6027362, + "arrayBuffers": 2183442, + "maxRssKiB": 374176 + }, + { + "pid": 41105, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 389677056, + "heapTotal": 237993984, + "heapUsed": 100284872, + "external": 6035625, + "arrayBuffers": 2210406, + "maxRssKiB": 380672 + }, + { + "pid": 41106, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 395886592, + "heapTotal": 237993984, + "heapUsed": 100284120, + "external": 6035667, + "arrayBuffers": 2210426, + "maxRssKiB": 386736 + }, + { + "pid": 41104, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 393478144, + "heapTotal": 238256128, + "heapUsed": 100285080, + "external": 6035612, + "arrayBuffers": 2210399, + "maxRssKiB": 384384 + }, + { + "pid": 41106, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 397705216, + "heapTotal": 240091136, + "heapUsed": 101896008, + "external": 6024046, + "arrayBuffers": 2183442, + "maxRssKiB": 388512 + }, + { + "pid": 41105, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 391528448, + "heapTotal": 239828992, + "heapUsed": 101897032, + "external": 6024034, + "arrayBuffers": 2183442, + "maxRssKiB": 382480 + }, + { + "pid": 41104, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 404471808, + "heapTotal": 240615424, + "heapUsed": 102924544, + "external": 6035867, + "arrayBuffers": 2210528, + "maxRssKiB": 395120 + }, + { + "pid": 41106, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 399425536, + "heapTotal": 241664000, + "heapUsed": 103462320, + "external": 6032238, + "arrayBuffers": 2183442, + "maxRssKiB": 390192 + }, + { + "pid": 41105, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 393117696, + "heapTotal": 241139712, + "heapUsed": 103459552, + "external": 6032226, + "arrayBuffers": 2183442, + "maxRssKiB": 384032 + }, + { + "pid": 41106, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 401326080, + "heapTotal": 242974720, + "heapUsed": 104950808, + "external": 6023735, + "arrayBuffers": 2201415, + "maxRssKiB": 392048 + }, + { + "pid": 41105, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 394985472, + "heapTotal": 242712576, + "heapUsed": 104941688, + "external": 6023723, + "arrayBuffers": 2183442, + "maxRssKiB": 385856 + }, + { + "pid": 41104, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 414154752, + "heapTotal": 243499008, + "heapUsed": 105531288, + "external": 6027733, + "arrayBuffers": 2183442, + "maxRssKiB": 404576 + }, + { + "pid": 41106, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 403013632, + "heapTotal": 244547584, + "heapUsed": 106502192, + "external": 6031927, + "arrayBuffers": 2183442, + "maxRssKiB": 393696 + }, + { + "pid": 41105, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 396754944, + "heapTotal": 244285440, + "heapUsed": 106507208, + "external": 6031915, + "arrayBuffers": 2183442, + "maxRssKiB": 387584 + }, + { + "pid": 41105, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 398819328, + "heapTotal": 246120448, + "heapUsed": 108271272, + "external": 6023722, + "arrayBuffers": 2183442, + "maxRssKiB": 389600 + }, + { + "pid": 41106, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 404996096, + "heapTotal": 246382592, + "heapUsed": 108269256, + "external": 6023734, + "arrayBuffers": 2183442, + "maxRssKiB": 395632 + }, + { + "pid": 41104, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 423198720, + "heapTotal": 245596160, + "heapUsed": 108034504, + "external": 6035556, + "arrayBuffers": 2183442, + "maxRssKiB": 413408 + }, + { + "pid": 41106, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 406978560, + "heapTotal": 248217600, + "heapUsed": 109917960, + "external": 6031926, + "arrayBuffers": 2183442, + "maxRssKiB": 397568 + }, + { + "pid": 41105, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 400703488, + "heapTotal": 247955456, + "heapUsed": 109920032, + "external": 6031914, + "arrayBuffers": 2183442, + "maxRssKiB": 391440 + }, + { + "pid": 41104, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 433307648, + "heapTotal": 248217600, + "heapUsed": 110643792, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 423280 + }, + { + "pid": 41104, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 435535872, + "heapTotal": 250052608, + "heapUsed": 112421216, + "external": 6031912, + "arrayBuffers": 2183442, + "maxRssKiB": 425456 + }, + { + "pid": 41104, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 437485568, + "heapTotal": 251887616, + "heapUsed": 114073608, + "external": 6023720, + "arrayBuffers": 2183442, + "maxRssKiB": 427360 + }, + { + "pid": 41103, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 115851264, + "heapTotal": 61489152, + "heapUsed": 25961960, + "external": 5699073, + "arrayBuffers": 2064962, + "maxRssKiB": 113136 + }, + { + "pid": 41103, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 116391936, + "heapTotal": 62537728, + "heapUsed": 27031984, + "external": 5699678, + "arrayBuffers": 2064962, + "maxRssKiB": 113664 + } + ], + "files": [ + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 1.354 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 1.358 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 1.483 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 2.346 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (104 MB heap size)", + "PASS build tests/integration/build/override/history.integration.test.ts (104 MB heap size)", + "PASS build tests/integration/build/override/newtab.integration.test.ts (108 MB heap size)", + "PASS build tests/integration/build/override/competing.integration.test.ts (25 MB heap size)" + ] + }, + { + "id": "14-measured-8-cli", + "phase": "measured", + "mode": "cli", + "workers": 8, + "wallSeconds": 7.269705541000003, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 41233, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 115556352, + "heapTotal": 60964864, + "heapUsed": 24951224, + "external": 5717873, + "arrayBuffers": 2056770, + "maxRssKiB": 112976 + }, + { + "pid": 41235, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116146176, + "heapTotal": 60178432, + "heapUsed": 24827568, + "external": 5717596, + "arrayBuffers": 2056770, + "maxRssKiB": 113552 + }, + { + "pid": 41236, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115294208, + "heapTotal": 60440576, + "heapUsed": 24826408, + "external": 5717624, + "arrayBuffers": 2056770, + "maxRssKiB": 112720 + }, + { + "pid": 41234, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116473856, + "heapTotal": 60440576, + "heapUsed": 24827328, + "external": 5717585, + "arrayBuffers": 2056770, + "maxRssKiB": 113968 + }, + { + "pid": 41233, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 116195328, + "heapTotal": 61751296, + "heapUsed": 25981032, + "external": 5699073, + "arrayBuffers": 2064962, + "maxRssKiB": 113472 + }, + { + "pid": 41234, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 117309440, + "heapTotal": 61489152, + "heapUsed": 25863848, + "external": 5698819, + "arrayBuffers": 2064962, + "maxRssKiB": 114560 + }, + { + "pid": 41236, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 115965952, + "heapTotal": 61227008, + "heapUsed": 25861744, + "external": 5698855, + "arrayBuffers": 2064962, + "maxRssKiB": 113248 + }, + { + "pid": 41235, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116883456, + "heapTotal": 61227008, + "heapUsed": 25861472, + "external": 5698829, + "arrayBuffers": 2064962, + "maxRssKiB": 114144 + }, + { + "pid": 41233, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 116817920, + "heapTotal": 62799872, + "heapUsed": 27053560, + "external": 5699678, + "arrayBuffers": 2064962, + "maxRssKiB": 114080 + }, + { + "pid": 41234, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117833728, + "heapTotal": 62799872, + "heapUsed": 26927096, + "external": 5706980, + "arrayBuffers": 2064962, + "maxRssKiB": 115072 + }, + { + "pid": 41236, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 116408320, + "heapTotal": 62275584, + "heapUsed": 26924096, + "external": 5707016, + "arrayBuffers": 2064962, + "maxRssKiB": 113680 + }, + { + "pid": 41235, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117309440, + "heapTotal": 62275584, + "heapUsed": 26923960, + "external": 5706990, + "arrayBuffers": 2064962, + "maxRssKiB": 114560 + }, + { + "pid": 41234, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 118472704, + "heapTotal": 63586304, + "heapUsed": 28001152, + "external": 5698817, + "arrayBuffers": 2064962, + "maxRssKiB": 115696 + }, + { + "pid": 41235, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 118063104, + "heapTotal": 63848448, + "heapUsed": 28000984, + "external": 5698827, + "arrayBuffers": 2064962, + "maxRssKiB": 115296 + }, + { + "pid": 41236, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117178368, + "heapTotal": 63586304, + "heapUsed": 28000888, + "external": 5698853, + "arrayBuffers": 2064962, + "maxRssKiB": 114432 + }, + { + "pid": 41235, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 118767616, + "heapTotal": 64897024, + "heapUsed": 29069752, + "external": 5705884, + "arrayBuffers": 2064962, + "maxRssKiB": 115984 + }, + { + "pid": 41236, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 117817344, + "heapTotal": 64897024, + "heapUsed": 29070904, + "external": 5705892, + "arrayBuffers": 2064962, + "maxRssKiB": 115056 + }, + { + "pid": 41234, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 119078912, + "heapTotal": 65159168, + "heapUsed": 29063440, + "external": 5707138, + "arrayBuffers": 2064962, + "maxRssKiB": 116288 + }, + { + "pid": 41235, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 119472128, + "heapTotal": 65945600, + "heapUsed": 30100216, + "external": 5697692, + "arrayBuffers": 2064962, + "maxRssKiB": 116672 + }, + { + "pid": 41236, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 118489088, + "heapTotal": 65945600, + "heapUsed": 30374320, + "external": 5697700, + "arrayBuffers": 2064962, + "maxRssKiB": 115712 + }, + { + "pid": 41234, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 119799808, + "heapTotal": 66469888, + "heapUsed": 30091816, + "external": 5707167, + "arrayBuffers": 2064962, + "maxRssKiB": 116992 + }, + { + "pid": 41235, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 120242176, + "heapTotal": 66994176, + "heapUsed": 31125144, + "external": 5705728, + "arrayBuffers": 2064962, + "maxRssKiB": 117424 + }, + { + "pid": 41236, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 119209984, + "heapTotal": 66732032, + "heapUsed": 31122616, + "external": 5705736, + "arrayBuffers": 2064962, + "maxRssKiB": 116416 + }, + { + "pid": 41234, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 120569856, + "heapTotal": 67518464, + "heapUsed": 31115048, + "external": 5698790, + "arrayBuffers": 2064962, + "maxRssKiB": 117744 + }, + { + "pid": 41235, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 120979456, + "heapTotal": 68304896, + "heapUsed": 32147024, + "external": 5697536, + "arrayBuffers": 2064962, + "maxRssKiB": 118144 + }, + { + "pid": 41236, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 119980032, + "heapTotal": 68042752, + "heapUsed": 32147264, + "external": 5697544, + "arrayBuffers": 2064962, + "maxRssKiB": 117168 + }, + { + "pid": 41234, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 121323520, + "heapTotal": 68829184, + "heapUsed": 32138904, + "external": 5707011, + "arrayBuffers": 2064962, + "maxRssKiB": 118480 + }, + { + "pid": 41235, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121765888, + "heapTotal": 69353472, + "heapUsed": 33211712, + "external": 5705727, + "arrayBuffers": 2064962, + "maxRssKiB": 118912 + }, + { + "pid": 41236, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 120750080, + "heapTotal": 69091328, + "heapUsed": 33211488, + "external": 5705735, + "arrayBuffers": 2064962, + "maxRssKiB": 117920 + }, + { + "pid": 41234, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121978880, + "heapTotal": 69877760, + "heapUsed": 33216776, + "external": 5697533, + "arrayBuffers": 2064962, + "maxRssKiB": 119120 + }, + { + "pid": 41236, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 121339904, + "heapTotal": 70402048, + "heapUsed": 34244448, + "external": 5705735, + "arrayBuffers": 2064962, + "maxRssKiB": 118496 + }, + { + "pid": 41235, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122454016, + "heapTotal": 70664192, + "heapUsed": 34245904, + "external": 5705727, + "arrayBuffers": 2064962, + "maxRssKiB": 119584 + }, + { + "pid": 41234, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122748928, + "heapTotal": 71188480, + "heapUsed": 34247776, + "external": 5705725, + "arrayBuffers": 2064962, + "maxRssKiB": 119872 + } + ], + "files": [ + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 2.18 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 6.782 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 6.78 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 6.847 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/competing.integration.test.ts (25 MB heap size)", + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (6.782 s, 32 MB heap size)", + "PASS build tests/integration/build/override/history.integration.test.ts (6.78 s, 32 MB heap size)", + "PASS build tests/integration/build/override/newtab.integration.test.ts (6.847 s, 32 MB heap size)" + ] + }, + { + "id": "15-measured-8-cli", + "phase": "measured", + "mode": "cli", + "workers": 8, + "wallSeconds": 7.12136099999999, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 41371, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 115769344, + "heapTotal": 61227008, + "heapUsed": 24951048, + "external": 5717874, + "arrayBuffers": 2056770, + "maxRssKiB": 113184 + }, + { + "pid": 41370, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115441664, + "heapTotal": 60964864, + "heapUsed": 24827488, + "external": 5717597, + "arrayBuffers": 2056770, + "maxRssKiB": 112944 + }, + { + "pid": 41369, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116375552, + "heapTotal": 60702720, + "heapUsed": 24827640, + "external": 5717625, + "arrayBuffers": 2056770, + "maxRssKiB": 113888 + }, + { + "pid": 41368, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115458048, + "heapTotal": 60702720, + "heapUsed": 24825944, + "external": 5717586, + "arrayBuffers": 2056770, + "maxRssKiB": 112880 + }, + { + "pid": 41371, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 116670464, + "heapTotal": 61751296, + "heapUsed": 25981408, + "external": 5699073, + "arrayBuffers": 2064962, + "maxRssKiB": 113936 + }, + { + "pid": 41368, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116097024, + "heapTotal": 60964864, + "heapUsed": 25859544, + "external": 5698819, + "arrayBuffers": 2064962, + "maxRssKiB": 113376 + }, + { + "pid": 41369, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 117211136, + "heapTotal": 61489152, + "heapUsed": 25860312, + "external": 5698855, + "arrayBuffers": 2064962, + "maxRssKiB": 114464 + }, + { + "pid": 41370, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116326400, + "heapTotal": 61227008, + "heapUsed": 25860488, + "external": 5698829, + "arrayBuffers": 2064962, + "maxRssKiB": 113600 + }, + { + "pid": 41371, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 117211136, + "heapTotal": 62799872, + "heapUsed": 27052640, + "external": 5699678, + "arrayBuffers": 2064962, + "maxRssKiB": 114464 + }, + { + "pid": 41370, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117112832, + "heapTotal": 62537728, + "heapUsed": 26925960, + "external": 5706990, + "arrayBuffers": 2064962, + "maxRssKiB": 114368 + }, + { + "pid": 41369, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117850112, + "heapTotal": 62537728, + "heapUsed": 26926456, + "external": 5707016, + "arrayBuffers": 2064962, + "maxRssKiB": 115088 + }, + { + "pid": 41368, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 116604928, + "heapTotal": 62013440, + "heapUsed": 26924088, + "external": 5706980, + "arrayBuffers": 2064962, + "maxRssKiB": 113872 + }, + { + "pid": 41370, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117784576, + "heapTotal": 63324160, + "heapUsed": 28003008, + "external": 5698827, + "arrayBuffers": 2064962, + "maxRssKiB": 115024 + }, + { + "pid": 41369, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 118554624, + "heapTotal": 63586304, + "heapUsed": 28003344, + "external": 5698853, + "arrayBuffers": 2064962, + "maxRssKiB": 115776 + }, + { + "pid": 41368, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117293056, + "heapTotal": 63324160, + "heapUsed": 28001496, + "external": 5698817, + "arrayBuffers": 2064962, + "maxRssKiB": 114544 + }, + { + "pid": 41369, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 119308288, + "heapTotal": 64634880, + "heapUsed": 29072432, + "external": 5705892, + "arrayBuffers": 2064962, + "maxRssKiB": 116512 + }, + { + "pid": 41370, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 118374400, + "heapTotal": 64634880, + "heapUsed": 29073840, + "external": 5705884, + "arrayBuffers": 2064962, + "maxRssKiB": 115600 + }, + { + "pid": 41368, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 118013952, + "heapTotal": 64897024, + "heapUsed": 29061448, + "external": 5707138, + "arrayBuffers": 2064962, + "maxRssKiB": 115248 + }, + { + "pid": 41369, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 119996416, + "heapTotal": 65945600, + "heapUsed": 30102416, + "external": 5697700, + "arrayBuffers": 2064962, + "maxRssKiB": 117184 + }, + { + "pid": 41370, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 119078912, + "heapTotal": 65945600, + "heapUsed": 30098688, + "external": 5697692, + "arrayBuffers": 2064962, + "maxRssKiB": 116288 + }, + { + "pid": 41368, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 118652928, + "heapTotal": 65945600, + "heapUsed": 30089232, + "external": 5707167, + "arrayBuffers": 2064962, + "maxRssKiB": 115872 + }, + { + "pid": 41369, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 120750080, + "heapTotal": 66994176, + "heapUsed": 31125232, + "external": 5705736, + "arrayBuffers": 2064962, + "maxRssKiB": 117920 + }, + { + "pid": 41370, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 119750656, + "heapTotal": 67256320, + "heapUsed": 31124432, + "external": 5705728, + "arrayBuffers": 2064962, + "maxRssKiB": 116944 + }, + { + "pid": 41368, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 119275520, + "heapTotal": 66732032, + "heapUsed": 31112832, + "external": 5698790, + "arrayBuffers": 2064962, + "maxRssKiB": 116480 + }, + { + "pid": 41369, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 121487360, + "heapTotal": 68304896, + "heapUsed": 32148024, + "external": 5697544, + "arrayBuffers": 2064962, + "maxRssKiB": 118640 + }, + { + "pid": 41370, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 120406016, + "heapTotal": 68304896, + "heapUsed": 32145192, + "external": 5697536, + "arrayBuffers": 2064962, + "maxRssKiB": 117584 + }, + { + "pid": 41368, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 119980032, + "heapTotal": 68042752, + "heapUsed": 32139488, + "external": 5707011, + "arrayBuffers": 2064962, + "maxRssKiB": 117168 + }, + { + "pid": 41369, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 122224640, + "heapTotal": 69615616, + "heapUsed": 33212824, + "external": 5705735, + "arrayBuffers": 2064962, + "maxRssKiB": 119360 + }, + { + "pid": 41370, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121225216, + "heapTotal": 69353472, + "heapUsed": 33212056, + "external": 5705727, + "arrayBuffers": 2064962, + "maxRssKiB": 118384 + }, + { + "pid": 41368, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 120782848, + "heapTotal": 69353472, + "heapUsed": 33216880, + "external": 5697533, + "arrayBuffers": 2064962, + "maxRssKiB": 117952 + }, + { + "pid": 41369, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122929152, + "heapTotal": 70664192, + "heapUsed": 34246280, + "external": 5705735, + "arrayBuffers": 2064962, + "maxRssKiB": 120048 + }, + { + "pid": 41370, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 121978880, + "heapTotal": 70664192, + "heapUsed": 34246680, + "external": 5705727, + "arrayBuffers": 2064962, + "maxRssKiB": 119120 + }, + { + "pid": 41368, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 121389056, + "heapTotal": 70402048, + "heapUsed": 34246888, + "external": 5705725, + "arrayBuffers": 2064962, + "maxRssKiB": 118544 + } + ], + "files": [ + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 2.062 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 6.648 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 6.655 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 6.735 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/competing.integration.test.ts (25 MB heap size)", + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (6.648 s, 32 MB heap size)", + "PASS build tests/integration/build/override/history.integration.test.ts (6.655 s, 32 MB heap size)", + "PASS build tests/integration/build/override/newtab.integration.test.ts (6.735 s, 32 MB heap size)" + ] + }, + { + "id": "16-measured-8-in-process", + "phase": "measured", + "mode": "in-process", + "workers": 8, + "wallSeconds": 3.6221990829999995, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 41549, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 115982336, + "heapTotal": 60964864, + "heapUsed": 24951672, + "external": 5717879, + "arrayBuffers": 2056770, + "maxRssKiB": 113392 + }, + { + "pid": 41547, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 356696064, + "heapTotal": 230653952, + "heapUsed": 92233616, + "external": 6002373, + "arrayBuffers": 2175250, + "maxRssKiB": 348464 + }, + { + "pid": 41548, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 358383616, + "heapTotal": 230916096, + "heapUsed": 92236872, + "external": 6002413, + "arrayBuffers": 2175250, + "maxRssKiB": 350112 + }, + { + "pid": 41546, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 356483072, + "heapTotal": 232226816, + "heapUsed": 92238264, + "external": 6002361, + "arrayBuffers": 2175250, + "maxRssKiB": 348256 + }, + { + "pid": 41547, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 370491392, + "heapTotal": 232488960, + "heapUsed": 94966608, + "external": 6027435, + "arrayBuffers": 2202216, + "maxRssKiB": 361936 + }, + { + "pid": 41548, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 374636544, + "heapTotal": 232751104, + "heapUsed": 94967000, + "external": 6027477, + "arrayBuffers": 2183442, + "maxRssKiB": 365984 + }, + { + "pid": 41546, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 370229248, + "heapTotal": 233799680, + "heapUsed": 94967024, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 361680 + }, + { + "pid": 41547, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 381779968, + "heapTotal": 235372544, + "heapUsed": 97692664, + "external": 6027375, + "arrayBuffers": 2202185, + "maxRssKiB": 372960 + }, + { + "pid": 41548, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 385597440, + "heapTotal": 235634688, + "heapUsed": 97693376, + "external": 6027417, + "arrayBuffers": 2202205, + "maxRssKiB": 376688 + }, + { + "pid": 41546, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 381517824, + "heapTotal": 236158976, + "heapUsed": 97694584, + "external": 6027362, + "arrayBuffers": 2183442, + "maxRssKiB": 372704 + }, + { + "pid": 41547, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 390692864, + "heapTotal": 237469696, + "heapUsed": 100282176, + "external": 6035625, + "arrayBuffers": 2210406, + "maxRssKiB": 381664 + }, + { + "pid": 41548, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 396263424, + "heapTotal": 237469696, + "heapUsed": 100282904, + "external": 6035667, + "arrayBuffers": 2183442, + "maxRssKiB": 387104 + }, + { + "pid": 41546, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 391725056, + "heapTotal": 237993984, + "heapUsed": 100284360, + "external": 6035612, + "arrayBuffers": 2183442, + "maxRssKiB": 382672 + }, + { + "pid": 41547, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 392691712, + "heapTotal": 239304704, + "heapUsed": 101894648, + "external": 6024034, + "arrayBuffers": 2183442, + "maxRssKiB": 383616 + }, + { + "pid": 41548, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 398082048, + "heapTotal": 239304704, + "heapUsed": 101897632, + "external": 6024046, + "arrayBuffers": 2183442, + "maxRssKiB": 388880 + }, + { + "pid": 41546, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 402472960, + "heapTotal": 240877568, + "heapUsed": 102924152, + "external": 6035867, + "arrayBuffers": 2183442, + "maxRssKiB": 393168 + }, + { + "pid": 41548, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 399949824, + "heapTotal": 240877568, + "heapUsed": 103455800, + "external": 6032238, + "arrayBuffers": 2183442, + "maxRssKiB": 390704 + }, + { + "pid": 41547, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 394461184, + "heapTotal": 240877568, + "heapUsed": 103460672, + "external": 6032226, + "arrayBuffers": 2183442, + "maxRssKiB": 385344 + }, + { + "pid": 41548, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 401866752, + "heapTotal": 242450432, + "heapUsed": 104949912, + "external": 6023735, + "arrayBuffers": 2183442, + "maxRssKiB": 392576 + }, + { + "pid": 41547, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 396460032, + "heapTotal": 242450432, + "heapUsed": 104934752, + "external": 6023723, + "arrayBuffers": 2201407, + "maxRssKiB": 387296 + }, + { + "pid": 41546, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 411992064, + "heapTotal": 243236864, + "heapUsed": 105529888, + "external": 6027733, + "arrayBuffers": 2183442, + "maxRssKiB": 402464 + }, + { + "pid": 41548, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 403570688, + "heapTotal": 244285440, + "heapUsed": 106500312, + "external": 6031927, + "arrayBuffers": 2183442, + "maxRssKiB": 394240 + }, + { + "pid": 41547, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 398163968, + "heapTotal": 244285440, + "heapUsed": 106493888, + "external": 6031915, + "arrayBuffers": 2183442, + "maxRssKiB": 388960 + }, + { + "pid": 41546, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 421478400, + "heapTotal": 245596160, + "heapUsed": 108037696, + "external": 6035556, + "arrayBuffers": 2210372, + "maxRssKiB": 411728 + }, + { + "pid": 41548, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 405618688, + "heapTotal": 245858304, + "heapUsed": 108263392, + "external": 6023734, + "arrayBuffers": 2183442, + "maxRssKiB": 396224 + }, + { + "pid": 41547, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 400261120, + "heapTotal": 246120448, + "heapUsed": 108258016, + "external": 6023722, + "arrayBuffers": 2183442, + "maxRssKiB": 391008 + }, + { + "pid": 41547, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 402243584, + "heapTotal": 247693312, + "heapUsed": 109906992, + "external": 6031914, + "arrayBuffers": 2183442, + "maxRssKiB": 392944 + }, + { + "pid": 41548, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 407535616, + "heapTotal": 248217600, + "heapUsed": 109911960, + "external": 6031926, + "arrayBuffers": 2183442, + "maxRssKiB": 398112 + }, + { + "pid": 41546, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 432340992, + "heapTotal": 248217600, + "heapUsed": 110644272, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 422336 + }, + { + "pid": 41546, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 434356224, + "heapTotal": 250052608, + "heapUsed": 112414904, + "external": 6031912, + "arrayBuffers": 2183442, + "maxRssKiB": 424304 + }, + { + "pid": 41546, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 436387840, + "heapTotal": 251887616, + "heapUsed": 114066488, + "external": 6023720, + "arrayBuffers": 2183442, + "maxRssKiB": 426288 + }, + { + "pid": 41549, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 116604928, + "heapTotal": 61489152, + "heapUsed": 25981696, + "external": 5699073, + "arrayBuffers": 2064962, + "maxRssKiB": 113984 + }, + { + "pid": 41549, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 117161984, + "heapTotal": 62799872, + "heapUsed": 27052488, + "external": 5699678, + "arrayBuffers": 2064962, + "maxRssKiB": 114416 + } + ], + "files": [ + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 1.849 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 1.853 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 1.972 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 3.074 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/history.integration.test.ts (104 MB heap size)", + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (104 MB heap size)", + "PASS build tests/integration/build/override/newtab.integration.test.ts (108 MB heap size)", + "PASS build tests/integration/build/override/competing.integration.test.ts (25 MB heap size)" + ] + }, + { + "id": "17-memory-1-cli", + "phase": "memory", + "mode": "cli", + "workers": 1, + "wallSeconds": 23.44392174999998, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 161693696, + "heapTotal": 106381312, + "heapUsed": 35468448, + "external": 11885796, + "arrayBuffers": 2056767, + "maxRssKiB": 158032 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 161972224, + "heapTotal": 107167744, + "heapUsed": 36457672, + "external": 11894026, + "arrayBuffers": 2064959, + "maxRssKiB": 158176 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 162152448, + "heapTotal": 107429888, + "heapUsed": 37539816, + "external": 11894631, + "arrayBuffers": 2064959, + "maxRssKiB": 158352 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 171180032, + "heapTotal": 110886912, + "heapUsed": 39776392, + "external": 13939335, + "arrayBuffers": 2073151, + "maxRssKiB": 167296 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 171245568, + "heapTotal": 111411200, + "heapUsed": 40808144, + "external": 11910156, + "arrayBuffers": 2081343, + "maxRssKiB": 167296 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 171458560, + "heapTotal": 112197632, + "heapUsed": 41850944, + "external": 11918317, + "arrayBuffers": 2081343, + "maxRssKiB": 167440 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 171868160, + "heapTotal": 113508352, + "heapUsed": 42912480, + "external": 11910154, + "arrayBuffers": 2081343, + "maxRssKiB": 167840 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 172376064, + "heapTotal": 114556928, + "heapUsed": 43951640, + "external": 11918475, + "arrayBuffers": 2081343, + "maxRssKiB": 168336 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 172818432, + "heapTotal": 115605504, + "heapUsed": 44981584, + "external": 11918504, + "arrayBuffers": 2081343, + "maxRssKiB": 168768 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 173129728, + "heapTotal": 116916224, + "heapUsed": 46007376, + "external": 11910127, + "arrayBuffers": 2081343, + "maxRssKiB": 169072 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 173604864, + "heapTotal": 117964800, + "heapUsed": 47027848, + "external": 11918348, + "arrayBuffers": 2081343, + "maxRssKiB": 169536 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 174096384, + "heapTotal": 119013376, + "heapUsed": 48090616, + "external": 11908870, + "arrayBuffers": 2081343, + "maxRssKiB": 170016 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 174653440, + "heapTotal": 120324096, + "heapUsed": 49119544, + "external": 11917062, + "arrayBuffers": 2081343, + "maxRssKiB": 170560 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 176324608, + "heapTotal": 117702656, + "heapUsed": 39983384, + "external": 13947563, + "arrayBuffers": 2073151, + "maxRssKiB": 172768 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 176586752, + "heapTotal": 112459776, + "heapUsed": 40994824, + "external": 11910192, + "arrayBuffers": 2081343, + "maxRssKiB": 172768 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 176603136, + "heapTotal": 112984064, + "heapUsed": 42046504, + "external": 11918353, + "arrayBuffers": 2081343, + "maxRssKiB": 172768 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 176619520, + "heapTotal": 114294784, + "heapUsed": 43086376, + "external": 11910190, + "arrayBuffers": 2081343, + "maxRssKiB": 172768 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 176619520, + "heapTotal": 115081216, + "heapUsed": 44120968, + "external": 11917229, + "arrayBuffers": 2081343, + "maxRssKiB": 172768 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 176619520, + "heapTotal": 116129792, + "heapUsed": 45148688, + "external": 11909036, + "arrayBuffers": 2081343, + "maxRssKiB": 172768 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 176619520, + "heapTotal": 117178368, + "heapUsed": 46175664, + "external": 11917073, + "arrayBuffers": 2081343, + "maxRssKiB": 172768 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 176619520, + "heapTotal": 118489088, + "heapUsed": 47211872, + "external": 11908881, + "arrayBuffers": 2081343, + "maxRssKiB": 172768 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 176619520, + "heapTotal": 119799808, + "heapUsed": 48264368, + "external": 11917072, + "arrayBuffers": 2081343, + "maxRssKiB": 172768 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 176619520, + "heapTotal": 120848384, + "heapUsed": 49293136, + "external": 11917072, + "arrayBuffers": 2081343, + "maxRssKiB": 172768 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 188104704, + "heapTotal": 185860096, + "heapUsed": 40087088, + "external": 13947537, + "arrayBuffers": 2073151, + "maxRssKiB": 184272 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 188317696, + "heapTotal": 179306496, + "heapUsed": 41097656, + "external": 11910166, + "arrayBuffers": 2081343, + "maxRssKiB": 184272 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 188481536, + "heapTotal": 179568640, + "heapUsed": 42138328, + "external": 11918327, + "arrayBuffers": 2081343, + "maxRssKiB": 184272 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 188497920, + "heapTotal": 180617216, + "heapUsed": 43193800, + "external": 11910164, + "arrayBuffers": 2081343, + "maxRssKiB": 184272 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 188514304, + "heapTotal": 182190080, + "heapUsed": 44228192, + "external": 11917221, + "arrayBuffers": 2081343, + "maxRssKiB": 184272 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 188514304, + "heapTotal": 183238656, + "heapUsed": 45256576, + "external": 11909029, + "arrayBuffers": 2081343, + "maxRssKiB": 184272 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 188514304, + "heapTotal": 184549376, + "heapUsed": 46277584, + "external": 11917065, + "arrayBuffers": 2081343, + "maxRssKiB": 184272 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 188514304, + "heapTotal": 185335808, + "heapUsed": 47307440, + "external": 11908873, + "arrayBuffers": 2081343, + "maxRssKiB": 184272 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 188563456, + "heapTotal": 186908672, + "heapUsed": 48358632, + "external": 11917064, + "arrayBuffers": 2081343, + "maxRssKiB": 184272 + }, + { + "pid": 41586, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 188628992, + "heapTotal": 187695104, + "heapUsed": 49385200, + "external": 11917064, + "arrayBuffers": 2081343, + "maxRssKiB": 184272 + } + ], + "files": [ + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 2.085 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 6.9 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 7.597 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 6.191 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/competing.integration.test.ts (35 MB heap size)", + "PASS build tests/integration/build/override/newtab.integration.test.ts (6.9 s, 46 MB heap size)", + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (7.597 s, 47 MB heap size)", + "PASS build tests/integration/build/override/history.integration.test.ts (6.191 s, 47 MB heap size)" + ] + }, + { + "id": "18-handles-1-cli", + "phase": "handles", + "mode": "cli", + "workers": 1, + "wallSeconds": 21.761076791, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 165871616, + "heapTotal": 106381312, + "heapUsed": 36076568, + "external": 11882347, + "arrayBuffers": 2056767, + "maxRssKiB": 162128 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 166477824, + "heapTotal": 107364352, + "heapUsed": 37330328, + "external": 11893808, + "arrayBuffers": 2064959, + "maxRssKiB": 162576 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 166723584, + "heapTotal": 108412928, + "heapUsed": 38415880, + "external": 11901969, + "arrayBuffers": 2064959, + "maxRssKiB": 162816 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 167018496, + "heapTotal": 109985792, + "heapUsed": 39458824, + "external": 11893806, + "arrayBuffers": 2064959, + "maxRssKiB": 163104 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 167788544, + "heapTotal": 111296512, + "heapUsed": 40539688, + "external": 11900845, + "arrayBuffers": 2064959, + "maxRssKiB": 163856 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 168296448, + "heapTotal": 112345088, + "heapUsed": 41569360, + "external": 11892653, + "arrayBuffers": 2064959, + "maxRssKiB": 164352 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 168673280, + "heapTotal": 113393664, + "heapUsed": 42592136, + "external": 11900689, + "arrayBuffers": 2064959, + "maxRssKiB": 164720 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 169476096, + "heapTotal": 114704384, + "heapUsed": 43620704, + "external": 11892497, + "arrayBuffers": 2064959, + "maxRssKiB": 165504 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 170098688, + "heapTotal": 116015104, + "heapUsed": 44687744, + "external": 11900688, + "arrayBuffers": 2064959, + "maxRssKiB": 166112 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 171327488, + "heapTotal": 116801536, + "heapUsed": 45723136, + "external": 11900688, + "arrayBuffers": 2064959, + "maxRssKiB": 167312 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 189710336, + "heapTotal": 120012800, + "heapUsed": 41392752, + "external": 13931143, + "arrayBuffers": 2073151, + "maxRssKiB": 185392 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 190267392, + "heapTotal": 112934912, + "heapUsed": 42384032, + "external": 11910156, + "arrayBuffers": 2081343, + "maxRssKiB": 185808 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 190545920, + "heapTotal": 114245632, + "heapUsed": 43431728, + "external": 11918317, + "arrayBuffers": 2081343, + "maxRssKiB": 186080 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 191070208, + "heapTotal": 115294208, + "heapUsed": 44472032, + "external": 11910154, + "arrayBuffers": 2081343, + "maxRssKiB": 186592 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 191119360, + "heapTotal": 116342784, + "heapUsed": 45521480, + "external": 11918475, + "arrayBuffers": 2081343, + "maxRssKiB": 186640 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 191807488, + "heapTotal": 117391360, + "heapUsed": 46559984, + "external": 11918504, + "arrayBuffers": 2081343, + "maxRssKiB": 187312 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 191807488, + "heapTotal": 118702080, + "heapUsed": 47567760, + "external": 11910127, + "arrayBuffers": 2081343, + "maxRssKiB": 187312 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 192479232, + "heapTotal": 119750656, + "heapUsed": 48591656, + "external": 11918348, + "arrayBuffers": 2081343, + "maxRssKiB": 187968 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 192528384, + "heapTotal": 121061376, + "heapUsed": 49643632, + "external": 11908870, + "arrayBuffers": 2081343, + "maxRssKiB": 188016 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 193118208, + "heapTotal": 122109952, + "heapUsed": 50663688, + "external": 11917062, + "arrayBuffers": 2081343, + "maxRssKiB": 188592 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 206749696, + "heapTotal": 123158528, + "heapUsed": 41537024, + "external": 13947537, + "arrayBuffers": 2073151, + "maxRssKiB": 202480 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 211894272, + "heapTotal": 181354496, + "heapUsed": 42545440, + "external": 11910166, + "arrayBuffers": 2081343, + "maxRssKiB": 206928 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 212353024, + "heapTotal": 182140928, + "heapUsed": 43581616, + "external": 11918327, + "arrayBuffers": 2081343, + "maxRssKiB": 207376 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 212910080, + "heapTotal": 183189504, + "heapUsed": 44632432, + "external": 11910164, + "arrayBuffers": 2081343, + "maxRssKiB": 207920 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 213270528, + "heapTotal": 184238080, + "heapUsed": 45666024, + "external": 11917221, + "arrayBuffers": 2081343, + "maxRssKiB": 208272 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 213794816, + "heapTotal": 185548800, + "heapUsed": 46690312, + "external": 11909029, + "arrayBuffers": 2081343, + "maxRssKiB": 208784 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 214286336, + "heapTotal": 186859520, + "heapUsed": 47731544, + "external": 11917065, + "arrayBuffers": 2081343, + "maxRssKiB": 209264 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 214564864, + "heapTotal": 187645952, + "heapUsed": 48747304, + "external": 11908873, + "arrayBuffers": 2081343, + "maxRssKiB": 209536 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 214859776, + "heapTotal": 188694528, + "heapUsed": 49801536, + "external": 11917064, + "arrayBuffers": 2081343, + "maxRssKiB": 209824 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 215449600, + "heapTotal": 189743104, + "heapUsed": 50834960, + "external": 11917064, + "arrayBuffers": 2081343, + "maxRssKiB": 210400 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 220905472, + "heapTotal": 189480960, + "heapUsed": 41751608, + "external": 13947812, + "arrayBuffers": 2073151, + "maxRssKiB": 216384 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 220905472, + "heapTotal": 180568064, + "heapUsed": 42750744, + "external": 11910410, + "arrayBuffers": 2081343, + "maxRssKiB": 216384 + }, + { + "pid": 41970, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 220905472, + "heapTotal": 181616640, + "heapUsed": 43787024, + "external": 11911015, + "arrayBuffers": 2081343, + "maxRssKiB": 216384 + } + ], + "files": [ + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 6.519 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 6.524 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 6.242 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 1.889 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (6.519 s, 43 MB heap size)", + "PASS build tests/integration/build/override/newtab.integration.test.ts (6.524 s, 48 MB heap size)", + "PASS build tests/integration/build/override/history.integration.test.ts (6.242 s, 48 MB heap size)", + "PASS build tests/integration/build/override/competing.integration.test.ts (41 MB heap size)" + ] + }, + { + "id": "19-memory-1-in-process", + "phase": "memory", + "mode": "in-process", + "workers": 1, + "wallSeconds": 4.619498250000004, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 382287872, + "heapTotal": 243826688, + "heapUsed": 102512544, + "external": 12612904, + "arrayBuffers": 2646518, + "maxRssKiB": 373456 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 395968512, + "heapTotal": 244613120, + "heapUsed": 105092528, + "external": 12168661, + "arrayBuffers": 2183439, + "maxRssKiB": 386816 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 404586496, + "heapTotal": 246710272, + "heapUsed": 107786544, + "external": 12168601, + "arrayBuffers": 2183439, + "maxRssKiB": 395232 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 414040064, + "heapTotal": 248807424, + "heapUsed": 110398928, + "external": 12176851, + "arrayBuffers": 2183439, + "maxRssKiB": 404464 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 425115648, + "heapTotal": 251953152, + "heapUsed": 113039992, + "external": 12177106, + "arrayBuffers": 2183439, + "maxRssKiB": 415280 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 435077120, + "heapTotal": 253526016, + "heapUsed": 115651024, + "external": 12168972, + "arrayBuffers": 2183439, + "maxRssKiB": 425008 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 445759488, + "heapTotal": 256671744, + "heapUsed": 118162560, + "external": 12176795, + "arrayBuffers": 2183439, + "maxRssKiB": 435440 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 454836224, + "heapTotal": 259293184, + "heapUsed": 120761616, + "external": 12168661, + "arrayBuffers": 2183439, + "maxRssKiB": 444304 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 456802304, + "heapTotal": 261390336, + "heapUsed": 122538040, + "external": 12173151, + "arrayBuffers": 2183439, + "maxRssKiB": 446224 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 458801152, + "heapTotal": 262438912, + "heapUsed": 124176056, + "external": 12164959, + "arrayBuffers": 2183439, + "maxRssKiB": 448176 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 470155264, + "heapTotal": 266420224, + "heapUsed": 120976144, + "external": 14214250, + "arrayBuffers": 2199823, + "maxRssKiB": 459264 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 478117888, + "heapTotal": 261701632, + "heapUsed": 123518624, + "external": 12193292, + "arrayBuffers": 2208015, + "maxRssKiB": 467040 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 484982784, + "heapTotal": 264323072, + "heapUsed": 126064360, + "external": 12193232, + "arrayBuffers": 2208015, + "maxRssKiB": 473744 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 492748800, + "heapTotal": 266682368, + "heapUsed": 128592264, + "external": 12201482, + "arrayBuffers": 2208015, + "maxRssKiB": 481328 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 493895680, + "heapTotal": 268255232, + "heapUsed": 130105368, + "external": 12189861, + "arrayBuffers": 2208015, + "maxRssKiB": 482448 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 495599616, + "heapTotal": 269828096, + "heapUsed": 131632168, + "external": 12198053, + "arrayBuffers": 2208015, + "maxRssKiB": 484112 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 497369088, + "heapTotal": 271925248, + "heapUsed": 133153368, + "external": 12189550, + "arrayBuffers": 2208015, + "maxRssKiB": 485840 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 499253248, + "heapTotal": 273235968, + "heapUsed": 134644752, + "external": 12197742, + "arrayBuffers": 2208015, + "maxRssKiB": 487680 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 501088256, + "heapTotal": 274808832, + "heapUsed": 136194320, + "external": 12189549, + "arrayBuffers": 2208015, + "maxRssKiB": 489472 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 502906880, + "heapTotal": 276643840, + "heapUsed": 137714552, + "external": 12197741, + "arrayBuffers": 2208015, + "maxRssKiB": 491248 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 511229952, + "heapTotal": 278216704, + "heapUsed": 130042088, + "external": 14230592, + "arrayBuffers": 2191631, + "maxRssKiB": 499824 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 519421952, + "heapTotal": 270876672, + "heapUsed": 132566904, + "external": 12185058, + "arrayBuffers": 2199823, + "maxRssKiB": 507376 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 527040512, + "heapTotal": 273235968, + "heapUsed": 135114960, + "external": 12184998, + "arrayBuffers": 2199823, + "maxRssKiB": 514816 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 534069248, + "heapTotal": 275857408, + "heapUsed": 137640872, + "external": 12193248, + "arrayBuffers": 2199823, + "maxRssKiB": 521680 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 534839296, + "heapTotal": 277430272, + "heapUsed": 139141648, + "external": 12181657, + "arrayBuffers": 2199823, + "maxRssKiB": 522432 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 535543808, + "heapTotal": 279003136, + "heapUsed": 140642920, + "external": 12189849, + "arrayBuffers": 2199823, + "maxRssKiB": 523120 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 536150016, + "heapTotal": 280576000, + "heapUsed": 142127480, + "external": 12181346, + "arrayBuffers": 2199823, + "maxRssKiB": 523712 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 537346048, + "heapTotal": 282148864, + "heapUsed": 143603256, + "external": 12189538, + "arrayBuffers": 2199823, + "maxRssKiB": 524880 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 539148288, + "heapTotal": 283721728, + "heapUsed": 145115144, + "external": 12181345, + "arrayBuffers": 2199823, + "maxRssKiB": 526640 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 540901376, + "heapTotal": 285294592, + "heapUsed": 146610264, + "external": 12189537, + "arrayBuffers": 2199823, + "maxRssKiB": 528352 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 542130176, + "heapTotal": 285294592, + "heapUsed": 137442704, + "external": 14212709, + "arrayBuffers": 2191631, + "maxRssKiB": 530000 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 542162944, + "heapTotal": 276643840, + "heapUsed": 138469728, + "external": 12175307, + "arrayBuffers": 2199823, + "maxRssKiB": 530000 + }, + { + "pid": 42319, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 542425088, + "heapTotal": 277692416, + "heapUsed": 139511656, + "external": 12175912, + "arrayBuffers": 2199823, + "maxRssKiB": 530000 + } + ], + "files": [ + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 1.23 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 0.477 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 0.494 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 1.858 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/newtab.integration.test.ts (118 MB heap size)", + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (131 MB heap size)", + "PASS build tests/integration/build/override/history.integration.test.ts (139 MB heap size)", + "PASS build tests/integration/build/override/competing.integration.test.ts (133 MB heap size)" + ] + }, + { + "id": "20-handles-1-in-process", + "phase": "handles", + "mode": "in-process", + "workers": 1, + "wallSeconds": 4.875271166999999, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 168148992, + "heapTotal": 107167744, + "heapUsed": 36108896, + "external": 11877396, + "arrayBuffers": 2056767, + "maxRssKiB": 164336 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 168607744, + "heapTotal": 108150784, + "heapUsed": 37365448, + "external": 11894026, + "arrayBuffers": 2064959, + "maxRssKiB": 164656 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 168886272, + "heapTotal": 108937216, + "heapUsed": 38512304, + "external": 11894631, + "arrayBuffers": 2064959, + "maxRssKiB": 164928 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 454098944, + "heapTotal": 262356992, + "heapUsed": 109691472, + "external": 12214802, + "arrayBuffers": 2183439, + "maxRssKiB": 443584 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 466239488, + "heapTotal": 255279104, + "heapUsed": 112288008, + "external": 12176853, + "arrayBuffers": 2191631, + "maxRssKiB": 455440 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 476725248, + "heapTotal": 256065536, + "heapUsed": 114982544, + "external": 12176793, + "arrayBuffers": 2191631, + "maxRssKiB": 465680 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 485670912, + "heapTotal": 257900544, + "heapUsed": 117612120, + "external": 12185043, + "arrayBuffers": 2191631, + "maxRssKiB": 474416 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 494108672, + "heapTotal": 259735552, + "heapUsed": 120253496, + "external": 12185298, + "arrayBuffers": 2191631, + "maxRssKiB": 482656 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 502595584, + "heapTotal": 262094848, + "heapUsed": 122849880, + "external": 12177164, + "arrayBuffers": 2191631, + "maxRssKiB": 490944 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 511033344, + "heapTotal": 264716288, + "heapUsed": 125351400, + "external": 12184987, + "arrayBuffers": 2191631, + "maxRssKiB": 499184 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 518963200, + "heapTotal": 267075584, + "heapUsed": 127957744, + "external": 12176853, + "arrayBuffers": 2191631, + "maxRssKiB": 506928 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 520945664, + "heapTotal": 268910592, + "heapUsed": 129720816, + "external": 12181343, + "arrayBuffers": 2191631, + "maxRssKiB": 508864 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 523075584, + "heapTotal": 270483456, + "heapUsed": 131335456, + "external": 12173151, + "arrayBuffers": 2191631, + "maxRssKiB": 510944 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 534921216, + "heapTotal": 274153472, + "heapUsed": 123791088, + "external": 14222400, + "arrayBuffers": 2199823, + "maxRssKiB": 522960 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 543014912, + "heapTotal": 267862016, + "heapUsed": 126373192, + "external": 12193250, + "arrayBuffers": 2208015, + "maxRssKiB": 530496 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 550354944, + "heapTotal": 269172736, + "heapUsed": 128939904, + "external": 12193190, + "arrayBuffers": 2208015, + "maxRssKiB": 537584 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 557727744, + "heapTotal": 271007744, + "heapUsed": 131452696, + "external": 12201440, + "arrayBuffers": 2208015, + "maxRssKiB": 544784 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 558317568, + "heapTotal": 272318464, + "heapUsed": 132963016, + "external": 12189849, + "arrayBuffers": 2208015, + "maxRssKiB": 545360 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 559005696, + "heapTotal": 273891328, + "heapUsed": 134488872, + "external": 12198041, + "arrayBuffers": 2208015, + "maxRssKiB": 546032 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 559726592, + "heapTotal": 275726336, + "heapUsed": 136037496, + "external": 12189538, + "arrayBuffers": 2208015, + "maxRssKiB": 546736 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 561086464, + "heapTotal": 277561344, + "heapUsed": 137516320, + "external": 12197730, + "arrayBuffers": 2208015, + "maxRssKiB": 548064 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 562741248, + "heapTotal": 278872064, + "heapUsed": 139067456, + "external": 12189537, + "arrayBuffers": 2208015, + "maxRssKiB": 549680 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 564494336, + "heapTotal": 280444928, + "heapUsed": 140580480, + "external": 12197729, + "arrayBuffers": 2208015, + "maxRssKiB": 551392 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 573669376, + "heapTotal": 283066368, + "heapUsed": 132856128, + "external": 14230634, + "arrayBuffers": 2191631, + "maxRssKiB": 560800 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 581861376, + "heapTotal": 274153472, + "heapUsed": 135362088, + "external": 12185100, + "arrayBuffers": 2199823, + "maxRssKiB": 568352 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 588005376, + "heapTotal": 277037056, + "heapUsed": 137930976, + "external": 12185040, + "arrayBuffers": 2199823, + "maxRssKiB": 574352 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 594477056, + "heapTotal": 279396352, + "heapUsed": 140445320, + "external": 12193290, + "arrayBuffers": 2199823, + "maxRssKiB": 580672 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 595230720, + "heapTotal": 280444928, + "heapUsed": 141972784, + "external": 12181669, + "arrayBuffers": 2199823, + "maxRssKiB": 581408 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 595836928, + "heapTotal": 282017792, + "heapUsed": 143459392, + "external": 12189861, + "arrayBuffers": 2199823, + "maxRssKiB": 582000 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 596541440, + "heapTotal": 283590656, + "heapUsed": 144942256, + "external": 12181358, + "arrayBuffers": 2199823, + "maxRssKiB": 582688 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 597966848, + "heapTotal": 285425664, + "heapUsed": 146420272, + "external": 12189550, + "arrayBuffers": 2199823, + "maxRssKiB": 584080 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 599883776, + "heapTotal": 286998528, + "heapUsed": 147930736, + "external": 12181357, + "arrayBuffers": 2199823, + "maxRssKiB": 585952 + }, + { + "pid": 42393, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 601522176, + "heapTotal": 288571392, + "heapUsed": 149438656, + "external": 12189549, + "arrayBuffers": 2199823, + "maxRssKiB": 587552 + } + ], + "files": [ + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 1.88 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 1.215 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 0.638 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 0.638 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/competing.integration.test.ts (36 MB heap size)", + "PASS build tests/integration/build/override/newtab.integration.test.ts (125 MB heap size)", + "PASS build tests/integration/build/override/history.integration.test.ts (133 MB heap size)", + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (142 MB heap size)" + ] + }, + { + "id": "21-limited-2-in-process", + "phase": "limited", + "mode": "in-process", + "workers": 2, + "wallSeconds": 3.032205500000011, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 42543, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 115720192, + "heapTotal": 61227008, + "heapUsed": 24929016, + "external": 5717914, + "arrayBuffers": 2056770, + "maxRssKiB": 113136 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 357695488, + "heapTotal": 231702528, + "heapUsed": 92238096, + "external": 6002361, + "arrayBuffers": 2175250, + "maxRssKiB": 349440 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 371785728, + "heapTotal": 233275392, + "heapUsed": 94973512, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 363200 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 383205376, + "heapTotal": 235634688, + "heapUsed": 97696136, + "external": 6027362, + "arrayBuffers": 2183442, + "maxRssKiB": 374352 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 393265152, + "heapTotal": 237993984, + "heapUsed": 100285432, + "external": 6035612, + "arrayBuffers": 2183442, + "maxRssKiB": 384176 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 404668416, + "heapTotal": 240353280, + "heapUsed": 102925056, + "external": 6035867, + "arrayBuffers": 2183442, + "maxRssKiB": 395312 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 413958144, + "heapTotal": 242974720, + "heapUsed": 105530648, + "external": 6027733, + "arrayBuffers": 2183442, + "maxRssKiB": 404384 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 424443904, + "heapTotal": 245596160, + "heapUsed": 108038160, + "external": 6035556, + "arrayBuffers": 2183442, + "maxRssKiB": 414624 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 435109888, + "heapTotal": 248217600, + "heapUsed": 110650088, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 425040 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 437174272, + "heapTotal": 250052608, + "heapUsed": 112421432, + "external": 6031912, + "arrayBuffers": 2183442, + "maxRssKiB": 427056 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 439189504, + "heapTotal": 251363328, + "heapUsed": 114075568, + "external": 6023720, + "arrayBuffers": 2183442, + "maxRssKiB": 429024 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 450297856, + "heapTotal": 255344640, + "heapUsed": 110864120, + "external": 8091942, + "arrayBuffers": 2199826, + "maxRssKiB": 439872 + }, + { + "pid": 42543, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 116506624, + "heapTotal": 61751296, + "heapUsed": 25988176, + "external": 5699073, + "arrayBuffers": 2064962, + "maxRssKiB": 113888 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 457998336, + "heapTotal": 254296064, + "heapUsed": 113408552, + "external": 6052011, + "arrayBuffers": 2208018, + "maxRssKiB": 447392 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 464617472, + "heapTotal": 255344640, + "heapUsed": 115960744, + "external": 6051951, + "arrayBuffers": 2208018, + "maxRssKiB": 453856 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 471072768, + "heapTotal": 256917504, + "heapUsed": 118496192, + "external": 6060201, + "arrayBuffers": 2208018, + "maxRssKiB": 460160 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 472711168, + "heapTotal": 258752512, + "heapUsed": 119992272, + "external": 6048610, + "arrayBuffers": 2208018, + "maxRssKiB": 461760 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 474447872, + "heapTotal": 260063232, + "heapUsed": 121520112, + "external": 6056802, + "arrayBuffers": 2208018, + "maxRssKiB": 463456 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 476135424, + "heapTotal": 261636096, + "heapUsed": 123013808, + "external": 6048299, + "arrayBuffers": 2208018, + "maxRssKiB": 465104 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 477741056, + "heapTotal": 262946816, + "heapUsed": 124504456, + "external": 6056491, + "arrayBuffers": 2208018, + "maxRssKiB": 466672 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 479248384, + "heapTotal": 264257536, + "heapUsed": 126057688, + "external": 6048298, + "arrayBuffers": 2208018, + "maxRssKiB": 468144 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 480772096, + "heapTotal": 265830400, + "heapUsed": 127577712, + "external": 6056490, + "arrayBuffers": 2208018, + "maxRssKiB": 469632 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 490455040, + "heapTotal": 268451840, + "heapUsed": 119890320, + "external": 8108370, + "arrayBuffers": 2191634, + "maxRssKiB": 479536 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 497238016, + "heapTotal": 260587520, + "heapUsed": 122403584, + "external": 6043861, + "arrayBuffers": 2199826, + "maxRssKiB": 485712 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 505020416, + "heapTotal": 262684672, + "heapUsed": 124961608, + "external": 6043801, + "arrayBuffers": 2199826, + "maxRssKiB": 493312 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 512000000, + "heapTotal": 265043968, + "heapUsed": 127482160, + "external": 6052051, + "arrayBuffers": 2199826, + "maxRssKiB": 500128 + }, + { + "pid": 42543, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 117161984, + "heapTotal": 63062016, + "heapUsed": 27055200, + "external": 5699678, + "arrayBuffers": 2064962, + "maxRssKiB": 114416 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 512622592, + "heapTotal": 266616832, + "heapUsed": 128987200, + "external": 6040430, + "arrayBuffers": 2199826, + "maxRssKiB": 500736 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 513228800, + "heapTotal": 268451840, + "heapUsed": 130487920, + "external": 6048622, + "arrayBuffers": 2199826, + "maxRssKiB": 501328 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 514031616, + "heapTotal": 269762560, + "heapUsed": 131968744, + "external": 6040119, + "arrayBuffers": 2199826, + "maxRssKiB": 502112 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 515620864, + "heapTotal": 271597568, + "heapUsed": 133446680, + "external": 6048311, + "arrayBuffers": 2199826, + "maxRssKiB": 503664 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 517308416, + "heapTotal": 272908288, + "heapUsed": 134953840, + "external": 6040118, + "arrayBuffers": 2199826, + "maxRssKiB": 505312 + }, + { + "pid": 42544, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 518897664, + "heapTotal": 274743296, + "heapUsed": 136450920, + "external": 6048310, + "arrayBuffers": 2199826, + "maxRssKiB": 506864 + } + ], + "files": [ + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 1.187 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 0.47 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 1.957 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 0.487 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/newtab.integration.test.ts (108 MB heap size)", + "PASS build tests/integration/build/override/history.integration.test.ts (121 MB heap size)", + "PASS build tests/integration/build/override/competing.integration.test.ts (25 MB heap size)", + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (130 MB heap size)" + ] + }, + { + "id": "22-limited-2-in-process", + "phase": "limited", + "mode": "in-process", + "workers": 2, + "wallSeconds": 3.0276612920000043, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 42635, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 116391936, + "heapTotal": 60702720, + "heapUsed": 24924272, + "external": 5717914, + "arrayBuffers": 2056770, + "maxRssKiB": 113792 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 358645760, + "heapTotal": 230916096, + "heapUsed": 92238840, + "external": 6002361, + "arrayBuffers": 2175250, + "maxRssKiB": 350368 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 371736576, + "heapTotal": 233013248, + "heapUsed": 94966872, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 363152 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 383713280, + "heapTotal": 235372544, + "heapUsed": 97691120, + "external": 6027362, + "arrayBuffers": 2183442, + "maxRssKiB": 374848 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 394821632, + "heapTotal": 237731840, + "heapUsed": 100284408, + "external": 6035612, + "arrayBuffers": 2183442, + "maxRssKiB": 385696 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 404750336, + "heapTotal": 240615424, + "heapUsed": 102923600, + "external": 6035867, + "arrayBuffers": 2183442, + "maxRssKiB": 395392 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 415596544, + "heapTotal": 243236864, + "heapUsed": 105530160, + "external": 6027733, + "arrayBuffers": 2183442, + "maxRssKiB": 405984 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 425443328, + "heapTotal": 245858304, + "heapUsed": 108042976, + "external": 6035556, + "arrayBuffers": 2183442, + "maxRssKiB": 415600 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 435568640, + "heapTotal": 248217600, + "heapUsed": 110650168, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 425488 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 437518336, + "heapTotal": 250052608, + "heapUsed": 112420784, + "external": 6031912, + "arrayBuffers": 2183442, + "maxRssKiB": 427392 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 439549952, + "heapTotal": 252149760, + "heapUsed": 114072224, + "external": 6023720, + "arrayBuffers": 2183442, + "maxRssKiB": 429376 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 452444160, + "heapTotal": 256655360, + "heapUsed": 110869776, + "external": 8091986, + "arrayBuffers": 2199826, + "maxRssKiB": 441968 + }, + { + "pid": 42635, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 117063680, + "heapTotal": 61489152, + "heapUsed": 25955880, + "external": 5699073, + "arrayBuffers": 2064962, + "maxRssKiB": 114320 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 459227136, + "heapTotal": 252198912, + "heapUsed": 113414960, + "external": 6052053, + "arrayBuffers": 2208018, + "maxRssKiB": 448592 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 466878464, + "heapTotal": 254033920, + "heapUsed": 115975944, + "external": 6051993, + "arrayBuffers": 2208018, + "maxRssKiB": 456064 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 474234880, + "heapTotal": 256131072, + "heapUsed": 118499256, + "external": 6060243, + "arrayBuffers": 2208018, + "maxRssKiB": 463248 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 475054080, + "heapTotal": 257966080, + "heapUsed": 120010008, + "external": 6048622, + "arrayBuffers": 2208018, + "maxRssKiB": 464048 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 476495872, + "heapTotal": 259538944, + "heapUsed": 121535832, + "external": 6056814, + "arrayBuffers": 2208018, + "maxRssKiB": 465456 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 478183424, + "heapTotal": 261373952, + "heapUsed": 123056024, + "external": 6048311, + "arrayBuffers": 2208018, + "maxRssKiB": 467104 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 479952896, + "heapTotal": 262684672, + "heapUsed": 124546856, + "external": 6056503, + "arrayBuffers": 2208018, + "maxRssKiB": 468832 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 481722368, + "heapTotal": 264257536, + "heapUsed": 126099496, + "external": 6048310, + "arrayBuffers": 2208018, + "maxRssKiB": 470560 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 483491840, + "heapTotal": 265830400, + "heapUsed": 127617792, + "external": 6056502, + "arrayBuffers": 2208018, + "maxRssKiB": 472288 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 492191744, + "heapTotal": 267141120, + "heapUsed": 119929568, + "external": 8108326, + "arrayBuffers": 2191634, + "maxRssKiB": 481232 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 500006912, + "heapTotal": 260325376, + "heapUsed": 122443000, + "external": 6043819, + "arrayBuffers": 2199826, + "maxRssKiB": 488416 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 506216448, + "heapTotal": 262422528, + "heapUsed": 125000152, + "external": 6043759, + "arrayBuffers": 2199826, + "maxRssKiB": 494480 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 514326528, + "heapTotal": 265568256, + "heapUsed": 127523064, + "external": 6052009, + "arrayBuffers": 2199826, + "maxRssKiB": 502400 + }, + { + "pid": 42635, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 117587968, + "heapTotal": 63062016, + "heapUsed": 27029664, + "external": 5699678, + "arrayBuffers": 2064962, + "maxRssKiB": 114832 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 515178496, + "heapTotal": 266878976, + "heapUsed": 129026488, + "external": 6040418, + "arrayBuffers": 2199826, + "maxRssKiB": 503232 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 515817472, + "heapTotal": 268451840, + "heapUsed": 130527112, + "external": 6048610, + "arrayBuffers": 2199826, + "maxRssKiB": 503856 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 516505600, + "heapTotal": 270024704, + "heapUsed": 132007776, + "external": 6040107, + "arrayBuffers": 2199826, + "maxRssKiB": 504528 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 517505024, + "heapTotal": 271597568, + "heapUsed": 133485648, + "external": 6048299, + "arrayBuffers": 2199826, + "maxRssKiB": 505504 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 519421952, + "heapTotal": 273432576, + "heapUsed": 134993992, + "external": 6040106, + "arrayBuffers": 2199826, + "maxRssKiB": 507376 + }, + { + "pid": 42636, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 521027584, + "heapTotal": 274743296, + "heapUsed": 136490808, + "external": 6048298, + "arrayBuffers": 2199826, + "maxRssKiB": 508944 + } + ], + "files": [ + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 1.192 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 0.455 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 1.952 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 0.48 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/newtab.integration.test.ts (108 MB heap size)", + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (121 MB heap size)", + "PASS build tests/integration/build/override/competing.integration.test.ts (25 MB heap size)", + "PASS build tests/integration/build/override/history.integration.test.ts (130 MB heap size)" + ] + }, + { + "id": "23-limited-2-in-process", + "phase": "limited", + "mode": "in-process", + "workers": 2, + "wallSeconds": 3.1493347920000088, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 42705, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 117456896, + "heapTotal": 60440576, + "heapUsed": 24954360, + "external": 5717914, + "arrayBuffers": 2056770, + "maxRssKiB": 114944 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 357875712, + "heapTotal": 231440384, + "heapUsed": 92233416, + "external": 6002361, + "arrayBuffers": 2175250, + "maxRssKiB": 349616 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 372162560, + "heapTotal": 232751104, + "heapUsed": 94970840, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 363568 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 384401408, + "heapTotal": 235110400, + "heapUsed": 97692840, + "external": 6027362, + "arrayBuffers": 2183442, + "maxRssKiB": 375520 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 395509760, + "heapTotal": 237993984, + "heapUsed": 100283696, + "external": 6035612, + "arrayBuffers": 2183442, + "maxRssKiB": 386368 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 405635072, + "heapTotal": 240353280, + "heapUsed": 102923200, + "external": 6035867, + "arrayBuffers": 2183442, + "maxRssKiB": 396256 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 414105600, + "heapTotal": 242974720, + "heapUsed": 105530040, + "external": 6027733, + "arrayBuffers": 2183442, + "maxRssKiB": 404528 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 424263680, + "heapTotal": 245596160, + "heapUsed": 108032936, + "external": 6035556, + "arrayBuffers": 2183442, + "maxRssKiB": 414448 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 433684480, + "heapTotal": 248217600, + "heapUsed": 110649368, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 423648 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 435683328, + "heapTotal": 250052608, + "heapUsed": 112420104, + "external": 6031912, + "arrayBuffers": 2183442, + "maxRssKiB": 425600 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 437403648, + "heapTotal": 251363328, + "heapUsed": 114071720, + "external": 6023720, + "arrayBuffers": 2183442, + "maxRssKiB": 427280 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 449806336, + "heapTotal": 255868928, + "heapUsed": 110870624, + "external": 8091942, + "arrayBuffers": 2199826, + "maxRssKiB": 439392 + }, + { + "pid": 42705, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 118013952, + "heapTotal": 61227008, + "heapUsed": 25980752, + "external": 5699073, + "arrayBuffers": 2064962, + "maxRssKiB": 115248 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 456884224, + "heapTotal": 252723200, + "heapUsed": 113419128, + "external": 6052011, + "arrayBuffers": 2208018, + "maxRssKiB": 446304 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 465125376, + "heapTotal": 254558208, + "heapUsed": 115974304, + "external": 6051951, + "arrayBuffers": 2208018, + "maxRssKiB": 454352 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 471941120, + "heapTotal": 255868928, + "heapUsed": 118495800, + "external": 6060201, + "arrayBuffers": 2208018, + "maxRssKiB": 461008 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 473137152, + "heapTotal": 257703936, + "heapUsed": 120008408, + "external": 6048610, + "arrayBuffers": 2208018, + "maxRssKiB": 462176 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 474923008, + "heapTotal": 259538944, + "heapUsed": 121533960, + "external": 6056802, + "arrayBuffers": 2208018, + "maxRssKiB": 463920 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 476676096, + "heapTotal": 261111808, + "heapUsed": 123054152, + "external": 6048299, + "arrayBuffers": 2208018, + "maxRssKiB": 465632 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 478543872, + "heapTotal": 262684672, + "heapUsed": 124544936, + "external": 6056491, + "arrayBuffers": 2208018, + "maxRssKiB": 467456 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 480247808, + "heapTotal": 264257536, + "heapUsed": 126097560, + "external": 6048298, + "arrayBuffers": 2208018, + "maxRssKiB": 469120 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 482099200, + "heapTotal": 266092544, + "heapUsed": 127616592, + "external": 6056490, + "arrayBuffers": 2208018, + "maxRssKiB": 470928 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 491634688, + "heapTotal": 267927552, + "heapUsed": 119927784, + "external": 8108370, + "arrayBuffers": 2191634, + "maxRssKiB": 480688 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 498843648, + "heapTotal": 259801088, + "heapUsed": 122440888, + "external": 6043861, + "arrayBuffers": 2199826, + "maxRssKiB": 487280 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 504299520, + "heapTotal": 262422528, + "heapUsed": 125001280, + "external": 6043801, + "arrayBuffers": 2199826, + "maxRssKiB": 492608 + }, + { + "pid": 42705, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 118587392, + "heapTotal": 62799872, + "heapUsed": 27053880, + "external": 5699678, + "arrayBuffers": 2064962, + "maxRssKiB": 115808 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 511000576, + "heapTotal": 265043968, + "heapUsed": 127519608, + "external": 6052051, + "arrayBuffers": 2199826, + "maxRssKiB": 499152 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 511787008, + "heapTotal": 266878976, + "heapUsed": 129024616, + "external": 6040430, + "arrayBuffers": 2199826, + "maxRssKiB": 499920 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 512409600, + "heapTotal": 268713984, + "heapUsed": 130525256, + "external": 6048622, + "arrayBuffers": 2199826, + "maxRssKiB": 500528 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 513753088, + "heapTotal": 270024704, + "heapUsed": 132006336, + "external": 6040119, + "arrayBuffers": 2199826, + "maxRssKiB": 501840 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 515555328, + "heapTotal": 271859712, + "heapUsed": 133483968, + "external": 6048311, + "arrayBuffers": 2199826, + "maxRssKiB": 503600 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 517275648, + "heapTotal": 273694720, + "heapUsed": 134991104, + "external": 6040118, + "arrayBuffers": 2199826, + "maxRssKiB": 505280 + }, + { + "pid": 42706, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 518864896, + "heapTotal": 274743296, + "heapUsed": 136488264, + "external": 6048310, + "arrayBuffers": 2199826, + "maxRssKiB": 506832 + } + ], + "files": [ + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 1.214 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 0.49 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 1.985 + }, + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 0.5 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/newtab.integration.test.ts (108 MB heap size)", + "PASS build tests/integration/build/override/history.integration.test.ts (121 MB heap size)", + "PASS build tests/integration/build/override/competing.integration.test.ts (25 MB heap size)", + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (130 MB heap size)" + ] + }, + { + "id": "24-limited-8-in-process", + "phase": "limited", + "mode": "in-process", + "workers": 8, + "wallSeconds": 2.467169709000009, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 42791, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 116031488, + "heapTotal": 60440576, + "heapUsed": 24933440, + "external": 5717913, + "arrayBuffers": 2056770, + "maxRssKiB": 113472 + }, + { + "pid": 42794, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 357367808, + "heapTotal": 230653952, + "heapUsed": 92231832, + "external": 6002373, + "arrayBuffers": 2175250, + "maxRssKiB": 349120 + }, + { + "pid": 42792, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 358514688, + "heapTotal": 231702528, + "heapUsed": 92238728, + "external": 6002361, + "arrayBuffers": 2175250, + "maxRssKiB": 350240 + }, + { + "pid": 42793, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 359399424, + "heapTotal": 231702528, + "heapUsed": 92237848, + "external": 6002413, + "arrayBuffers": 2175250, + "maxRssKiB": 351104 + }, + { + "pid": 42794, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 369213440, + "heapTotal": 233013248, + "heapUsed": 94966576, + "external": 6027435, + "arrayBuffers": 2183442, + "maxRssKiB": 360688 + }, + { + "pid": 42792, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 370655232, + "heapTotal": 233275392, + "heapUsed": 94969464, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 362096 + }, + { + "pid": 42793, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 371081216, + "heapTotal": 233537536, + "heapUsed": 94968496, + "external": 6027477, + "arrayBuffers": 2183442, + "maxRssKiB": 362512 + }, + { + "pid": 42794, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 379043840, + "heapTotal": 235634688, + "heapUsed": 97689768, + "external": 6027375, + "arrayBuffers": 2183442, + "maxRssKiB": 370288 + }, + { + "pid": 42792, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 382189568, + "heapTotal": 235372544, + "heapUsed": 97696800, + "external": 6027362, + "arrayBuffers": 2183442, + "maxRssKiB": 373360 + }, + { + "pid": 42793, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 384942080, + "heapTotal": 236158976, + "heapUsed": 97692832, + "external": 6027417, + "arrayBuffers": 2183442, + "maxRssKiB": 376048 + }, + { + "pid": 42792, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 394297344, + "heapTotal": 238256128, + "heapUsed": 100284024, + "external": 6035612, + "arrayBuffers": 2183442, + "maxRssKiB": 385184 + }, + { + "pid": 42794, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 389726208, + "heapTotal": 237993984, + "heapUsed": 100282576, + "external": 6035625, + "arrayBuffers": 2183442, + "maxRssKiB": 380720 + }, + { + "pid": 42793, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 397754368, + "heapTotal": 238518272, + "heapUsed": 100285904, + "external": 6035667, + "arrayBuffers": 2183442, + "maxRssKiB": 388560 + }, + { + "pid": 42794, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 391626752, + "heapTotal": 239828992, + "heapUsed": 101894704, + "external": 6024034, + "arrayBuffers": 2183442, + "maxRssKiB": 382576 + }, + { + "pid": 42793, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 399720448, + "heapTotal": 240353280, + "heapUsed": 101895856, + "external": 6024046, + "arrayBuffers": 2183442, + "maxRssKiB": 390480 + }, + { + "pid": 42792, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 405716992, + "heapTotal": 240877568, + "heapUsed": 102923488, + "external": 6035867, + "arrayBuffers": 2183442, + "maxRssKiB": 396336 + }, + { + "pid": 42794, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 393461760, + "heapTotal": 241664000, + "heapUsed": 103461528, + "external": 6032226, + "arrayBuffers": 2183442, + "maxRssKiB": 384368 + }, + { + "pid": 42793, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 401489920, + "heapTotal": 242188288, + "heapUsed": 103462664, + "external": 6032238, + "arrayBuffers": 2183442, + "maxRssKiB": 392208 + }, + { + "pid": 42792, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 414711808, + "heapTotal": 243236864, + "heapUsed": 105530120, + "external": 6027733, + "arrayBuffers": 2183442, + "maxRssKiB": 405120 + }, + { + "pid": 42794, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 395427840, + "heapTotal": 242712576, + "heapUsed": 104939336, + "external": 6023723, + "arrayBuffers": 2183442, + "maxRssKiB": 386288 + }, + { + "pid": 42793, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 403603456, + "heapTotal": 243499008, + "heapUsed": 104951240, + "external": 6023735, + "arrayBuffers": 2183442, + "maxRssKiB": 394272 + }, + { + "pid": 42793, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 405291008, + "heapTotal": 245071872, + "heapUsed": 106506568, + "external": 6031927, + "arrayBuffers": 2183442, + "maxRssKiB": 395920 + }, + { + "pid": 42794, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 397213696, + "heapTotal": 244285440, + "heapUsed": 106504944, + "external": 6031915, + "arrayBuffers": 2183442, + "maxRssKiB": 388032 + }, + { + "pid": 42792, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 425033728, + "heapTotal": 245858304, + "heapUsed": 108037472, + "external": 6035556, + "arrayBuffers": 2183442, + "maxRssKiB": 415200 + }, + { + "pid": 42794, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 399261696, + "heapTotal": 246120448, + "heapUsed": 108268784, + "external": 6023722, + "arrayBuffers": 2183442, + "maxRssKiB": 390032 + }, + { + "pid": 42793, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 407552000, + "heapTotal": 246906880, + "heapUsed": 108270040, + "external": 6023734, + "arrayBuffers": 2183442, + "maxRssKiB": 398128 + }, + { + "pid": 42792, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 435339264, + "heapTotal": 247693312, + "heapUsed": 110649240, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 425264 + }, + { + "pid": 42794, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 401227776, + "heapTotal": 248217600, + "heapUsed": 109917224, + "external": 6031914, + "arrayBuffers": 2183442, + "maxRssKiB": 391952 + }, + { + "pid": 42793, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 409583616, + "heapTotal": 248741888, + "heapUsed": 109918944, + "external": 6031926, + "arrayBuffers": 2183442, + "maxRssKiB": 400112 + }, + { + "pid": 42792, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 437485568, + "heapTotal": 250052608, + "heapUsed": 112422920, + "external": 6031912, + "arrayBuffers": 2183442, + "maxRssKiB": 427360 + }, + { + "pid": 42792, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 439500800, + "heapTotal": 252149760, + "heapUsed": 114076016, + "external": 6023720, + "arrayBuffers": 2183442, + "maxRssKiB": 429328 + }, + { + "pid": 42791, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 116539392, + "heapTotal": 61227008, + "heapUsed": 25961392, + "external": 5699073, + "arrayBuffers": 2064962, + "maxRssKiB": 113808 + }, + { + "pid": 42791, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 117161984, + "heapTotal": 62799872, + "heapUsed": 27032760, + "external": 5699678, + "arrayBuffers": 2064962, + "maxRssKiB": 114416 + } + ], + "files": [ + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 1.225 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 1.224 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 1.297 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 1.995 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (104 MB heap size)", + "PASS build tests/integration/build/override/history.integration.test.ts (104 MB heap size)", + "PASS build tests/integration/build/override/newtab.integration.test.ts (108 MB heap size)", + "PASS build tests/integration/build/override/competing.integration.test.ts (25 MB heap size)" + ] + }, + { + "id": "25-limited-8-in-process", + "phase": "limited", + "mode": "in-process", + "workers": 8, + "wallSeconds": 2.462256625000009, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 42872, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 115818496, + "heapTotal": 61227008, + "heapUsed": 24951512, + "external": 5717914, + "arrayBuffers": 2056770, + "maxRssKiB": 113232 + }, + { + "pid": 42873, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 358940672, + "heapTotal": 231440384, + "heapUsed": 92237608, + "external": 6002361, + "arrayBuffers": 2175250, + "maxRssKiB": 350656 + }, + { + "pid": 42874, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 357941248, + "heapTotal": 231702528, + "heapUsed": 92239712, + "external": 6002413, + "arrayBuffers": 2175250, + "maxRssKiB": 349680 + }, + { + "pid": 42875, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 357744640, + "heapTotal": 230916096, + "heapUsed": 92224752, + "external": 6002373, + "arrayBuffers": 2175250, + "maxRssKiB": 349488 + }, + { + "pid": 42873, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 371539968, + "heapTotal": 233275392, + "heapUsed": 94969976, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 362960 + }, + { + "pid": 42874, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 369917952, + "heapTotal": 233537536, + "heapUsed": 94971280, + "external": 6027477, + "arrayBuffers": 2183442, + "maxRssKiB": 361376 + }, + { + "pid": 42875, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 371916800, + "heapTotal": 232488960, + "heapUsed": 94951912, + "external": 6027435, + "arrayBuffers": 2183442, + "maxRssKiB": 363328 + }, + { + "pid": 42873, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 381042688, + "heapTotal": 235372544, + "heapUsed": 97692208, + "external": 6027362, + "arrayBuffers": 2183442, + "maxRssKiB": 372240 + }, + { + "pid": 42874, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 381468672, + "heapTotal": 235896832, + "heapUsed": 97693704, + "external": 6027417, + "arrayBuffers": 2183442, + "maxRssKiB": 372656 + }, + { + "pid": 42875, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 383451136, + "heapTotal": 235110400, + "heapUsed": 97678656, + "external": 6027375, + "arrayBuffers": 2183442, + "maxRssKiB": 374592 + }, + { + "pid": 42874, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 393986048, + "heapTotal": 237993984, + "heapUsed": 100284440, + "external": 6035667, + "arrayBuffers": 2183442, + "maxRssKiB": 384880 + }, + { + "pid": 42873, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 393183232, + "heapTotal": 238518272, + "heapUsed": 100284152, + "external": 6035612, + "arrayBuffers": 2183442, + "maxRssKiB": 384096 + }, + { + "pid": 42875, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 395280384, + "heapTotal": 237731840, + "heapUsed": 100283664, + "external": 6035625, + "arrayBuffers": 2183442, + "maxRssKiB": 386144 + }, + { + "pid": 42874, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 395755520, + "heapTotal": 239828992, + "heapUsed": 101897432, + "external": 6024046, + "arrayBuffers": 2183442, + "maxRssKiB": 386608 + }, + { + "pid": 42875, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 397099008, + "heapTotal": 239566848, + "heapUsed": 101896296, + "external": 6024034, + "arrayBuffers": 2183442, + "maxRssKiB": 387920 + }, + { + "pid": 42873, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 403406848, + "heapTotal": 240877568, + "heapUsed": 102921688, + "external": 6035867, + "arrayBuffers": 2183442, + "maxRssKiB": 394080 + }, + { + "pid": 42874, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 397557760, + "heapTotal": 241664000, + "heapUsed": 103463664, + "external": 6032238, + "arrayBuffers": 2183442, + "maxRssKiB": 388368 + }, + { + "pid": 42875, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 398934016, + "heapTotal": 241139712, + "heapUsed": 103463344, + "external": 6032226, + "arrayBuffers": 2183442, + "maxRssKiB": 389712 + }, + { + "pid": 42874, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 399441920, + "heapTotal": 242974720, + "heapUsed": 104952152, + "external": 6023735, + "arrayBuffers": 2183442, + "maxRssKiB": 390208 + }, + { + "pid": 42873, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 412794880, + "heapTotal": 243499008, + "heapUsed": 105528672, + "external": 6027733, + "arrayBuffers": 2183442, + "maxRssKiB": 403248 + }, + { + "pid": 42875, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 400916480, + "heapTotal": 242712576, + "heapUsed": 104940896, + "external": 6023723, + "arrayBuffers": 2183442, + "maxRssKiB": 391648 + }, + { + "pid": 42874, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 401145856, + "heapTotal": 244809728, + "heapUsed": 106502128, + "external": 6031927, + "arrayBuffers": 2183442, + "maxRssKiB": 391872 + }, + { + "pid": 42875, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 402669568, + "heapTotal": 244285440, + "heapUsed": 106506384, + "external": 6031915, + "arrayBuffers": 2183442, + "maxRssKiB": 393360 + }, + { + "pid": 42873, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 423673856, + "heapTotal": 245858304, + "heapUsed": 108035992, + "external": 6035556, + "arrayBuffers": 2183442, + "maxRssKiB": 413872 + }, + { + "pid": 42874, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 403324928, + "heapTotal": 246644736, + "heapUsed": 108270784, + "external": 6023734, + "arrayBuffers": 2183442, + "maxRssKiB": 394000 + }, + { + "pid": 42875, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 404668416, + "heapTotal": 246120448, + "heapUsed": 108269320, + "external": 6023722, + "arrayBuffers": 2183442, + "maxRssKiB": 395312 + }, + { + "pid": 42873, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 432144384, + "heapTotal": 248479744, + "heapUsed": 110642784, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 422144 + }, + { + "pid": 42874, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 405372928, + "heapTotal": 248741888, + "heapUsed": 109920144, + "external": 6031926, + "arrayBuffers": 2183442, + "maxRssKiB": 396000 + }, + { + "pid": 42875, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 406700032, + "heapTotal": 247955456, + "heapUsed": 109918688, + "external": 6031914, + "arrayBuffers": 2183442, + "maxRssKiB": 397296 + }, + { + "pid": 42873, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 434421760, + "heapTotal": 250052608, + "heapUsed": 112418704, + "external": 6031912, + "arrayBuffers": 2183442, + "maxRssKiB": 424368 + }, + { + "pid": 42873, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 436256768, + "heapTotal": 252411904, + "heapUsed": 114070456, + "external": 6023720, + "arrayBuffers": 2183442, + "maxRssKiB": 426160 + }, + { + "pid": 42872, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 116506624, + "heapTotal": 61751296, + "heapUsed": 25982544, + "external": 5699073, + "arrayBuffers": 2064962, + "maxRssKiB": 113776 + }, + { + "pid": 42872, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 117211136, + "heapTotal": 63062016, + "heapUsed": 27052744, + "external": 5699678, + "arrayBuffers": 2064962, + "maxRssKiB": 114464 + } + ], + "files": [ + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 1.204 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 1.214 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 1.285 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 2.001 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (104 MB heap size)", + "PASS build tests/integration/build/override/history.integration.test.ts (104 MB heap size)", + "PASS build tests/integration/build/override/newtab.integration.test.ts (108 MB heap size)", + "PASS build tests/integration/build/override/competing.integration.test.ts (25 MB heap size)" + ] + }, + { + "id": "26-limited-8-in-process", + "phase": "limited", + "mode": "in-process", + "workers": 8, + "wallSeconds": 2.5396672910000198, + "tests": 33, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "leftovers": 0, + "openHandles": 0, + "samples": [ + { + "pid": 42943, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 117522432, + "heapTotal": 60964864, + "heapUsed": 24925896, + "external": 5717914, + "arrayBuffers": 2056770, + "maxRssKiB": 114896 + }, + { + "pid": 42946, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 359006208, + "heapTotal": 230916096, + "heapUsed": 92235656, + "external": 6002413, + "arrayBuffers": 2175250, + "maxRssKiB": 350720 + }, + { + "pid": 42944, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 359907328, + "heapTotal": 231702528, + "heapUsed": 92238832, + "external": 6002361, + "arrayBuffers": 2175250, + "maxRssKiB": 351600 + }, + { + "pid": 42945, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 354861056, + "heapTotal": 231440384, + "heapUsed": 92237304, + "external": 6002373, + "arrayBuffers": 2175250, + "maxRssKiB": 346672 + }, + { + "pid": 42945, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 367411200, + "heapTotal": 233799680, + "heapUsed": 94969976, + "external": 6027435, + "arrayBuffers": 2183442, + "maxRssKiB": 358928 + }, + { + "pid": 42944, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 372981760, + "heapTotal": 234061824, + "heapUsed": 94969512, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 364368 + }, + { + "pid": 42946, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 370966528, + "heapTotal": 233013248, + "heapUsed": 94965192, + "external": 6027477, + "arrayBuffers": 2183442, + "maxRssKiB": 362400 + }, + { + "pid": 42945, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 379551744, + "heapTotal": 235896832, + "heapUsed": 97690720, + "external": 6027375, + "arrayBuffers": 2183442, + "maxRssKiB": 370784 + }, + { + "pid": 42944, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 383762432, + "heapTotal": 236158976, + "heapUsed": 97691960, + "external": 6027362, + "arrayBuffers": 2183442, + "maxRssKiB": 374896 + }, + { + "pid": 42946, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 383352832, + "heapTotal": 235634688, + "heapUsed": 97692136, + "external": 6027417, + "arrayBuffers": 2183442, + "maxRssKiB": 374496 + }, + { + "pid": 42945, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 389545984, + "heapTotal": 238256128, + "heapUsed": 100284152, + "external": 6035625, + "arrayBuffers": 2183442, + "maxRssKiB": 380544 + }, + { + "pid": 42944, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 392822784, + "heapTotal": 237993984, + "heapUsed": 100283512, + "external": 6035612, + "arrayBuffers": 2183442, + "maxRssKiB": 383744 + }, + { + "pid": 42946, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 395542528, + "heapTotal": 237731840, + "heapUsed": 100282488, + "external": 6035667, + "arrayBuffers": 2183442, + "maxRssKiB": 386400 + }, + { + "pid": 42946, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 397312000, + "heapTotal": 239566848, + "heapUsed": 101895288, + "external": 6024046, + "arrayBuffers": 2183442, + "maxRssKiB": 388128 + }, + { + "pid": 42945, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 391249920, + "heapTotal": 239828992, + "heapUsed": 101895736, + "external": 6024034, + "arrayBuffers": 2183442, + "maxRssKiB": 382208 + }, + { + "pid": 42944, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 404504576, + "heapTotal": 240615424, + "heapUsed": 102922768, + "external": 6035867, + "arrayBuffers": 2183442, + "maxRssKiB": 395152 + }, + { + "pid": 42946, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 399147008, + "heapTotal": 241401856, + "heapUsed": 103461648, + "external": 6032238, + "arrayBuffers": 2183442, + "maxRssKiB": 389920 + }, + { + "pid": 42945, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 392888320, + "heapTotal": 241664000, + "heapUsed": 103462464, + "external": 6032226, + "arrayBuffers": 2183442, + "maxRssKiB": 383808 + }, + { + "pid": 42946, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 401145856, + "heapTotal": 242974720, + "heapUsed": 104951216, + "external": 6023735, + "arrayBuffers": 2183442, + "maxRssKiB": 391872 + }, + { + "pid": 42945, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 394887168, + "heapTotal": 243236864, + "heapUsed": 104945576, + "external": 6023723, + "arrayBuffers": 2201407, + "maxRssKiB": 385760 + }, + { + "pid": 42944, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 415088640, + "heapTotal": 243236864, + "heapUsed": 105529464, + "external": 6027733, + "arrayBuffers": 2183442, + "maxRssKiB": 405488 + }, + { + "pid": 42945, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 396738560, + "heapTotal": 245071872, + "heapUsed": 106505912, + "external": 6031915, + "arrayBuffers": 2183442, + "maxRssKiB": 387568 + }, + { + "pid": 42946, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 402964480, + "heapTotal": 244285440, + "heapUsed": 106505536, + "external": 6031927, + "arrayBuffers": 2183442, + "maxRssKiB": 393648 + }, + { + "pid": 42944, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 426049536, + "heapTotal": 245858304, + "heapUsed": 108042264, + "external": 6035556, + "arrayBuffers": 2183442, + "maxRssKiB": 416192 + }, + { + "pid": 42945, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 398770176, + "heapTotal": 246382592, + "heapUsed": 108269232, + "external": 6023722, + "arrayBuffers": 2183442, + "maxRssKiB": 389552 + }, + { + "pid": 42946, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 404996096, + "heapTotal": 246120448, + "heapUsed": 108268264, + "external": 6023734, + "arrayBuffers": 2183442, + "maxRssKiB": 395632 + }, + { + "pid": 42946, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 406765568, + "heapTotal": 248479744, + "heapUsed": 109917336, + "external": 6031926, + "arrayBuffers": 2183442, + "maxRssKiB": 397360 + }, + { + "pid": 42945, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 400523264, + "heapTotal": 248479744, + "heapUsed": 109919280, + "external": 6031914, + "arrayBuffers": 2183442, + "maxRssKiB": 391264 + }, + { + "pid": 42944, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 434831360, + "heapTotal": 247955456, + "heapUsed": 110649152, + "external": 6027422, + "arrayBuffers": 2183442, + "maxRssKiB": 424768 + }, + { + "pid": 42944, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 437010432, + "heapTotal": 250314752, + "heapUsed": 112421080, + "external": 6031912, + "arrayBuffers": 2183442, + "maxRssKiB": 426896 + }, + { + "pid": 42944, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 438861824, + "heapTotal": 251887616, + "heapUsed": 114073944, + "external": 6023720, + "arrayBuffers": 2183442, + "maxRssKiB": 428704 + }, + { + "pid": 42943, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 118259712, + "heapTotal": 61751296, + "heapUsed": 25956520, + "external": 5699073, + "arrayBuffers": 2064962, + "maxRssKiB": 115648 + }, + { + "pid": 42943, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 118833152, + "heapTotal": 62799872, + "heapUsed": 27026792, + "external": 5699678, + "arrayBuffers": 2064962, + "maxRssKiB": 116048 + } + ], + "files": [ + { + "path": "tests/integration/build/override/bookmarks.integration.test.ts", + "seconds": 1.261 + }, + { + "path": "tests/integration/build/override/history.integration.test.ts", + "seconds": 1.263 + }, + { + "path": "tests/integration/build/override/newtab.integration.test.ts", + "seconds": 1.363 + }, + { + "path": "tests/integration/build/override/competing.integration.test.ts", + "seconds": 2.079 + } + ], + "heapOutput": [ + "PASS build tests/integration/build/override/bookmarks.integration.test.ts (104 MB heap size)", + "PASS build tests/integration/build/override/history.integration.test.ts (104 MB heap size)", + "PASS build tests/integration/build/override/newtab.integration.test.ts (108 MB heap size)", + "PASS build tests/integration/build/override/competing.integration.test.ts (25 MB heap size)" + ] + } + ], + "stressFixtureCopiesRemaining": 0, + "stress": [ + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 389103616, + "heapTotal": 242778112, + "heapUsed": 102882896, + "external": 21570785, + "arrayBuffers": 2175247, + "maxRssKiB": 380112 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 401883136, + "heapTotal": 244613120, + "heapUsed": 105476648, + "external": 12168661, + "arrayBuffers": 2183439, + "maxRssKiB": 392592 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 413384704, + "heapTotal": 246710272, + "heapUsed": 108164104, + "external": 12168601, + "arrayBuffers": 2183439, + "maxRssKiB": 403824 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 423149568, + "heapTotal": 249069568, + "heapUsed": 110779360, + "external": 12176851, + "arrayBuffers": 2183439, + "maxRssKiB": 413360 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 432275456, + "heapTotal": 251691008, + "heapUsed": 113414456, + "external": 12177106, + "arrayBuffers": 2183439, + "maxRssKiB": 422272 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 441352192, + "heapTotal": 254574592, + "heapUsed": 116031032, + "external": 12168972, + "arrayBuffers": 2183439, + "maxRssKiB": 431136 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 450412544, + "heapTotal": 256933888, + "heapUsed": 118536448, + "external": 12176795, + "arrayBuffers": 2183439, + "maxRssKiB": 439984 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 459718656, + "heapTotal": 259293184, + "heapUsed": 121140704, + "external": 12168661, + "arrayBuffers": 2183439, + "maxRssKiB": 449072 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 461799424, + "heapTotal": 261652480, + "heapUsed": 122913024, + "external": 12173151, + "arrayBuffers": 2183439, + "maxRssKiB": 451104 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 463798272, + "heapTotal": 262438912, + "heapUsed": 124554944, + "external": 12164959, + "arrayBuffers": 2183439, + "maxRssKiB": 453056 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 473972736, + "heapTotal": 265584640, + "heapUsed": 127228776, + "external": 12176808, + "arrayBuffers": 2183439, + "maxRssKiB": 462992 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 483000320, + "heapTotal": 267943936, + "heapUsed": 129772584, + "external": 12168674, + "arrayBuffers": 2183439, + "maxRssKiB": 471808 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 491356160, + "heapTotal": 270565376, + "heapUsed": 132298880, + "external": 12176806, + "arrayBuffers": 2183439, + "maxRssKiB": 479968 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 500547584, + "heapTotal": 272924672, + "heapUsed": 134813520, + "external": 12168672, + "arrayBuffers": 2183439, + "maxRssKiB": 488944 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 502267904, + "heapTotal": 274759680, + "heapUsed": 136310328, + "external": 12173465, + "arrayBuffers": 2183439, + "maxRssKiB": 490624 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 504004608, + "heapTotal": 276070400, + "heapUsed": 137829288, + "external": 12165273, + "arrayBuffers": 2183439, + "maxRssKiB": 492320 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 505774080, + "heapTotal": 277905408, + "heapUsed": 139344872, + "external": 12173154, + "arrayBuffers": 2183439, + "maxRssKiB": 494048 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 507641856, + "heapTotal": 279216128, + "heapUsed": 140834440, + "external": 12164962, + "arrayBuffers": 2183439, + "maxRssKiB": 495872 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 509345792, + "heapTotal": 281051136, + "heapUsed": 142353624, + "external": 12173153, + "arrayBuffers": 2183439, + "maxRssKiB": 497536 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 511246336, + "heapTotal": 281837568, + "heapUsed": 143854368, + "external": 12164961, + "arrayBuffers": 2183439, + "maxRssKiB": 499392 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 519847936, + "heapTotal": 284983296, + "heapUsed": 146347232, + "external": 12176850, + "arrayBuffers": 2183439, + "maxRssKiB": 507792 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 528105472, + "heapTotal": 287342592, + "heapUsed": 148863704, + "external": 12168716, + "arrayBuffers": 2183439, + "maxRssKiB": 515856 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 535838720, + "heapTotal": 289701888, + "heapUsed": 151417192, + "external": 12176848, + "arrayBuffers": 2183439, + "maxRssKiB": 523408 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 544751616, + "heapTotal": 292323328, + "heapUsed": 153916288, + "external": 12168714, + "arrayBuffers": 2183439, + "maxRssKiB": 532112 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 546537472, + "heapTotal": 293634048, + "heapUsed": 155402616, + "external": 12173477, + "arrayBuffers": 2183439, + "maxRssKiB": 533856 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 548192256, + "heapTotal": 295469056, + "heapUsed": 156894944, + "external": 12165285, + "arrayBuffers": 2183439, + "maxRssKiB": 535472 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 550027264, + "heapTotal": 297304064, + "heapUsed": 158372272, + "external": 12173166, + "arrayBuffers": 2183439, + "maxRssKiB": 537264 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 551649280, + "heapTotal": 298614784, + "heapUsed": 159856832, + "external": 12164974, + "arrayBuffers": 2183439, + "maxRssKiB": 538848 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 553385984, + "heapTotal": 300449792, + "heapUsed": 161337992, + "external": 12164973, + "arrayBuffers": 2183439, + "maxRssKiB": 540544 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 1 bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 554926080, + "heapTotal": 301760512, + "heapUsed": 162817024, + "external": 12173165, + "arrayBuffers": 2183439, + "maxRssKiB": 542048 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 562724864, + "heapTotal": 304381952, + "heapUsed": 165321168, + "external": 12168603, + "arrayBuffers": 2183439, + "maxRssKiB": 549664 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 570523648, + "heapTotal": 307003392, + "heapUsed": 167821104, + "external": 12176853, + "arrayBuffers": 2183439, + "maxRssKiB": 557280 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 578600960, + "heapTotal": 309624832, + "heapUsed": 170324280, + "external": 12176793, + "arrayBuffers": 2183439, + "maxRssKiB": 565168 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 586465280, + "heapTotal": 311984128, + "heapUsed": 172830000, + "external": 12168659, + "arrayBuffers": 2183439, + "maxRssKiB": 572848 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 594690048, + "heapTotal": 314605568, + "heapUsed": 175340544, + "external": 12177106, + "arrayBuffers": 2183439, + "maxRssKiB": 580880 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 601669632, + "heapTotal": 316964864, + "heapUsed": 177862992, + "external": 12168972, + "arrayBuffers": 2183439, + "maxRssKiB": 587696 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 610320384, + "heapTotal": 319586304, + "heapUsed": 180373680, + "external": 12176795, + "arrayBuffers": 2183439, + "maxRssKiB": 596144 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 618905600, + "heapTotal": 321945600, + "heapUsed": 182875864, + "external": 12168661, + "arrayBuffers": 2183439, + "maxRssKiB": 604528 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 620412928, + "heapTotal": 324042752, + "heapUsed": 184367912, + "external": 12173151, + "arrayBuffers": 2183439, + "maxRssKiB": 606000 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 622067712, + "heapTotal": 325353472, + "heapUsed": 185844424, + "external": 12164959, + "arrayBuffers": 2183439, + "maxRssKiB": 607616 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 629768192, + "heapTotal": 327712768, + "heapUsed": 188353320, + "external": 12176808, + "arrayBuffers": 2183439, + "maxRssKiB": 615136 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 637976576, + "heapTotal": 330072064, + "heapUsed": 190891360, + "external": 12168674, + "arrayBuffers": 2183439, + "maxRssKiB": 623152 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 645283840, + "heapTotal": 332955648, + "heapUsed": 193437376, + "external": 12176806, + "arrayBuffers": 2183439, + "maxRssKiB": 630288 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 653590528, + "heapTotal": 335577088, + "heapUsed": 195965664, + "external": 12168672, + "arrayBuffers": 2183439, + "maxRssKiB": 638400 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 655327232, + "heapTotal": 337412096, + "heapUsed": 197473672, + "external": 12173465, + "arrayBuffers": 2183439, + "maxRssKiB": 640096 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 657129472, + "heapTotal": 338198528, + "heapUsed": 198992520, + "external": 12165273, + "arrayBuffers": 2183439, + "maxRssKiB": 641856 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 658849792, + "heapTotal": 340295680, + "heapUsed": 200463856, + "external": 12173154, + "arrayBuffers": 2183439, + "maxRssKiB": 643536 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 660537344, + "heapTotal": 341606400, + "heapUsed": 201971456, + "external": 12164962, + "arrayBuffers": 2183439, + "maxRssKiB": 645184 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 662306816, + "heapTotal": 343179264, + "heapUsed": 203445704, + "external": 12173153, + "arrayBuffers": 2183439, + "maxRssKiB": 646912 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 664092672, + "heapTotal": 344752128, + "heapUsed": 204929600, + "external": 12164961, + "arrayBuffers": 2183439, + "maxRssKiB": 648656 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 672776192, + "heapTotal": 347111424, + "heapUsed": 207430448, + "external": 12176850, + "arrayBuffers": 2183439, + "maxRssKiB": 657136 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 681771008, + "heapTotal": 349732864, + "heapUsed": 209950712, + "external": 12168716, + "arrayBuffers": 2183439, + "maxRssKiB": 665920 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 690143232, + "heapTotal": 352354304, + "heapUsed": 212439960, + "external": 12176848, + "arrayBuffers": 2183439, + "maxRssKiB": 674096 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 698220544, + "heapTotal": 354713600, + "heapUsed": 214950832, + "external": 12168714, + "arrayBuffers": 2183439, + "maxRssKiB": 681984 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 700071936, + "heapTotal": 356548608, + "heapUsed": 216432456, + "external": 12173477, + "arrayBuffers": 2183439, + "maxRssKiB": 683792 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 701808640, + "heapTotal": 358383616, + "heapUsed": 217903960, + "external": 12165285, + "arrayBuffers": 2183439, + "maxRssKiB": 685488 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 703479808, + "heapTotal": 360218624, + "heapUsed": 219410232, + "external": 12173166, + "arrayBuffers": 2183439, + "maxRssKiB": 687120 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 705200128, + "heapTotal": 361529344, + "heapUsed": 220890328, + "external": 12164974, + "arrayBuffers": 2183439, + "maxRssKiB": 688800 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 706838528, + "heapTotal": 363364352, + "heapUsed": 222366392, + "external": 12173165, + "arrayBuffers": 2183439, + "maxRssKiB": 690400 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 2 bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 708673536, + "heapTotal": 364675072, + "heapUsed": 223834400, + "external": 12164973, + "arrayBuffers": 2183439, + "maxRssKiB": 692192 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 716750848, + "heapTotal": 367034368, + "heapUsed": 226329752, + "external": 12176795, + "arrayBuffers": 2183439, + "maxRssKiB": 700080 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 725630976, + "heapTotal": 369393664, + "heapUsed": 228823736, + "external": 12168661, + "arrayBuffers": 2183439, + "maxRssKiB": 708752 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 732626944, + "heapTotal": 372015104, + "heapUsed": 231319728, + "external": 12176793, + "arrayBuffers": 2183439, + "maxRssKiB": 715584 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 741801984, + "heapTotal": 374374400, + "heapUsed": 233852136, + "external": 12168659, + "arrayBuffers": 2183439, + "maxRssKiB": 724544 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 749780992, + "heapTotal": 376995840, + "heapUsed": 236343968, + "external": 12177106, + "arrayBuffers": 2183439, + "maxRssKiB": 732336 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 758398976, + "heapTotal": 379617280, + "heapUsed": 238833648, + "external": 12168972, + "arrayBuffers": 2183439, + "maxRssKiB": 740752 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 765902848, + "heapTotal": 382238720, + "heapUsed": 241336344, + "external": 12176795, + "arrayBuffers": 2183439, + "maxRssKiB": 748080 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 775553024, + "heapTotal": 384598016, + "heapUsed": 243828536, + "external": 12168661, + "arrayBuffers": 2183439, + "maxRssKiB": 757504 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 777125888, + "heapTotal": 386170880, + "heapUsed": 245310808, + "external": 12173151, + "arrayBuffers": 2183439, + "maxRssKiB": 759040 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 778797056, + "heapTotal": 387481600, + "heapUsed": 246808864, + "external": 12164959, + "arrayBuffers": 2183439, + "maxRssKiB": 760672 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 786661376, + "heapTotal": 390103040, + "heapUsed": 249303312, + "external": 12176808, + "arrayBuffers": 2183439, + "maxRssKiB": 768352 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 796016640, + "heapTotal": 392724480, + "heapUsed": 251801520, + "external": 12168674, + "arrayBuffers": 2183439, + "maxRssKiB": 777488 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 803356672, + "heapTotal": 395608064, + "heapUsed": 254310528, + "external": 12176806, + "arrayBuffers": 2183439, + "maxRssKiB": 784656 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 812580864, + "heapTotal": 397967360, + "heapUsed": 256821304, + "external": 12168672, + "arrayBuffers": 2183439, + "maxRssKiB": 793664 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 814333952, + "heapTotal": 399802368, + "heapUsed": 258285600, + "external": 12173465, + "arrayBuffers": 2183439, + "maxRssKiB": 795376 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 815939584, + "heapTotal": 400588800, + "heapUsed": 259754456, + "external": 12165273, + "arrayBuffers": 2183439, + "maxRssKiB": 796944 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 817790976, + "heapTotal": 402685952, + "heapUsed": 261214592, + "external": 12173154, + "arrayBuffers": 2183439, + "maxRssKiB": 798752 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 819675136, + "heapTotal": 403996672, + "heapUsed": 262692392, + "external": 12164962, + "arrayBuffers": 2183439, + "maxRssKiB": 800736 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 821608448, + "heapTotal": 405569536, + "heapUsed": 264161568, + "external": 12173153, + "arrayBuffers": 2183439, + "maxRssKiB": 802480 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 823885824, + "heapTotal": 407404544, + "heapUsed": 265642544, + "external": 12164961, + "arrayBuffers": 2183439, + "maxRssKiB": 804704 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 833257472, + "heapTotal": 409763840, + "heapUsed": 268132128, + "external": 12176850, + "arrayBuffers": 2183439, + "maxRssKiB": 813856 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 842727424, + "heapTotal": 412385280, + "heapUsed": 270641808, + "external": 12168716, + "arrayBuffers": 2183439, + "maxRssKiB": 823104 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 851034112, + "heapTotal": 414744576, + "heapUsed": 273133352, + "external": 12176848, + "arrayBuffers": 2183439, + "maxRssKiB": 831216 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 859619328, + "heapTotal": 417103872, + "heapUsed": 275675832, + "external": 12168714, + "arrayBuffers": 2183439, + "maxRssKiB": 839600 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 861241344, + "heapTotal": 418938880, + "heapUsed": 277158176, + "external": 12173477, + "arrayBuffers": 2183439, + "maxRssKiB": 841184 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 863141888, + "heapTotal": 420511744, + "heapUsed": 278681968, + "external": 12165285, + "arrayBuffers": 2183439, + "maxRssKiB": 843040 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 865189888, + "heapTotal": 422084608, + "heapUsed": 280144560, + "external": 12173166, + "arrayBuffers": 2183439, + "maxRssKiB": 845040 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 867221504, + "heapTotal": 423657472, + "heapUsed": 281616064, + "external": 12164974, + "arrayBuffers": 2183439, + "maxRssKiB": 847024 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 869564416, + "heapTotal": 425230336, + "heapUsed": 283098720, + "external": 12173165, + "arrayBuffers": 2183439, + "maxRssKiB": 849312 + }, + { + "pid": 42961, + "worker": "1", + "file": "tests/integration/build/override/memory-pilot.integration.test.ts", + "test": "memory round 3 bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 871759872, + "heapTotal": 426803200, + "heapUsed": 284561984, + "external": 12164973, + "arrayBuffers": 2183439, + "maxRssKiB": 851456 + } + ] +} diff --git a/tests/benchmarks/2026-09-28-override-pilot.md b/tests/benchmarks/2026-09-28-override-pilot.md new file mode 100644 index 00000000..18084405 --- /dev/null +++ b/tests/benchmarks/2026-09-28-override-pilot.md @@ -0,0 +1,159 @@ +# Override in-process pilot — 2026-09-28 + +This is a historical report for the superseded in-process backend. The current helper and benchmark runner +use [file-owned child sessions](2026-09-28-override-session.md). The project-hashed virtual-directory workaround +described here has been removed; the independent absolute-root fixes remain. + +## Decision + +Keep CLI builds as the default. The opt-in in-process path is faster at both worker limits, but repeated +builds retain memory after forced GC. Do not extend it to other integration groups yet. No global +`workerIdleMemoryLimit` or worker-count changes are part of this pilot. The separate absolute-root fix is +required by the helper (`67fd974f`) and is included in the final measurement series below. + +The prerequisite is `005e8457`: awaited, environment-restoring `buildApp`, with package tests under +`tests/integration/build/cli`. The experimental helper is selected by `ADNBN_OVERRIDE_BUILD_MODE=in-process`. + +## Identical work + +All 33 unique full scenario names match in every comparison run. The same temporary application copies and +assertions are used. Thirty ordinary page builds switch backend; all three competing-entrypoint cases remain +on CLI. Both backends use production mode and the same browsers/manifest versions. CLI runs from the fixture; +in-process builds receive its absolute root while keeping the worker's working directory unchanged. +There is no shared compilation cache or concurrent test within a file. + +Jest substitutes `createRequire` and `process.env`. The adapter uses Node's public `getBuiltinModule` API to +load the built package in the same worker, temporarily sharing the harness environment and restoring it in +`finally`. The diagnostics assert environment values, object identity and unchanged cwd after every test. +Production `buildApp` is unchanged by the pilot. + +## Time + +Apple M3 Max, 16 logical CPUs, macOS arm64, Node 24.5.0. One package build before the series; no production +changes during measurements. Each worker limit has a warm-up for each backend followed by three alternating +measured pairs. Time includes the complete Jest command and reporting, excludes the package build. Every +run uses `--expose-gc`, the existing macOS `--no-sparkplug`, `--logHeapUsage`, JSON reporting and the same +per-test diagnostic hook (including GC). These are instrumented comparisons, not uninstrumented CI timings. +With four files, the eight-worker setting can use at most four workers. + +| Worker limit | CLI median (range) | In-process median (range) | Ratio | +| ------------ | --------------------- | ------------------------- | ----- | +| 2 | 13.36 s (13.30–13.37) | 3.63 s (2.66–3.68) | 3.68× | +| 8 | 7.27 s (7.12–7.27) | 2.81 s (2.56–3.62) | 2.59× | + +The ranges are reported rather than promising the best run; three measured pairs per worker limit are a small sample. +These are local measurements, not evidence of Linux/Windows CI performance. + +## Memory and handles + +The sequential four-file run reports CLI worker heap of 35/46/47/47 MiB, versus approximately +118/131/139/133 MiB with the pilot. Worker RSS reaches about 180 MiB for CLI and 517 MiB for in-process. +CLI child-process memory is **not** included in those worker numbers; they are not total machine-memory comparisons. + +A separate diagnostic repeats the existing thirty page cases three times in one file and one worker. All +90 builds pass, but live heap and RSS keep growing despite an explicit GC after each test: + +| Builds completed | Heap after GC | Worker RSS | +| ---------------- | ------------- | ---------- | +| 1 | 98.1 MiB | 371.1 MiB | +| 30 | 155.3 MiB | 529.2 MiB | +| 60 | 213.5 MiB | 675.8 MiB | +| 90 | 271.4 MiB | 831.4 MiB | + +The earlier series also retained memory after GC (271.3 MiB heap / 830.7 MiB RSS at build 90). A standalone +Rspack reproduction now isolates compiler retention without the framework, config loader or Jest; see below. +This does not exclude additional retention in the complete framework build. + +Three additional runs per worker limit with `--workerIdleMemoryLimit=128MB` passed. However, this four-file +workload did not demonstrate mid-run worker replacement: recorded PID counts remain two/four. At two workers +one worker can finish all three in-process files before crossing the threshold at the end. The setting therefore +does not establish that accumulation is controlled, and it cannot recycle a worker midway through the 90-build +file. No memory limit was added to the regular Jest configuration. + +Separate `--detectOpenHandles` runs pass all 33 cases for both backends and report zero open handles. +Successful compiler shutdown and a clean handle report do not prove absence of retained JavaScript/native memory. + +Every series run leaves zero `fixture-*` copies in `.cache/integration`. Before measurement, 57 pre-existing +copies dated September 14–24 were preserved in `.cache/integration-before-pilot-2026-09-28`, not deleted. + +## Other findings and scope + +- The separate root-directory regression covers real entrypoints through CLI and `buildApp`, with two + projects built A → B → A from an unrelated cwd. The fix preserves absolute import roots, resolves source + aliases and compiler context from `rootDir`, and provides project package lookup for virtual modules. + Absolute filenames use portable virtual names without embedded Windows drive roots. Parser-relative + imports and its existing project `tsconfig` injection remain unchanged. The helper no longer changes cwd. +- The first series without cwd switching exposed shared temporary-module directories: closing one worker's + compiler deleted another project's entry modules. That series failed and is excluded from the final timings. + Entrypoint and offscreen temporary directories are now keyed by project root. An IPC-controlled regression + reproduced deletion before the fix and verifies that a pending compiler can run after the other closes. + Node 22 also exposed the external plugin's check-then-create race on `cwd/node_modules`; the adapter now + prepares directories recursively. The root regression covers Chrome and Firefox, including Offscreen's + generated background module resolved through its direct virtual alias. +- Moving internal CLI config fixtures under integrations exposed overly broad application discovery. + Preparation/typecheck now requires both `adnbn.config.ts` and `package.json`; two tests cover exclusion of + internal configs and inclusion of the three real override applications. +- All 33 pilot cases and three root-directory regressions also passed with local Node 22.18.0. + This does not replace the Linux/Windows CI matrix. + +## Standalone Rspack retention + +`rspack-retention.mjs` uses only Node and the installed `@rspack/core`. Each variant runs in a fresh process, +creates twelve compilers, awaits `run` and `close`, and counts `WeakRef` survivors after GC across event-loop +turns. Temporary sources are generated under the OS temporary directory and removed in `finally`. +It does not retain `Stats`, inspect private bindings or load framework code. + +On Rspack 1.7.11, Node 24.5.0, macOS arm64: + +| Configuration | Compilers remaining / 12 | +| ---------------------------------------------- | ------------------------ | +| Baseline, RegExp rule test, function include | 0 | +| Function rule test, use, resourceQuery | 12 | +| RegExp cache-group test | 0 | +| Function cache-group test | 12 | +| HTML file template or string templateContent | 0 | +| Function templateContent or templateParameters | 12 | + +The framework uses a functional asset rule and graph-dependent cache-group tests. Its `View.html()` passes +`template`, not `templateContent`. Replacing the asset rule alone would not remove the other triggers. +The installed Rspack adapter wraps functional resource conditions and passes them to the native binding; +the exact native ownership/release defect still needs upstream confirmation. Stable `external` and +`arrayBuffers` do not by themselves attribute all RSS growth to a native leak. + +```bash +node tests/benchmarks/rspack-retention.mjs +node --expose-gc tests/benchmarks/rspack-retention.mjs rule-test-function +``` + +[Raw retention results](2026-09-28-rspack-retention.json) record versions, counts and memory measurements. +The script is a diagnostic, not a GC-sensitive test in the regular suite; rerun it when evaluating a Rspack update. + +## Reproduction + +Run exclusively, without another integration run or a concurrent package build: + +```bash +npm run build +node tests/benchmarks/override-pilot.mjs +``` + +The runner writes logs, per-test worker metrics and Jest JSON under `.cache/benchmarks/override-pilot`. +It checks scenario identity and fixture cleanup. The 90-build diagnostic temporarily creates a test beside +existing override tests, reusing their matrices, and removes it in `finally`. `--stress-only` repeats that probe; +`--limits-only` appends memory-limit comparisons to an existing results file. Existing unrelated fixture copies +must be accounted for separately before a run: the runner never deletes them to satisfy its cleanup assertion. + +[Raw results](2026-09-28-override-pilot.json) include all timings, full scenario names, worker PID/heap/RSS samples, +Jest heap output, handle counts and the 90-build diagnostic. + +## Validation + +- Final `npm run check` with the default CLI backend: 183 suites / 2002 tests, package build, framework typecheck, + and fixture builds/typechecks passed. +- Chrome + Firefox on eight workers: 30 suites / 41 tests passed; one Chrome startup timed out at 30 seconds + with ECONNREFUSED before the scenario ran. The timeout was not increased. The isolated retry passed + in 4.3 s; all 31 suites / 42 tests passed on two workers; the final repeat took 63.2 s. This does not prove absence of rare + startup failures under heavier concurrency. +- Inventory: 214 files, each in exactly one project; zero local module mocks. +- Unit coverage: 139 suites / 1685 tests passed with localhost access. +- CI for these unpushed changes remains unverified; no push or workflow dispatch was performed. diff --git a/tests/benchmarks/2026-09-28-override-session.json b/tests/benchmarks/2026-09-28-override-session.json new file mode 100644 index 00000000..18fba4a3 --- /dev/null +++ b/tests/benchmarks/2026-09-28-override-session.json @@ -0,0 +1,11037 @@ +{ + "node": "v24.5.0", + "platform": "darwin", + "arch": "arm64", + "cpus": 16, + "flags": [ + "--expose-gc", + "--no-sparkplug" + ], + "runs": [ + { + "id": "1-warmup-2-cli", + "phase": "warmup", + "mode": "cli", + "workers": 2, + "wallSeconds": 13.201181042, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "tests": 33, + "leftovers": 0, + "openHandles": 0, + "children": [], + "parentSamples": [ + { + "pid": 96504, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 123076608, + "heapTotal": 60702720, + "heapUsed": 24951432, + "external": 5722819, + "arrayBuffers": 2062059, + "maxRssKiB": 120448 + }, + { + "pid": 96503, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 122798080, + "heapTotal": 60964864, + "heapUsed": 24948512, + "external": 5722808, + "arrayBuffers": 2062059, + "maxRssKiB": 120192 + }, + { + "pid": 96504, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 123994112, + "heapTotal": 61489152, + "heapUsed": 25995120, + "external": 5704053, + "arrayBuffers": 2070251, + "maxRssKiB": 121088 + }, + { + "pid": 96503, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 123387904, + "heapTotal": 61489152, + "heapUsed": 25994256, + "external": 5704043, + "arrayBuffers": 2070251, + "maxRssKiB": 120496 + }, + { + "pid": 96503, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 123928576, + "heapTotal": 62537728, + "heapUsed": 27056000, + "external": 5712204, + "arrayBuffers": 2070251, + "maxRssKiB": 121024 + }, + { + "pid": 96504, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 124534784, + "heapTotal": 62537728, + "heapUsed": 27058920, + "external": 5712214, + "arrayBuffers": 2070251, + "maxRssKiB": 121616 + }, + { + "pid": 96503, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 124665856, + "heapTotal": 63586304, + "heapUsed": 28133320, + "external": 5704041, + "arrayBuffers": 2070251, + "maxRssKiB": 121744 + }, + { + "pid": 96504, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 125108224, + "heapTotal": 63848448, + "heapUsed": 28134208, + "external": 5704051, + "arrayBuffers": 2070251, + "maxRssKiB": 122192 + }, + { + "pid": 96504, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 125829120, + "heapTotal": 64897024, + "heapUsed": 29201728, + "external": 5711173, + "arrayBuffers": 2070251, + "maxRssKiB": 122880 + }, + { + "pid": 96503, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 125419520, + "heapTotal": 65159168, + "heapUsed": 29194040, + "external": 5712362, + "arrayBuffers": 2070251, + "maxRssKiB": 122480 + }, + { + "pid": 96504, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 126517248, + "heapTotal": 65945600, + "heapUsed": 30231008, + "external": 5702981, + "arrayBuffers": 2080957, + "maxRssKiB": 123568 + }, + { + "pid": 96503, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 126140416, + "heapTotal": 66207744, + "heapUsed": 30222736, + "external": 5712391, + "arrayBuffers": 2070251, + "maxRssKiB": 123184 + }, + { + "pid": 96504, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 127221760, + "heapTotal": 66994176, + "heapUsed": 31255184, + "external": 5711017, + "arrayBuffers": 2070251, + "maxRssKiB": 124240 + }, + { + "pid": 96503, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 126910464, + "heapTotal": 67518464, + "heapUsed": 31246912, + "external": 5704014, + "arrayBuffers": 2070251, + "maxRssKiB": 123936 + }, + { + "pid": 96504, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 127926272, + "heapTotal": 68042752, + "heapUsed": 32277200, + "external": 5702825, + "arrayBuffers": 2070251, + "maxRssKiB": 124928 + }, + { + "pid": 96503, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 127680512, + "heapTotal": 68567040, + "heapUsed": 32269640, + "external": 5712235, + "arrayBuffers": 2070251, + "maxRssKiB": 124688 + }, + { + "pid": 96504, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 128614400, + "heapTotal": 68829184, + "heapUsed": 33344368, + "external": 5711016, + "arrayBuffers": 2070251, + "maxRssKiB": 125600 + }, + { + "pid": 96503, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 128417792, + "heapTotal": 69877760, + "heapUsed": 33348888, + "external": 5702822, + "arrayBuffers": 2070251, + "maxRssKiB": 125408 + }, + { + "pid": 96504, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 129286144, + "heapTotal": 70139904, + "heapUsed": 34379760, + "external": 5711016, + "arrayBuffers": 2070251, + "maxRssKiB": 126256 + }, + { + "pid": 96503, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 129105920, + "heapTotal": 70926336, + "heapUsed": 34379040, + "external": 5711014, + "arrayBuffers": 2070251, + "maxRssKiB": 126080 + }, + { + "pid": 96504, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 146604032, + "heapTotal": 105054208, + "heapUsed": 29614360, + "external": 7765668, + "arrayBuffers": 2078443, + "maxRssKiB": 143296 + }, + { + "pid": 96503, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 146423808, + "heapTotal": 105840640, + "heapUsed": 29750544, + "external": 7765982, + "arrayBuffers": 2078443, + "maxRssKiB": 143120 + }, + { + "pid": 96503, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 147275776, + "heapTotal": 101122048, + "heapUsed": 30732960, + "external": 5720746, + "arrayBuffers": 2086635, + "maxRssKiB": 143824 + }, + { + "pid": 96504, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 148029440, + "heapTotal": 101384192, + "heapUsed": 30602696, + "external": 5720463, + "arrayBuffers": 2086635, + "maxRssKiB": 144560 + }, + { + "pid": 96503, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 147570688, + "heapTotal": 102170624, + "heapUsed": 31766656, + "external": 5721286, + "arrayBuffers": 2086635, + "maxRssKiB": 144112 + }, + { + "pid": 96504, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 148242432, + "heapTotal": 102170624, + "heapUsed": 31640904, + "external": 5728624, + "arrayBuffers": 2086635, + "maxRssKiB": 144768 + }, + { + "pid": 96504, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 148258816, + "heapTotal": 103219200, + "heapUsed": 32679896, + "external": 5720461, + "arrayBuffers": 2086635, + "maxRssKiB": 144784 + }, + { + "pid": 96504, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 148275200, + "heapTotal": 104529920, + "heapUsed": 33729872, + "external": 5727565, + "arrayBuffers": 2086635, + "maxRssKiB": 144800 + }, + { + "pid": 96504, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 148307968, + "heapTotal": 105578496, + "heapUsed": 34754768, + "external": 5719373, + "arrayBuffers": 2086635, + "maxRssKiB": 144832 + }, + { + "pid": 96504, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 148307968, + "heapTotal": 106364928, + "heapUsed": 35775912, + "external": 5727409, + "arrayBuffers": 2086635, + "maxRssKiB": 144832 + }, + { + "pid": 96504, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 148455424, + "heapTotal": 107675648, + "heapUsed": 36797736, + "external": 5719217, + "arrayBuffers": 2086635, + "maxRssKiB": 144976 + }, + { + "pid": 96504, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 149143552, + "heapTotal": 108986368, + "heapUsed": 37852776, + "external": 5727408, + "arrayBuffers": 2086635, + "maxRssKiB": 145648 + }, + { + "pid": 96504, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 149684224, + "heapTotal": 110297088, + "heapUsed": 38895960, + "external": 5727408, + "arrayBuffers": 2086635, + "maxRssKiB": 146176 + } + ] + }, + { + "id": "2-warmup-2-session", + "phase": "warmup", + "mode": "session", + "workers": 2, + "wallSeconds": 3.541855040999999, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "tests": 33, + "leftovers": 0, + "openHandles": 0, + "children": [ + { + "pid": 96821, + "build": 1, + "rootDir": ".cache/integration/fixture-FmnjsN", + "cwd": ".cache/integration/session-ryC8xE", + "rss": 338231296, + "heapUsed": 71203192, + "maxRssKiB": 330432 + }, + { + "pid": 96822, + "build": 1, + "rootDir": ".cache/integration/fixture-PaQlKf", + "cwd": ".cache/integration/session-UqKC6r", + "rss": 343883776, + "heapUsed": 71208264, + "maxRssKiB": 335952 + }, + { + "pid": 96821, + "build": 2, + "rootDir": ".cache/integration/fixture-R34AsH", + "cwd": ".cache/integration/session-ryC8xE", + "rss": 350961664, + "heapUsed": 72813176, + "maxRssKiB": 342864 + }, + { + "pid": 96822, + "build": 2, + "rootDir": ".cache/integration/fixture-HSmPZL", + "cwd": ".cache/integration/session-UqKC6r", + "rss": 356040704, + "heapUsed": 72816680, + "maxRssKiB": 347824 + }, + { + "pid": 96821, + "build": 3, + "rootDir": ".cache/integration/fixture-1CXC53", + "cwd": ".cache/integration/session-ryC8xE", + "rss": 359677952, + "heapUsed": 74448720, + "maxRssKiB": 351376 + }, + { + "pid": 96822, + "build": 3, + "rootDir": ".cache/integration/fixture-pzz9Tx", + "cwd": ".cache/integration/session-UqKC6r", + "rss": 367460352, + "heapUsed": 74456936, + "maxRssKiB": 358976 + }, + { + "pid": 96821, + "build": 4, + "rootDir": ".cache/integration/fixture-f20gKb", + "cwd": ".cache/integration/session-ryC8xE", + "rss": 369623040, + "heapUsed": 75995392, + "maxRssKiB": 361088 + }, + { + "pid": 96822, + "build": 4, + "rootDir": ".cache/integration/fixture-caxDRQ", + "cwd": ".cache/integration/session-UqKC6r", + "rss": 376193024, + "heapUsed": 76006456, + "maxRssKiB": 367504 + }, + { + "pid": 96821, + "build": 5, + "rootDir": ".cache/integration/fixture-MhISh4", + "cwd": ".cache/integration/session-ryC8xE", + "rss": 370327552, + "heapUsed": 76563168, + "maxRssKiB": 361776 + }, + { + "pid": 96822, + "build": 5, + "rootDir": ".cache/integration/fixture-ZAjtWK", + "cwd": ".cache/integration/session-UqKC6r", + "rss": 384253952, + "heapUsed": 77604032, + "maxRssKiB": 375376 + }, + { + "pid": 96821, + "build": 6, + "rootDir": ".cache/integration/fixture-30NlPT", + "cwd": ".cache/integration/session-ryC8xE", + "rss": 370868224, + "heapUsed": 77100416, + "maxRssKiB": 362304 + }, + { + "pid": 96822, + "build": 6, + "rootDir": ".cache/integration/fixture-Zssut1", + "cwd": ".cache/integration/session-UqKC6r", + "rss": 392724480, + "heapUsed": 79179744, + "maxRssKiB": 383648 + }, + { + "pid": 96821, + "build": 7, + "rootDir": ".cache/integration/fixture-sOG3Qc", + "cwd": ".cache/integration/session-ryC8xE", + "rss": 371523584, + "heapUsed": 77549520, + "maxRssKiB": 362944 + }, + { + "pid": 96821, + "build": 8, + "rootDir": ".cache/integration/fixture-1c6Nvo", + "cwd": ".cache/integration/session-ryC8xE", + "rss": 372097024, + "heapUsed": 78090696, + "maxRssKiB": 363504 + }, + { + "pid": 96822, + "build": 7, + "rootDir": ".cache/integration/fixture-eTE8Zb", + "cwd": ".cache/integration/session-UqKC6r", + "rss": 401260544, + "heapUsed": 80669736, + "maxRssKiB": 391984 + }, + { + "pid": 96821, + "build": 9, + "rootDir": ".cache/integration/fixture-GNRICB", + "cwd": ".cache/integration/session-ryC8xE", + "rss": 372752384, + "heapUsed": 78794440, + "maxRssKiB": 364144 + }, + { + "pid": 96822, + "build": 8, + "rootDir": ".cache/integration/fixture-kIoxlz", + "cwd": ".cache/integration/session-UqKC6r", + "rss": 409583616, + "heapUsed": 82248016, + "maxRssKiB": 400112 + }, + { + "pid": 96821, + "build": 10, + "rootDir": ".cache/integration/fixture-wav3Bk", + "cwd": ".cache/integration/session-ryC8xE", + "rss": 373407744, + "heapUsed": 79395936, + "maxRssKiB": 364784 + }, + { + "pid": 96822, + "build": 9, + "rootDir": ".cache/integration/fixture-AFo5aY", + "cwd": ".cache/integration/session-UqKC6r", + "rss": 410976256, + "heapUsed": 82956832, + "maxRssKiB": 401472 + }, + { + "pid": 96822, + "build": 10, + "rootDir": ".cache/integration/fixture-F8crQg", + "cwd": ".cache/integration/session-UqKC6r", + "rss": 412385280, + "heapUsed": 83548032, + "maxRssKiB": 402848 + }, + { + "pid": 96823, + "build": 1, + "rootDir": ".cache/integration/fixture-DbcV6R", + "cwd": ".cache/integration/session-KB3RvB", + "rss": 313458688, + "heapUsed": 71143776, + "maxRssKiB": 306240 + }, + { + "pid": 96823, + "build": 2, + "rootDir": ".cache/integration/fixture-cbGZyO", + "cwd": ".cache/integration/session-KB3RvB", + "rss": 326172672, + "heapUsed": 72841032, + "maxRssKiB": 318656 + }, + { + "pid": 96823, + "build": 3, + "rootDir": ".cache/integration/fixture-UKNDEU", + "cwd": ".cache/integration/session-KB3RvB", + "rss": 336838656, + "heapUsed": 74467680, + "maxRssKiB": 329072 + }, + { + "pid": 96823, + "build": 4, + "rootDir": ".cache/integration/fixture-lEt0Ex", + "cwd": ".cache/integration/session-KB3RvB", + "rss": 348536832, + "heapUsed": 76021032, + "maxRssKiB": 340496 + }, + { + "pid": 96823, + "build": 5, + "rootDir": ".cache/integration/fixture-uz6de7", + "cwd": ".cache/integration/session-KB3RvB", + "rss": 349913088, + "heapUsed": 76588136, + "maxRssKiB": 341840 + }, + { + "pid": 96823, + "build": 6, + "rootDir": ".cache/integration/fixture-Q0dVrD", + "cwd": ".cache/integration/session-KB3RvB", + "rss": 351010816, + "heapUsed": 77127288, + "maxRssKiB": 342912 + }, + { + "pid": 96823, + "build": 7, + "rootDir": ".cache/integration/fixture-sMAeKc", + "cwd": ".cache/integration/session-KB3RvB", + "rss": 352256000, + "heapUsed": 77587000, + "maxRssKiB": 344128 + }, + { + "pid": 96823, + "build": 8, + "rootDir": ".cache/integration/fixture-9P4V4U", + "cwd": ".cache/integration/session-KB3RvB", + "rss": 358973440, + "heapUsed": 78117096, + "maxRssKiB": 350688 + }, + { + "pid": 96823, + "build": 9, + "rootDir": ".cache/integration/fixture-3pFdIY", + "cwd": ".cache/integration/session-KB3RvB", + "rss": 360431616, + "heapUsed": 78818784, + "maxRssKiB": 352112 + }, + { + "pid": 96823, + "build": 10, + "rootDir": ".cache/integration/fixture-xQiSnU", + "cwd": ".cache/integration/session-KB3RvB", + "rss": 361840640, + "heapUsed": 79422616, + "maxRssKiB": 353488 + } + ], + "parentSamples": [ + { + "pid": 96805, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115752960, + "heapTotal": 60702720, + "heapUsed": 24852728, + "external": 5723121, + "arrayBuffers": 2062066, + "maxRssKiB": 113248 + }, + { + "pid": 96806, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115441664, + "heapTotal": 60964864, + "heapUsed": 24852920, + "external": 5723129, + "arrayBuffers": 2062066, + "maxRssKiB": 112944 + }, + { + "pid": 96806, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116162560, + "heapTotal": 61751296, + "heapUsed": 25882608, + "external": 5703744, + "arrayBuffers": 2070258, + "maxRssKiB": 113440 + }, + { + "pid": 96805, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116457472, + "heapTotal": 61751296, + "heapUsed": 25883184, + "external": 5703737, + "arrayBuffers": 2070258, + "maxRssKiB": 113728 + }, + { + "pid": 96805, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117030912, + "heapTotal": 63062016, + "heapUsed": 26940920, + "external": 5703706, + "arrayBuffers": 2070258, + "maxRssKiB": 114288 + }, + { + "pid": 96806, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 116736000, + "heapTotal": 62799872, + "heapUsed": 26940608, + "external": 5703713, + "arrayBuffers": 2070258, + "maxRssKiB": 114000 + }, + { + "pid": 96806, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117374976, + "heapTotal": 63848448, + "heapUsed": 28000584, + "external": 5711934, + "arrayBuffers": 2070258, + "maxRssKiB": 114624 + }, + { + "pid": 96805, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117522432, + "heapTotal": 63848448, + "heapUsed": 28001088, + "external": 5711927, + "arrayBuffers": 2070258, + "maxRssKiB": 114768 + }, + { + "pid": 96806, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 118145024, + "heapTotal": 65159168, + "heapUsed": 29059176, + "external": 5703091, + "arrayBuffers": 2070258, + "maxRssKiB": 115376 + }, + { + "pid": 96805, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 118226944, + "heapTotal": 65159168, + "heapUsed": 29046800, + "external": 5703864, + "arrayBuffers": 2070258, + "maxRssKiB": 115456 + }, + { + "pid": 96806, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 118734848, + "heapTotal": 66207744, + "heapUsed": 30088032, + "external": 5711283, + "arrayBuffers": 2070258, + "maxRssKiB": 115968 + }, + { + "pid": 96805, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 118865920, + "heapTotal": 66207744, + "heapUsed": 30078560, + "external": 5712085, + "arrayBuffers": 2070258, + "maxRssKiB": 116096 + }, + { + "pid": 96806, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 119504896, + "heapTotal": 67256320, + "heapUsed": 31109968, + "external": 5702935, + "arrayBuffers": 2070258, + "maxRssKiB": 116704 + }, + { + "pid": 96806, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 120209408, + "heapTotal": 68567040, + "heapUsed": 32133272, + "external": 5711127, + "arrayBuffers": 2070258, + "maxRssKiB": 117392 + }, + { + "pid": 96805, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 119619584, + "heapTotal": 67256320, + "heapUsed": 31102536, + "external": 5711900, + "arrayBuffers": 2070258, + "maxRssKiB": 116816 + }, + { + "pid": 96806, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 120864768, + "heapTotal": 69615616, + "heapUsed": 33188856, + "external": 5702934, + "arrayBuffers": 2070258, + "maxRssKiB": 118032 + }, + { + "pid": 96805, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 120356864, + "heapTotal": 68567040, + "heapUsed": 32125952, + "external": 5703737, + "arrayBuffers": 2070258, + "maxRssKiB": 117536 + }, + { + "pid": 96806, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 121634816, + "heapTotal": 70664192, + "heapUsed": 34221520, + "external": 5711128, + "arrayBuffers": 2070258, + "maxRssKiB": 118784 + }, + { + "pid": 96805, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121110528, + "heapTotal": 69877760, + "heapUsed": 33192768, + "external": 5702932, + "arrayBuffers": 2070258, + "maxRssKiB": 118272 + }, + { + "pid": 96805, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 121683968, + "heapTotal": 70926336, + "heapUsed": 34224808, + "external": 5711126, + "arrayBuffers": 2070258, + "maxRssKiB": 118848 + }, + { + "pid": 96806, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 138313728, + "heapTotal": 103743488, + "heapUsed": 29537216, + "external": 7765979, + "arrayBuffers": 2078457, + "maxRssKiB": 135200 + }, + { + "pid": 96805, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 138641408, + "heapTotal": 104529920, + "heapUsed": 29656840, + "external": 7774185, + "arrayBuffers": 2086642, + "maxRssKiB": 135520 + }, + { + "pid": 96806, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 138756096, + "heapTotal": 100859904, + "heapUsed": 30527400, + "external": 5720155, + "arrayBuffers": 2086649, + "maxRssKiB": 135504 + }, + { + "pid": 96806, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 139051008, + "heapTotal": 101646336, + "heapUsed": 31561664, + "external": 5720124, + "arrayBuffers": 2086649, + "maxRssKiB": 135792 + }, + { + "pid": 96806, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 139067392, + "heapTotal": 103219200, + "heapUsed": 32600224, + "external": 5728345, + "arrayBuffers": 2086649, + "maxRssKiB": 135808 + }, + { + "pid": 96806, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 139067392, + "heapTotal": 104005632, + "heapUsed": 33639712, + "external": 5719490, + "arrayBuffers": 2086649, + "maxRssKiB": 135808 + }, + { + "pid": 96806, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 139427840, + "heapTotal": 105316352, + "heapUsed": 34677920, + "external": 5727682, + "arrayBuffers": 2086649, + "maxRssKiB": 136160 + }, + { + "pid": 96806, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 139952128, + "heapTotal": 106364928, + "heapUsed": 35703336, + "external": 5719334, + "arrayBuffers": 2086649, + "maxRssKiB": 136688 + }, + { + "pid": 96806, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 140705792, + "heapTotal": 107675648, + "heapUsed": 36723400, + "external": 5727526, + "arrayBuffers": 2086649, + "maxRssKiB": 137408 + }, + { + "pid": 96806, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 141361152, + "heapTotal": 108724224, + "heapUsed": 37762328, + "external": 5719333, + "arrayBuffers": 2086649, + "maxRssKiB": 138048 + }, + { + "pid": 96806, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 141983744, + "heapTotal": 109772800, + "heapUsed": 38790752, + "external": 5727527, + "arrayBuffers": 2086649, + "maxRssKiB": 138656 + }, + { + "pid": 96805, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 139296768, + "heapTotal": 101384192, + "heapUsed": 30643904, + "external": 5728945, + "arrayBuffers": 2094834, + "maxRssKiB": 136032 + }, + { + "pid": 96805, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 139378688, + "heapTotal": 101908480, + "heapUsed": 31689616, + "external": 5729485, + "arrayBuffers": 2094834, + "maxRssKiB": 136112 + } + ] + }, + { + "id": "3-measured-2-cli", + "phase": "measured", + "mode": "cli", + "workers": 2, + "wallSeconds": 13.095825540999998, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "tests": 33, + "leftovers": 0, + "openHandles": 0, + "children": [], + "parentSamples": [ + { + "pid": 96877, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 115998720, + "heapTotal": 60964864, + "heapUsed": 24957576, + "external": 5723163, + "arrayBuffers": 2062059, + "maxRssKiB": 113408 + }, + { + "pid": 96878, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116719616, + "heapTotal": 60440576, + "heapUsed": 24862504, + "external": 5722810, + "arrayBuffers": 2062059, + "maxRssKiB": 114224 + }, + { + "pid": 96877, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 116801536, + "heapTotal": 61489152, + "heapUsed": 25989744, + "external": 5704362, + "arrayBuffers": 2070251, + "maxRssKiB": 114064 + }, + { + "pid": 96878, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 117522432, + "heapTotal": 61489152, + "heapUsed": 25892256, + "external": 5704043, + "arrayBuffers": 2070251, + "maxRssKiB": 114768 + }, + { + "pid": 96877, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 117243904, + "heapTotal": 62537728, + "heapUsed": 27060600, + "external": 5704902, + "arrayBuffers": 2070251, + "maxRssKiB": 114496 + }, + { + "pid": 96878, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 118112256, + "heapTotal": 62537728, + "heapUsed": 26956384, + "external": 5712204, + "arrayBuffers": 2070251, + "maxRssKiB": 115344 + }, + { + "pid": 96877, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 125140992, + "heapTotal": 66256896, + "heapUsed": 29326664, + "external": 7773834, + "arrayBuffers": 2078443, + "maxRssKiB": 122336 + }, + { + "pid": 96878, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 118898688, + "heapTotal": 63586304, + "heapUsed": 28033240, + "external": 5704041, + "arrayBuffers": 2070251, + "maxRssKiB": 116112 + }, + { + "pid": 96877, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 125403136, + "heapTotal": 66781184, + "heapUsed": 30363960, + "external": 5720437, + "arrayBuffers": 2086635, + "maxRssKiB": 122464 + }, + { + "pid": 96878, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 119619584, + "heapTotal": 64897024, + "heapUsed": 29091832, + "external": 5712362, + "arrayBuffers": 2070251, + "maxRssKiB": 116816 + }, + { + "pid": 96877, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 125829120, + "heapTotal": 67829760, + "heapUsed": 31411776, + "external": 5728598, + "arrayBuffers": 2086635, + "maxRssKiB": 122880 + }, + { + "pid": 96878, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 120291328, + "heapTotal": 65683456, + "heapUsed": 30120592, + "external": 5712391, + "arrayBuffers": 2070251, + "maxRssKiB": 117472 + }, + { + "pid": 96877, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 128811008, + "heapTotal": 102432768, + "heapUsed": 32470688, + "external": 5720435, + "arrayBuffers": 2086635, + "maxRssKiB": 125792 + }, + { + "pid": 96878, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 121044992, + "heapTotal": 66994176, + "heapUsed": 31145048, + "external": 5704014, + "arrayBuffers": 2070251, + "maxRssKiB": 118208 + }, + { + "pid": 96877, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 129433600, + "heapTotal": 103743488, + "heapUsed": 33518192, + "external": 5727557, + "arrayBuffers": 2086635, + "maxRssKiB": 126400 + }, + { + "pid": 96878, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 121798656, + "heapTotal": 68042752, + "heapUsed": 32170776, + "external": 5712235, + "arrayBuffers": 2070251, + "maxRssKiB": 118944 + }, + { + "pid": 96877, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 129974272, + "heapTotal": 104792064, + "heapUsed": 34564520, + "external": 5719365, + "arrayBuffers": 2086635, + "maxRssKiB": 126928 + }, + { + "pid": 96878, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 122552320, + "heapTotal": 69353472, + "heapUsed": 33249120, + "external": 5702822, + "arrayBuffers": 2070251, + "maxRssKiB": 119680 + }, + { + "pid": 96877, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 130629632, + "heapTotal": 105840640, + "heapUsed": 35590760, + "external": 5727401, + "arrayBuffers": 2086635, + "maxRssKiB": 127568 + }, + { + "pid": 96878, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 123256832, + "heapTotal": 70402048, + "heapUsed": 34279288, + "external": 5711014, + "arrayBuffers": 2070251, + "maxRssKiB": 120368 + }, + { + "pid": 96877, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 131268608, + "heapTotal": 106889216, + "heapUsed": 36611848, + "external": 5719209, + "arrayBuffers": 2086635, + "maxRssKiB": 128208 + }, + { + "pid": 96878, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 140230656, + "heapTotal": 106102784, + "heapUsed": 29550376, + "external": 7765670, + "arrayBuffers": 2078443, + "maxRssKiB": 137072 + }, + { + "pid": 96877, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 131940352, + "heapTotal": 107675648, + "heapUsed": 37664920, + "external": 5727400, + "arrayBuffers": 2086635, + "maxRssKiB": 128848 + }, + { + "pid": 96878, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 140525568, + "heapTotal": 101122048, + "heapUsed": 30539176, + "external": 5720463, + "arrayBuffers": 2086635, + "maxRssKiB": 137232 + }, + { + "pid": 96877, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 132595712, + "heapTotal": 108986368, + "heapUsed": 38696336, + "external": 5727400, + "arrayBuffers": 2086635, + "maxRssKiB": 129488 + }, + { + "pid": 96878, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 140541952, + "heapTotal": 101646336, + "heapUsed": 31572832, + "external": 5728624, + "arrayBuffers": 2086635, + "maxRssKiB": 137248 + }, + { + "pid": 96878, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 140673024, + "heapTotal": 102694912, + "heapUsed": 32615456, + "external": 5720461, + "arrayBuffers": 2086635, + "maxRssKiB": 137376 + }, + { + "pid": 96878, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 140673024, + "heapTotal": 103743488, + "heapUsed": 33662240, + "external": 5727565, + "arrayBuffers": 2086635, + "maxRssKiB": 137376 + }, + { + "pid": 96878, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 140705792, + "heapTotal": 104792064, + "heapUsed": 34701368, + "external": 5719373, + "arrayBuffers": 2086635, + "maxRssKiB": 137408 + }, + { + "pid": 96878, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 140705792, + "heapTotal": 106102784, + "heapUsed": 35721976, + "external": 5727409, + "arrayBuffers": 2086635, + "maxRssKiB": 137408 + }, + { + "pid": 96878, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 140722176, + "heapTotal": 107151360, + "heapUsed": 36747296, + "external": 5719217, + "arrayBuffers": 2086635, + "maxRssKiB": 137424 + }, + { + "pid": 96878, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 141213696, + "heapTotal": 108199936, + "heapUsed": 37787568, + "external": 5727408, + "arrayBuffers": 2086635, + "maxRssKiB": 137904 + }, + { + "pid": 96878, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 141869056, + "heapTotal": 109510656, + "heapUsed": 38817096, + "external": 5727408, + "arrayBuffers": 2086635, + "maxRssKiB": 138544 + } + ] + }, + { + "id": "4-measured-2-session", + "phase": "measured", + "mode": "session", + "workers": 2, + "wallSeconds": 3.6496417919999984, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "tests": 33, + "leftovers": 0, + "openHandles": 0, + "children": [ + { + "pid": 97237, + "build": 1, + "rootDir": ".cache/integration/fixture-8vou0G", + "cwd": ".cache/integration/session-bYzhbx", + "rss": 316702720, + "heapUsed": 71158840, + "maxRssKiB": 309408 + }, + { + "pid": 97236, + "build": 1, + "rootDir": ".cache/integration/fixture-in7Fhi", + "cwd": ".cache/integration/session-Pa9e5n", + "rss": 341245952, + "heapUsed": 71199424, + "maxRssKiB": 333376 + }, + { + "pid": 97237, + "build": 2, + "rootDir": ".cache/integration/fixture-Y0mCMA", + "cwd": ".cache/integration/session-bYzhbx", + "rss": 328744960, + "heapUsed": 72851856, + "maxRssKiB": 321168 + }, + { + "pid": 97236, + "build": 2, + "rootDir": ".cache/integration/fixture-6SJuMK", + "cwd": ".cache/integration/session-Pa9e5n", + "rss": 353697792, + "heapUsed": 72805168, + "maxRssKiB": 345536 + }, + { + "pid": 97236, + "build": 3, + "rootDir": ".cache/integration/fixture-JvGs4o", + "cwd": ".cache/integration/session-Pa9e5n", + "rss": 363446272, + "heapUsed": 74443216, + "maxRssKiB": 355056 + }, + { + "pid": 97237, + "build": 3, + "rootDir": ".cache/integration/fixture-lhHxVP", + "cwd": ".cache/integration/session-bYzhbx", + "rss": 340410368, + "heapUsed": 74485528, + "maxRssKiB": 332560 + }, + { + "pid": 97237, + "build": 4, + "rootDir": ".cache/integration/fixture-SpzkVx", + "cwd": ".cache/integration/session-bYzhbx", + "rss": 351862784, + "heapUsed": 76036840, + "maxRssKiB": 343744 + }, + { + "pid": 97236, + "build": 4, + "rootDir": ".cache/integration/fixture-L3Sw3B", + "cwd": ".cache/integration/session-Pa9e5n", + "rss": 372555776, + "heapUsed": 75997696, + "maxRssKiB": 363952 + }, + { + "pid": 97237, + "build": 5, + "rootDir": ".cache/integration/fixture-yhBO7a", + "cwd": ".cache/integration/session-bYzhbx", + "rss": 353435648, + "heapUsed": 76604888, + "maxRssKiB": 345280 + }, + { + "pid": 97236, + "build": 5, + "rootDir": ".cache/integration/fixture-3aOQx8", + "cwd": ".cache/integration/session-Pa9e5n", + "rss": 381583360, + "heapUsed": 77596040, + "maxRssKiB": 372768 + }, + { + "pid": 97237, + "build": 6, + "rootDir": ".cache/integration/fixture-SLKwOk", + "cwd": ".cache/integration/session-bYzhbx", + "rss": 354451456, + "heapUsed": 77142576, + "maxRssKiB": 346272 + }, + { + "pid": 97237, + "build": 7, + "rootDir": ".cache/integration/fixture-NifYog", + "cwd": ".cache/integration/session-bYzhbx", + "rss": 355696640, + "heapUsed": 77588440, + "maxRssKiB": 347488 + }, + { + "pid": 97236, + "build": 6, + "rootDir": ".cache/integration/fixture-TSzAi2", + "cwd": ".cache/integration/session-Pa9e5n", + "rss": 389758976, + "heapUsed": 79171928, + "maxRssKiB": 380752 + }, + { + "pid": 97237, + "build": 8, + "rootDir": ".cache/integration/fixture-Vp7uTb", + "cwd": ".cache/integration/session-bYzhbx", + "rss": 356827136, + "heapUsed": 78132936, + "maxRssKiB": 348592 + }, + { + "pid": 97236, + "build": 7, + "rootDir": ".cache/integration/fixture-qDwzRA", + "cwd": ".cache/integration/session-Pa9e5n", + "rss": 397705216, + "heapUsed": 80663664, + "maxRssKiB": 388512 + }, + { + "pid": 97237, + "build": 9, + "rootDir": ".cache/integration/fixture-LIm5ai", + "cwd": ".cache/integration/session-bYzhbx", + "rss": 358219776, + "heapUsed": 78833800, + "maxRssKiB": 349952 + }, + { + "pid": 97237, + "build": 10, + "rootDir": ".cache/integration/fixture-qJ1PR7", + "cwd": ".cache/integration/session-bYzhbx", + "rss": 359759872, + "heapUsed": 79437600, + "maxRssKiB": 351456 + }, + { + "pid": 97236, + "build": 8, + "rootDir": ".cache/integration/fixture-b2R8By", + "cwd": ".cache/integration/session-Pa9e5n", + "rss": 407339008, + "heapUsed": 82242552, + "maxRssKiB": 397920 + }, + { + "pid": 97236, + "build": 9, + "rootDir": ".cache/integration/fixture-rcYCcw", + "cwd": ".cache/integration/session-Pa9e5n", + "rss": 408748032, + "heapUsed": 82948704, + "maxRssKiB": 399296 + }, + { + "pid": 97236, + "build": 10, + "rootDir": ".cache/integration/fixture-EacgpA", + "cwd": ".cache/integration/session-Pa9e5n", + "rss": 410107904, + "heapUsed": 83538944, + "maxRssKiB": 400624 + }, + { + "pid": 97238, + "build": 1, + "rootDir": ".cache/integration/fixture-kD5TCC", + "cwd": ".cache/integration/session-v3McW2", + "rss": 339214336, + "heapUsed": 71199416, + "maxRssKiB": 331392 + }, + { + "pid": 97238, + "build": 2, + "rootDir": ".cache/integration/fixture-0vapv0", + "cwd": ".cache/integration/session-v3McW2", + "rss": 351567872, + "heapUsed": 72808672, + "maxRssKiB": 343456 + }, + { + "pid": 97238, + "build": 3, + "rootDir": ".cache/integration/fixture-YmjQfY", + "cwd": ".cache/integration/session-v3McW2", + "rss": 362528768, + "heapUsed": 74449392, + "maxRssKiB": 354160 + }, + { + "pid": 97238, + "build": 4, + "rootDir": ".cache/integration/fixture-PVyG6w", + "cwd": ".cache/integration/session-v3McW2", + "rss": 372375552, + "heapUsed": 75999024, + "maxRssKiB": 363776 + }, + { + "pid": 97238, + "build": 5, + "rootDir": ".cache/integration/fixture-pBGuht", + "cwd": ".cache/integration/session-v3McW2", + "rss": 373161984, + "heapUsed": 76559624, + "maxRssKiB": 364544 + }, + { + "pid": 97238, + "build": 6, + "rootDir": ".cache/integration/fixture-COqo9f", + "cwd": ".cache/integration/session-v3McW2", + "rss": 373751808, + "heapUsed": 77097456, + "maxRssKiB": 365120 + }, + { + "pid": 97238, + "build": 7, + "rootDir": ".cache/integration/fixture-fIUU2f", + "cwd": ".cache/integration/session-v3McW2", + "rss": 374489088, + "heapUsed": 77561512, + "maxRssKiB": 365840 + }, + { + "pid": 97238, + "build": 8, + "rootDir": ".cache/integration/fixture-p20i2f", + "cwd": ".cache/integration/session-v3McW2", + "rss": 375242752, + "heapUsed": 78092976, + "maxRssKiB": 366576 + }, + { + "pid": 97238, + "build": 9, + "rootDir": ".cache/integration/fixture-ObWwOD", + "cwd": ".cache/integration/session-v3McW2", + "rss": 376111104, + "heapUsed": 78790840, + "maxRssKiB": 367424 + }, + { + "pid": 97238, + "build": 10, + "rootDir": ".cache/integration/fixture-8mROAt", + "cwd": ".cache/integration/session-v3McW2", + "rss": 376782848, + "heapUsed": 79395736, + "maxRssKiB": 368080 + } + ], + "parentSamples": [ + { + "pid": 97234, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116228096, + "heapTotal": 60964864, + "heapUsed": 24852400, + "external": 5723131, + "arrayBuffers": 2062066, + "maxRssKiB": 113728 + }, + { + "pid": 97233, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116359168, + "heapTotal": 60440576, + "heapUsed": 24853272, + "external": 5723123, + "arrayBuffers": 2062066, + "maxRssKiB": 113808 + }, + { + "pid": 97234, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 117014528, + "heapTotal": 61489152, + "heapUsed": 25879136, + "external": 5703744, + "arrayBuffers": 2070258, + "maxRssKiB": 114272 + }, + { + "pid": 97233, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 117096448, + "heapTotal": 61489152, + "heapUsed": 25882640, + "external": 5703737, + "arrayBuffers": 2070258, + "maxRssKiB": 114352 + }, + { + "pid": 97234, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117538816, + "heapTotal": 62799872, + "heapUsed": 26938352, + "external": 5703713, + "arrayBuffers": 2070258, + "maxRssKiB": 114784 + }, + { + "pid": 97233, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117555200, + "heapTotal": 62537728, + "heapUsed": 26938440, + "external": 5703706, + "arrayBuffers": 2070258, + "maxRssKiB": 114800 + }, + { + "pid": 97234, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 118308864, + "heapTotal": 63848448, + "heapUsed": 28001016, + "external": 5711934, + "arrayBuffers": 2070258, + "maxRssKiB": 115536 + }, + { + "pid": 97233, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 118161408, + "heapTotal": 63586304, + "heapUsed": 28001296, + "external": 5711927, + "arrayBuffers": 2070258, + "maxRssKiB": 115392 + }, + { + "pid": 97234, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 119013376, + "heapTotal": 64634880, + "heapUsed": 29060488, + "external": 5703091, + "arrayBuffers": 2070258, + "maxRssKiB": 116240 + }, + { + "pid": 97233, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 118865920, + "heapTotal": 64897024, + "heapUsed": 29048320, + "external": 5703864, + "arrayBuffers": 2070258, + "maxRssKiB": 116080 + }, + { + "pid": 97234, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 119668736, + "heapTotal": 65945600, + "heapUsed": 30086800, + "external": 5711283, + "arrayBuffers": 2070258, + "maxRssKiB": 116864 + }, + { + "pid": 97234, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 120356864, + "heapTotal": 66994176, + "heapUsed": 31110384, + "external": 5702935, + "arrayBuffers": 2070258, + "maxRssKiB": 117536 + }, + { + "pid": 97233, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 119603200, + "heapTotal": 65945600, + "heapUsed": 30078704, + "external": 5712085, + "arrayBuffers": 2070258, + "maxRssKiB": 116800 + }, + { + "pid": 97234, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 121044992, + "heapTotal": 68042752, + "heapUsed": 32133832, + "external": 5711127, + "arrayBuffers": 2070258, + "maxRssKiB": 118224 + }, + { + "pid": 97233, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 120340480, + "heapTotal": 67256320, + "heapUsed": 31101368, + "external": 5711900, + "arrayBuffers": 2070258, + "maxRssKiB": 117520 + }, + { + "pid": 97234, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121683968, + "heapTotal": 69091328, + "heapUsed": 33188984, + "external": 5702934, + "arrayBuffers": 2070258, + "maxRssKiB": 118832 + }, + { + "pid": 97234, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122388480, + "heapTotal": 70139904, + "heapUsed": 34219616, + "external": 5711128, + "arrayBuffers": 2070258, + "maxRssKiB": 119520 + }, + { + "pid": 97233, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 121012224, + "heapTotal": 68567040, + "heapUsed": 32125008, + "external": 5703737, + "arrayBuffers": 2070258, + "maxRssKiB": 118176 + }, + { + "pid": 97233, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121749504, + "heapTotal": 69353472, + "heapUsed": 33193248, + "external": 5702932, + "arrayBuffers": 2070258, + "maxRssKiB": 118896 + }, + { + "pid": 97233, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122486784, + "heapTotal": 70664192, + "heapUsed": 34225928, + "external": 5711126, + "arrayBuffers": 2070258, + "maxRssKiB": 119616 + }, + { + "pid": 97234, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 138805248, + "heapTotal": 104005632, + "heapUsed": 29537312, + "external": 7765981, + "arrayBuffers": 2078457, + "maxRssKiB": 135680 + }, + { + "pid": 97233, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 139182080, + "heapTotal": 106364928, + "heapUsed": 29678632, + "external": 7774187, + "arrayBuffers": 2086642, + "maxRssKiB": 136048 + }, + { + "pid": 97234, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 139362304, + "heapTotal": 101384192, + "heapUsed": 30526280, + "external": 5720155, + "arrayBuffers": 2086649, + "maxRssKiB": 136096 + }, + { + "pid": 97234, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 139378688, + "heapTotal": 101908480, + "heapUsed": 31563024, + "external": 5720124, + "arrayBuffers": 2086649, + "maxRssKiB": 136112 + }, + { + "pid": 97234, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 139395072, + "heapTotal": 102957056, + "heapUsed": 32598448, + "external": 5728345, + "arrayBuffers": 2086649, + "maxRssKiB": 136128 + }, + { + "pid": 97234, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 139558912, + "heapTotal": 104005632, + "heapUsed": 33640224, + "external": 5719490, + "arrayBuffers": 2086649, + "maxRssKiB": 136288 + }, + { + "pid": 97234, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 139689984, + "heapTotal": 105316352, + "heapUsed": 34677088, + "external": 5727682, + "arrayBuffers": 2086649, + "maxRssKiB": 136416 + }, + { + "pid": 97234, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 140034048, + "heapTotal": 106627072, + "heapUsed": 35703896, + "external": 5719334, + "arrayBuffers": 2086649, + "maxRssKiB": 136784 + }, + { + "pid": 97234, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 140738560, + "heapTotal": 107675648, + "heapUsed": 36722456, + "external": 5727526, + "arrayBuffers": 2086649, + "maxRssKiB": 137440 + }, + { + "pid": 97234, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 141361152, + "heapTotal": 108462080, + "heapUsed": 37762216, + "external": 5719333, + "arrayBuffers": 2086649, + "maxRssKiB": 138048 + }, + { + "pid": 97234, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 142065664, + "heapTotal": 109772800, + "heapUsed": 38792768, + "external": 5727527, + "arrayBuffers": 2086649, + "maxRssKiB": 138736 + }, + { + "pid": 97233, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 139952128, + "heapTotal": 101122048, + "heapUsed": 30663328, + "external": 5728945, + "arrayBuffers": 2094834, + "maxRssKiB": 136672 + }, + { + "pid": 97233, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 140345344, + "heapTotal": 101908480, + "heapUsed": 31710464, + "external": 5729485, + "arrayBuffers": 2094834, + "maxRssKiB": 137056 + } + ] + }, + { + "id": "5-measured-2-session", + "phase": "measured", + "mode": "session", + "workers": 2, + "wallSeconds": 3.5277742089999955, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "tests": 33, + "leftovers": 0, + "openHandles": 0, + "children": [ + { + "pid": 97286, + "build": 1, + "rootDir": ".cache/integration/fixture-aqbrs0", + "cwd": ".cache/integration/session-G8kYZd", + "rss": 312524800, + "heapUsed": 71152440, + "maxRssKiB": 305328 + }, + { + "pid": 97286, + "build": 2, + "rootDir": ".cache/integration/fixture-0GEhER", + "cwd": ".cache/integration/session-G8kYZd", + "rss": 327221248, + "heapUsed": 72839216, + "maxRssKiB": 319680 + }, + { + "pid": 97286, + "build": 3, + "rootDir": ".cache/integration/fixture-8YDC2Y", + "cwd": ".cache/integration/session-G8kYZd", + "rss": 339410944, + "heapUsed": 74473704, + "maxRssKiB": 331584 + }, + { + "pid": 97286, + "build": 4, + "rootDir": ".cache/integration/fixture-5gz7PU", + "cwd": ".cache/integration/session-G8kYZd", + "rss": 350044160, + "heapUsed": 76023080, + "maxRssKiB": 341968 + }, + { + "pid": 97286, + "build": 5, + "rootDir": ".cache/integration/fixture-LqMShT", + "cwd": ".cache/integration/session-G8kYZd", + "rss": 360857600, + "heapUsed": 77627952, + "maxRssKiB": 352528 + }, + { + "pid": 97286, + "build": 6, + "rootDir": ".cache/integration/fixture-exR2EK", + "cwd": ".cache/integration/session-G8kYZd", + "rss": 376029184, + "heapUsed": 79205064, + "maxRssKiB": 367344 + }, + { + "pid": 97286, + "build": 7, + "rootDir": ".cache/integration/fixture-JxYV70", + "cwd": ".cache/integration/session-G8kYZd", + "rss": 385187840, + "heapUsed": 80682880, + "maxRssKiB": 376288 + }, + { + "pid": 97286, + "build": 8, + "rootDir": ".cache/integration/fixture-hmTMFA", + "cwd": ".cache/integration/session-G8kYZd", + "rss": 394903552, + "heapUsed": 82261256, + "maxRssKiB": 385776 + }, + { + "pid": 97286, + "build": 9, + "rootDir": ".cache/integration/fixture-IeZBHy", + "cwd": ".cache/integration/session-G8kYZd", + "rss": 396378112, + "heapUsed": 82970128, + "maxRssKiB": 387216 + }, + { + "pid": 97286, + "build": 10, + "rootDir": ".cache/integration/fixture-UGayAN", + "cwd": ".cache/integration/session-G8kYZd", + "rss": 397656064, + "heapUsed": 83573864, + "maxRssKiB": 388464 + }, + { + "pid": 97348, + "build": 1, + "rootDir": ".cache/integration/fixture-nmJjWd", + "cwd": ".cache/integration/session-i2kDU7", + "rss": 312410112, + "heapUsed": 71142424, + "maxRssKiB": 305216 + }, + { + "pid": 97348, + "build": 2, + "rootDir": ".cache/integration/fixture-NkjxGa", + "cwd": ".cache/integration/session-i2kDU7", + "rss": 325664768, + "heapUsed": 72838216, + "maxRssKiB": 318160 + }, + { + "pid": 97348, + "build": 3, + "rootDir": ".cache/integration/fixture-U90ZFC", + "cwd": ".cache/integration/session-i2kDU7", + "rss": 337428480, + "heapUsed": 74467928, + "maxRssKiB": 329648 + }, + { + "pid": 97348, + "build": 4, + "rootDir": ".cache/integration/fixture-a08n7n", + "cwd": ".cache/integration/session-i2kDU7", + "rss": 348504064, + "heapUsed": 76017264, + "maxRssKiB": 340464 + }, + { + "pid": 97348, + "build": 5, + "rootDir": ".cache/integration/fixture-zjOWdy", + "cwd": ".cache/integration/session-i2kDU7", + "rss": 349863936, + "heapUsed": 76586784, + "maxRssKiB": 341792 + }, + { + "pid": 97348, + "build": 6, + "rootDir": ".cache/integration/fixture-nGjQ3h", + "cwd": ".cache/integration/session-i2kDU7", + "rss": 350928896, + "heapUsed": 77126312, + "maxRssKiB": 342832 + }, + { + "pid": 97348, + "build": 7, + "rootDir": ".cache/integration/fixture-CpFPP7", + "cwd": ".cache/integration/session-i2kDU7", + "rss": 352321536, + "heapUsed": 77573152, + "maxRssKiB": 344192 + }, + { + "pid": 97348, + "build": 8, + "rootDir": ".cache/integration/fixture-a0KWKp", + "cwd": ".cache/integration/session-i2kDU7", + "rss": 359006208, + "heapUsed": 78119544, + "maxRssKiB": 350720 + }, + { + "pid": 97348, + "build": 9, + "rootDir": ".cache/integration/fixture-p5jwyO", + "cwd": ".cache/integration/session-i2kDU7", + "rss": 360398848, + "heapUsed": 78817224, + "maxRssKiB": 352080 + }, + { + "pid": 97348, + "build": 10, + "rootDir": ".cache/integration/fixture-SKVfvg", + "cwd": ".cache/integration/session-i2kDU7", + "rss": 361889792, + "heapUsed": 79422040, + "maxRssKiB": 353536 + }, + { + "pid": 97349, + "build": 1, + "rootDir": ".cache/integration/fixture-Qo6BJV", + "cwd": ".cache/integration/session-LDsTHa", + "rss": 311312384, + "heapUsed": 71140672, + "maxRssKiB": 304144 + }, + { + "pid": 97349, + "build": 2, + "rootDir": ".cache/integration/fixture-coANBP", + "cwd": ".cache/integration/session-LDsTHa", + "rss": 323764224, + "heapUsed": 72832608, + "maxRssKiB": 316304 + }, + { + "pid": 97349, + "build": 3, + "rootDir": ".cache/integration/fixture-Y5JGTy", + "cwd": ".cache/integration/session-LDsTHa", + "rss": 335052800, + "heapUsed": 74466128, + "maxRssKiB": 327328 + }, + { + "pid": 97349, + "build": 4, + "rootDir": ".cache/integration/fixture-iHbAUR", + "cwd": ".cache/integration/session-LDsTHa", + "rss": 345538560, + "heapUsed": 76017456, + "maxRssKiB": 337568 + }, + { + "pid": 97349, + "build": 5, + "rootDir": ".cache/integration/fixture-rLLIMt", + "cwd": ".cache/integration/session-LDsTHa", + "rss": 346963968, + "heapUsed": 76584528, + "maxRssKiB": 338960 + }, + { + "pid": 97349, + "build": 6, + "rootDir": ".cache/integration/fixture-OnWwkW", + "cwd": ".cache/integration/session-LDsTHa", + "rss": 348078080, + "heapUsed": 77122720, + "maxRssKiB": 340048 + }, + { + "pid": 97349, + "build": 7, + "rootDir": ".cache/integration/fixture-hTiOiG", + "cwd": ".cache/integration/session-LDsTHa", + "rss": 349339648, + "heapUsed": 77583344, + "maxRssKiB": 341280 + }, + { + "pid": 97349, + "build": 8, + "rootDir": ".cache/integration/fixture-0E5r7m", + "cwd": ".cache/integration/session-LDsTHa", + "rss": 356040704, + "heapUsed": 78117584, + "maxRssKiB": 347824 + }, + { + "pid": 97349, + "build": 9, + "rootDir": ".cache/integration/fixture-Or831i", + "cwd": ".cache/integration/session-LDsTHa", + "rss": 357548032, + "heapUsed": 78814584, + "maxRssKiB": 349296 + }, + { + "pid": 97349, + "build": 10, + "rootDir": ".cache/integration/fixture-mNlBOl", + "cwd": ".cache/integration/session-LDsTHa", + "rss": 358842368, + "heapUsed": 79421776, + "maxRssKiB": 350560 + } + ], + "parentSamples": [ + { + "pid": 97279, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 116047872, + "heapTotal": 60964864, + "heapUsed": 24957464, + "external": 5723167, + "arrayBuffers": 2101183, + "maxRssKiB": 113456 + }, + { + "pid": 97280, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116211712, + "heapTotal": 60178432, + "heapUsed": 24854072, + "external": 5723123, + "arrayBuffers": 2101139, + "maxRssKiB": 113616 + }, + { + "pid": 97280, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116768768, + "heapTotal": 61227008, + "heapUsed": 25882120, + "external": 5703737, + "arrayBuffers": 2070258, + "maxRssKiB": 114032 + }, + { + "pid": 97280, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117194752, + "heapTotal": 62537728, + "heapUsed": 26938992, + "external": 5703706, + "arrayBuffers": 2070258, + "maxRssKiB": 114448 + }, + { + "pid": 97280, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117850112, + "heapTotal": 63586304, + "heapUsed": 28001840, + "external": 5711927, + "arrayBuffers": 2070258, + "maxRssKiB": 115088 + }, + { + "pid": 97280, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 118587392, + "heapTotal": 64634880, + "heapUsed": 29049032, + "external": 5703864, + "arrayBuffers": 2070258, + "maxRssKiB": 115808 + }, + { + "pid": 97280, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 119291904, + "heapTotal": 65945600, + "heapUsed": 30079544, + "external": 5712085, + "arrayBuffers": 2070258, + "maxRssKiB": 116496 + }, + { + "pid": 97280, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 119996416, + "heapTotal": 66994176, + "heapUsed": 31101760, + "external": 5711900, + "arrayBuffers": 2070258, + "maxRssKiB": 117184 + }, + { + "pid": 97280, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 120750080, + "heapTotal": 68304896, + "heapUsed": 32125032, + "external": 5703737, + "arrayBuffers": 2070258, + "maxRssKiB": 117920 + }, + { + "pid": 97280, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121503744, + "heapTotal": 69353472, + "heapUsed": 33193048, + "external": 5702932, + "arrayBuffers": 2070258, + "maxRssKiB": 118656 + }, + { + "pid": 97280, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122241024, + "heapTotal": 70664192, + "heapUsed": 34225952, + "external": 5711126, + "arrayBuffers": 2070258, + "maxRssKiB": 119376 + }, + { + "pid": 97279, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 116604928, + "heapTotal": 61489152, + "heapUsed": 25987064, + "external": 5704362, + "arrayBuffers": 2070251, + "maxRssKiB": 113872 + }, + { + "pid": 97279, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 117112832, + "heapTotal": 62537728, + "heapUsed": 27059736, + "external": 5704902, + "arrayBuffers": 2070251, + "maxRssKiB": 114368 + }, + { + "pid": 97280, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 139280384, + "heapTotal": 106627072, + "heapUsed": 29539168, + "external": 7774151, + "arrayBuffers": 2086649, + "maxRssKiB": 136144 + }, + { + "pid": 97280, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 140017664, + "heapTotal": 100597760, + "heapUsed": 30527368, + "external": 5728327, + "arrayBuffers": 2094841, + "maxRssKiB": 136736 + }, + { + "pid": 97280, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 140017664, + "heapTotal": 101122048, + "heapUsed": 31562864, + "external": 5728296, + "arrayBuffers": 2094841, + "maxRssKiB": 136736 + }, + { + "pid": 97280, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 140017664, + "heapTotal": 102170624, + "heapUsed": 32601888, + "external": 5736517, + "arrayBuffers": 2094841, + "maxRssKiB": 136736 + }, + { + "pid": 97280, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 140034048, + "heapTotal": 103481344, + "heapUsed": 33640272, + "external": 5727674, + "arrayBuffers": 2094841, + "maxRssKiB": 136752 + }, + { + "pid": 97280, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 140083200, + "heapTotal": 104529920, + "heapUsed": 34679480, + "external": 5735866, + "arrayBuffers": 2094841, + "maxRssKiB": 136800 + }, + { + "pid": 97280, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 140165120, + "heapTotal": 105578496, + "heapUsed": 35703944, + "external": 5727518, + "arrayBuffers": 2094841, + "maxRssKiB": 136880 + }, + { + "pid": 97280, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 140165120, + "heapTotal": 106889216, + "heapUsed": 36724136, + "external": 5735710, + "arrayBuffers": 2094841, + "maxRssKiB": 136880 + }, + { + "pid": 97280, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 140197888, + "heapTotal": 107937792, + "heapUsed": 37763912, + "external": 5727517, + "arrayBuffers": 2094841, + "maxRssKiB": 136912 + }, + { + "pid": 97280, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 140673024, + "heapTotal": 109248512, + "heapUsed": 38797456, + "external": 5735711, + "arrayBuffers": 2094841, + "maxRssKiB": 137376 + }, + { + "pid": 97279, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 125059072, + "heapTotal": 66519040, + "heapUsed": 29346784, + "external": 7774166, + "arrayBuffers": 2078450, + "maxRssKiB": 122256 + }, + { + "pid": 97279, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 125517824, + "heapTotal": 67305472, + "heapUsed": 30370432, + "external": 5720148, + "arrayBuffers": 2086642, + "maxRssKiB": 122576 + }, + { + "pid": 97279, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 126140416, + "heapTotal": 68354048, + "heapUsed": 31414224, + "external": 5720117, + "arrayBuffers": 2086642, + "maxRssKiB": 123184 + }, + { + "pid": 97279, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 129286144, + "heapTotal": 102957056, + "heapUsed": 32474920, + "external": 5728338, + "arrayBuffers": 2086642, + "maxRssKiB": 126256 + }, + { + "pid": 97279, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 129613824, + "heapTotal": 103743488, + "heapUsed": 33522992, + "external": 5719483, + "arrayBuffers": 2086642, + "maxRssKiB": 126576 + }, + { + "pid": 97279, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 130105344, + "heapTotal": 105054208, + "heapUsed": 34551264, + "external": 5727675, + "arrayBuffers": 2086642, + "maxRssKiB": 127056 + }, + { + "pid": 97279, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 130826240, + "heapTotal": 106102784, + "heapUsed": 35591384, + "external": 5719327, + "arrayBuffers": 2086642, + "maxRssKiB": 127760 + }, + { + "pid": 97279, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 131399680, + "heapTotal": 107151360, + "heapUsed": 36609944, + "external": 5727519, + "arrayBuffers": 2086642, + "maxRssKiB": 128320 + }, + { + "pid": 97279, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 132136960, + "heapTotal": 108199936, + "heapUsed": 37666096, + "external": 5719326, + "arrayBuffers": 2086642, + "maxRssKiB": 129040 + }, + { + "pid": 97279, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 132694016, + "heapTotal": 109248512, + "heapUsed": 38695728, + "external": 5727520, + "arrayBuffers": 2086642, + "maxRssKiB": 129584 + } + ] + }, + { + "id": "6-measured-2-cli", + "phase": "measured", + "mode": "cli", + "workers": 2, + "wallSeconds": 12.941111665999998, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "tests": 33, + "leftovers": 0, + "openHandles": 0, + "children": [], + "parentSamples": [ + { + "pid": 97389, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 115556352, + "heapTotal": 61227008, + "heapUsed": 24958392, + "external": 5723162, + "arrayBuffers": 2062059, + "maxRssKiB": 112976 + }, + { + "pid": 97390, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116097024, + "heapTotal": 60702720, + "heapUsed": 24861472, + "external": 5722809, + "arrayBuffers": 2062059, + "maxRssKiB": 113600 + }, + { + "pid": 97389, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 116490240, + "heapTotal": 62013440, + "heapUsed": 25989096, + "external": 5704362, + "arrayBuffers": 2070251, + "maxRssKiB": 113760 + }, + { + "pid": 97390, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116850688, + "heapTotal": 61227008, + "heapUsed": 25894104, + "external": 5704043, + "arrayBuffers": 2070251, + "maxRssKiB": 114112 + }, + { + "pid": 97389, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 117112832, + "heapTotal": 63062016, + "heapUsed": 27060024, + "external": 5704902, + "arrayBuffers": 2070251, + "maxRssKiB": 114368 + }, + { + "pid": 97390, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117358592, + "heapTotal": 62275584, + "heapUsed": 26955992, + "external": 5712204, + "arrayBuffers": 2070251, + "maxRssKiB": 114608 + }, + { + "pid": 97389, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 125468672, + "heapTotal": 66781184, + "heapUsed": 29326288, + "external": 7773861, + "arrayBuffers": 2078443, + "maxRssKiB": 122656 + }, + { + "pid": 97390, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 118112256, + "heapTotal": 63586304, + "heapUsed": 28035104, + "external": 5704041, + "arrayBuffers": 2070251, + "maxRssKiB": 115344 + }, + { + "pid": 97389, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 125730816, + "heapTotal": 67567616, + "heapUsed": 30365768, + "external": 5720463, + "arrayBuffers": 2086635, + "maxRssKiB": 122784 + }, + { + "pid": 97390, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 118882304, + "heapTotal": 64897024, + "heapUsed": 29093424, + "external": 5712362, + "arrayBuffers": 2070251, + "maxRssKiB": 116096 + }, + { + "pid": 97389, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 125976576, + "heapTotal": 68091904, + "heapUsed": 31409352, + "external": 5728624, + "arrayBuffers": 2086635, + "maxRssKiB": 123024 + }, + { + "pid": 97390, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 119537664, + "heapTotal": 66207744, + "heapUsed": 30123112, + "external": 5712391, + "arrayBuffers": 2070251, + "maxRssKiB": 116736 + }, + { + "pid": 97390, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 120291328, + "heapTotal": 67256320, + "heapUsed": 31147552, + "external": 5704014, + "arrayBuffers": 2070251, + "maxRssKiB": 117472 + }, + { + "pid": 97389, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 129024000, + "heapTotal": 102694912, + "heapUsed": 32471696, + "external": 5720461, + "arrayBuffers": 2086635, + "maxRssKiB": 126000 + }, + { + "pid": 97389, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 129499136, + "heapTotal": 103743488, + "heapUsed": 33517664, + "external": 5727565, + "arrayBuffers": 2086635, + "maxRssKiB": 126464 + }, + { + "pid": 97390, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 121012224, + "heapTotal": 68567040, + "heapUsed": 32170288, + "external": 5712235, + "arrayBuffers": 2070251, + "maxRssKiB": 118176 + }, + { + "pid": 97389, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 130203648, + "heapTotal": 104792064, + "heapUsed": 34564744, + "external": 5719373, + "arrayBuffers": 2086635, + "maxRssKiB": 127152 + }, + { + "pid": 97390, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121815040, + "heapTotal": 69615616, + "heapUsed": 33248760, + "external": 5702822, + "arrayBuffers": 2070251, + "maxRssKiB": 118960 + }, + { + "pid": 97389, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 130891776, + "heapTotal": 106102784, + "heapUsed": 35592168, + "external": 5727409, + "arrayBuffers": 2086635, + "maxRssKiB": 127824 + }, + { + "pid": 97390, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122535936, + "heapTotal": 70926336, + "heapUsed": 34278896, + "external": 5711014, + "arrayBuffers": 2070251, + "maxRssKiB": 119664 + }, + { + "pid": 97389, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 131497984, + "heapTotal": 107151360, + "heapUsed": 36613848, + "external": 5719217, + "arrayBuffers": 2086635, + "maxRssKiB": 128416 + }, + { + "pid": 97390, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 138985472, + "heapTotal": 104005632, + "heapUsed": 29549008, + "external": 7765641, + "arrayBuffers": 2078443, + "maxRssKiB": 135856 + }, + { + "pid": 97389, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 132169728, + "heapTotal": 108199936, + "heapUsed": 37665080, + "external": 5727408, + "arrayBuffers": 2086635, + "maxRssKiB": 129072 + }, + { + "pid": 97390, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 139280384, + "heapTotal": 100859904, + "heapUsed": 30538256, + "external": 5720437, + "arrayBuffers": 2086635, + "maxRssKiB": 136016 + }, + { + "pid": 97389, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 132907008, + "heapTotal": 109510656, + "heapUsed": 38695792, + "external": 5727408, + "arrayBuffers": 2086635, + "maxRssKiB": 129792 + }, + { + "pid": 97390, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 139542528, + "heapTotal": 101646336, + "heapUsed": 31571920, + "external": 5728598, + "arrayBuffers": 2086635, + "maxRssKiB": 136272 + }, + { + "pid": 97390, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 139624448, + "heapTotal": 102694912, + "heapUsed": 32617024, + "external": 5720435, + "arrayBuffers": 2086635, + "maxRssKiB": 136352 + }, + { + "pid": 97390, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 139640832, + "heapTotal": 104005632, + "heapUsed": 33661256, + "external": 5727557, + "arrayBuffers": 2086635, + "maxRssKiB": 136368 + }, + { + "pid": 97390, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 139788288, + "heapTotal": 105054208, + "heapUsed": 34699936, + "external": 5719364, + "arrayBuffers": 2086635, + "maxRssKiB": 136512 + }, + { + "pid": 97390, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 140197888, + "heapTotal": 106102784, + "heapUsed": 35719520, + "external": 5727401, + "arrayBuffers": 2086635, + "maxRssKiB": 136912 + }, + { + "pid": 97390, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 140935168, + "heapTotal": 107151360, + "heapUsed": 36747064, + "external": 5719209, + "arrayBuffers": 2086635, + "maxRssKiB": 137632 + }, + { + "pid": 97390, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 141705216, + "heapTotal": 108462080, + "heapUsed": 37786440, + "external": 5727400, + "arrayBuffers": 2086635, + "maxRssKiB": 138384 + }, + { + "pid": 97390, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 142442496, + "heapTotal": 109772800, + "heapUsed": 38816624, + "external": 5727400, + "arrayBuffers": 2086635, + "maxRssKiB": 139104 + } + ] + }, + { + "id": "7-measured-2-cli", + "phase": "measured", + "mode": "cli", + "workers": 2, + "wallSeconds": 12.859592665999998, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "tests": 33, + "leftovers": 0, + "openHandles": 0, + "children": [], + "parentSamples": [ + { + "pid": 97734, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116113408, + "heapTotal": 60702720, + "heapUsed": 24860536, + "external": 5722810, + "arrayBuffers": 2062059, + "maxRssKiB": 113648 + }, + { + "pid": 97733, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116441088, + "heapTotal": 60440576, + "heapUsed": 24864168, + "external": 5722849, + "arrayBuffers": 2062059, + "maxRssKiB": 113936 + }, + { + "pid": 97733, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 117260288, + "heapTotal": 61227008, + "heapUsed": 25894792, + "external": 5704079, + "arrayBuffers": 2070251, + "maxRssKiB": 114512 + }, + { + "pid": 97734, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116899840, + "heapTotal": 60964864, + "heapUsed": 25892064, + "external": 5704043, + "arrayBuffers": 2070251, + "maxRssKiB": 114160 + }, + { + "pid": 97733, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117784576, + "heapTotal": 62537728, + "heapUsed": 26958488, + "external": 5712240, + "arrayBuffers": 2070251, + "maxRssKiB": 115024 + }, + { + "pid": 97734, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117686272, + "heapTotal": 62537728, + "heapUsed": 26957032, + "external": 5712204, + "arrayBuffers": 2070251, + "maxRssKiB": 114928 + }, + { + "pid": 97734, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 118390784, + "heapTotal": 63324160, + "heapUsed": 28034000, + "external": 5704041, + "arrayBuffers": 2070251, + "maxRssKiB": 115616 + }, + { + "pid": 97733, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 118292480, + "heapTotal": 63586304, + "heapUsed": 28033664, + "external": 5704077, + "arrayBuffers": 2070251, + "maxRssKiB": 115520 + }, + { + "pid": 97733, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 118980608, + "heapTotal": 64372736, + "heapUsed": 29102704, + "external": 5711181, + "arrayBuffers": 2070251, + "maxRssKiB": 116192 + }, + { + "pid": 97734, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 119259136, + "heapTotal": 64897024, + "heapUsed": 29095216, + "external": 5712362, + "arrayBuffers": 2070251, + "maxRssKiB": 116464 + }, + { + "pid": 97733, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 119635968, + "heapTotal": 65683456, + "heapUsed": 30134768, + "external": 5702988, + "arrayBuffers": 2070251, + "maxRssKiB": 116832 + }, + { + "pid": 97734, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 120012800, + "heapTotal": 66207744, + "heapUsed": 30123336, + "external": 5712391, + "arrayBuffers": 2070251, + "maxRssKiB": 117200 + }, + { + "pid": 97733, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 120471552, + "heapTotal": 66994176, + "heapUsed": 31157560, + "external": 5711024, + "arrayBuffers": 2070251, + "maxRssKiB": 117648 + }, + { + "pid": 97734, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 120569856, + "heapTotal": 67256320, + "heapUsed": 31146592, + "external": 5704014, + "arrayBuffers": 2070251, + "maxRssKiB": 117744 + }, + { + "pid": 97733, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 121159680, + "heapTotal": 68042752, + "heapUsed": 32179728, + "external": 5702833, + "arrayBuffers": 2070251, + "maxRssKiB": 118320 + }, + { + "pid": 97734, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 121372672, + "heapTotal": 68304896, + "heapUsed": 32168736, + "external": 5712235, + "arrayBuffers": 2070251, + "maxRssKiB": 118528 + }, + { + "pid": 97733, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121946112, + "heapTotal": 69353472, + "heapUsed": 33244792, + "external": 5711024, + "arrayBuffers": 2070251, + "maxRssKiB": 119088 + }, + { + "pid": 97734, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 122060800, + "heapTotal": 69615616, + "heapUsed": 33248264, + "external": 5702822, + "arrayBuffers": 2070251, + "maxRssKiB": 119200 + }, + { + "pid": 97733, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122650624, + "heapTotal": 70402048, + "heapUsed": 34279384, + "external": 5711024, + "arrayBuffers": 2070251, + "maxRssKiB": 119776 + }, + { + "pid": 97734, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122732544, + "heapTotal": 70664192, + "heapUsed": 34279632, + "external": 5711014, + "arrayBuffers": 2070251, + "maxRssKiB": 119856 + }, + { + "pid": 97733, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 139198464, + "heapTotal": 104529920, + "heapUsed": 29548360, + "external": 7765642, + "arrayBuffers": 2078443, + "maxRssKiB": 136064 + }, + { + "pid": 97734, + "worker": "2", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 139395072, + "heapTotal": 104005632, + "heapUsed": 29669008, + "external": 7765984, + "arrayBuffers": 2078443, + "maxRssKiB": 136256 + }, + { + "pid": 97734, + "worker": "2", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 139624448, + "heapTotal": 101122048, + "heapUsed": 30676424, + "external": 5720746, + "arrayBuffers": 2086635, + "maxRssKiB": 136352 + }, + { + "pid": 97733, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 139673600, + "heapTotal": 100859904, + "heapUsed": 30536528, + "external": 5720437, + "arrayBuffers": 2086635, + "maxRssKiB": 136400 + }, + { + "pid": 97733, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 139804672, + "heapTotal": 101908480, + "heapUsed": 31572360, + "external": 5728598, + "arrayBuffers": 2086635, + "maxRssKiB": 136528 + }, + { + "pid": 97734, + "worker": "2", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 139722752, + "heapTotal": 101908480, + "heapUsed": 31703304, + "external": 5721286, + "arrayBuffers": 2086635, + "maxRssKiB": 136448 + }, + { + "pid": 97733, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 139886592, + "heapTotal": 103219200, + "heapUsed": 32612144, + "external": 5720435, + "arrayBuffers": 2086635, + "maxRssKiB": 136608 + }, + { + "pid": 97733, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 139935744, + "heapTotal": 104267776, + "heapUsed": 33659192, + "external": 5727557, + "arrayBuffers": 2086635, + "maxRssKiB": 136656 + }, + { + "pid": 97733, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 139968512, + "heapTotal": 105316352, + "heapUsed": 34698456, + "external": 5719365, + "arrayBuffers": 2086635, + "maxRssKiB": 136688 + }, + { + "pid": 97733, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 140492800, + "heapTotal": 106627072, + "heapUsed": 35721552, + "external": 5727401, + "arrayBuffers": 2086635, + "maxRssKiB": 137200 + }, + { + "pid": 97733, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 141017088, + "heapTotal": 107675648, + "heapUsed": 36740808, + "external": 5719209, + "arrayBuffers": 2086635, + "maxRssKiB": 137712 + }, + { + "pid": 97733, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 141721600, + "heapTotal": 108724224, + "heapUsed": 37786120, + "external": 5727399, + "arrayBuffers": 2086635, + "maxRssKiB": 138400 + }, + { + "pid": 97733, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 142475264, + "heapTotal": 109772800, + "heapUsed": 38816528, + "external": 5727400, + "arrayBuffers": 2086635, + "maxRssKiB": 139136 + } + ] + }, + { + "id": "8-measured-2-session", + "phase": "measured", + "mode": "session", + "workers": 2, + "wallSeconds": 3.506650125, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "tests": 33, + "leftovers": 0, + "openHandles": 0, + "children": [ + { + "pid": 97970, + "build": 1, + "rootDir": ".cache/integration/fixture-YwUPnu", + "cwd": ".cache/integration/session-1ptfYz", + "rss": 313098240, + "heapUsed": 71149648, + "maxRssKiB": 305888 + }, + { + "pid": 97971, + "build": 1, + "rootDir": ".cache/integration/fixture-JCTRe5", + "cwd": ".cache/integration/session-kMHOQa", + "rss": 342687744, + "heapUsed": 71198856, + "maxRssKiB": 334784 + }, + { + "pid": 97970, + "build": 2, + "rootDir": ".cache/integration/fixture-UHniEX", + "cwd": ".cache/integration/session-1ptfYz", + "rss": 325451776, + "heapUsed": 72839784, + "maxRssKiB": 317952 + }, + { + "pid": 97971, + "build": 2, + "rootDir": ".cache/integration/fixture-r1OqVU", + "cwd": ".cache/integration/session-kMHOQa", + "rss": 353927168, + "heapUsed": 72810400, + "maxRssKiB": 345760 + }, + { + "pid": 97971, + "build": 3, + "rootDir": ".cache/integration/fixture-8aPRzc", + "cwd": ".cache/integration/session-kMHOQa", + "rss": 363921408, + "heapUsed": 74441392, + "maxRssKiB": 355520 + }, + { + "pid": 97970, + "build": 3, + "rootDir": ".cache/integration/fixture-vysHyD", + "cwd": ".cache/integration/session-1ptfYz", + "rss": 336248832, + "heapUsed": 74474872, + "maxRssKiB": 328496 + }, + { + "pid": 97971, + "build": 4, + "rootDir": ".cache/integration/fixture-I9L9PG", + "cwd": ".cache/integration/session-kMHOQa", + "rss": 373571584, + "heapUsed": 75990584, + "maxRssKiB": 364944 + }, + { + "pid": 97970, + "build": 4, + "rootDir": ".cache/integration/fixture-lB2KOv", + "cwd": ".cache/integration/session-1ptfYz", + "rss": 345554944, + "heapUsed": 76025192, + "maxRssKiB": 337584 + }, + { + "pid": 97970, + "build": 5, + "rootDir": ".cache/integration/fixture-NKHBnP", + "cwd": ".cache/integration/session-1ptfYz", + "rss": 346963968, + "heapUsed": 76594672, + "maxRssKiB": 338960 + }, + { + "pid": 97971, + "build": 5, + "rootDir": ".cache/integration/fixture-YThxIf", + "cwd": ".cache/integration/session-kMHOQa", + "rss": 382550016, + "heapUsed": 77593896, + "maxRssKiB": 373712 + }, + { + "pid": 97970, + "build": 6, + "rootDir": ".cache/integration/fixture-fOSpNo", + "cwd": ".cache/integration/session-1ptfYz", + "rss": 348110848, + "heapUsed": 77131888, + "maxRssKiB": 340080 + }, + { + "pid": 97971, + "build": 6, + "rootDir": ".cache/integration/fixture-JVUFAh", + "cwd": ".cache/integration/session-kMHOQa", + "rss": 389726208, + "heapUsed": 79170776, + "maxRssKiB": 380720 + }, + { + "pid": 97970, + "build": 7, + "rootDir": ".cache/integration/fixture-4OQMAw", + "cwd": ".cache/integration/session-1ptfYz", + "rss": 349372416, + "heapUsed": 77591488, + "maxRssKiB": 341312 + }, + { + "pid": 97970, + "build": 8, + "rootDir": ".cache/integration/fixture-A63oKi", + "cwd": ".cache/integration/session-1ptfYz", + "rss": 356122624, + "heapUsed": 78124384, + "maxRssKiB": 347904 + }, + { + "pid": 97971, + "build": 7, + "rootDir": ".cache/integration/fixture-g6nHWW", + "cwd": ".cache/integration/session-kMHOQa", + "rss": 398934016, + "heapUsed": 80649896, + "maxRssKiB": 389712 + }, + { + "pid": 97970, + "build": 9, + "rootDir": ".cache/integration/fixture-nqlkEb", + "cwd": ".cache/integration/session-1ptfYz", + "rss": 357564416, + "heapUsed": 78826504, + "maxRssKiB": 349312 + }, + { + "pid": 97971, + "build": 8, + "rootDir": ".cache/integration/fixture-9QhupS", + "cwd": ".cache/integration/session-kMHOQa", + "rss": 407617536, + "heapUsed": 82228248, + "maxRssKiB": 398192 + }, + { + "pid": 97970, + "build": 10, + "rootDir": ".cache/integration/fixture-9zFHwZ", + "cwd": ".cache/integration/session-1ptfYz", + "rss": 358891520, + "heapUsed": 79428880, + "maxRssKiB": 350608 + }, + { + "pid": 97971, + "build": 9, + "rootDir": ".cache/integration/fixture-lJGL6k", + "cwd": ".cache/integration/session-kMHOQa", + "rss": 409026560, + "heapUsed": 82936472, + "maxRssKiB": 399568 + }, + { + "pid": 97971, + "build": 10, + "rootDir": ".cache/integration/fixture-F4KNtQ", + "cwd": ".cache/integration/session-kMHOQa", + "rss": 410238976, + "heapUsed": 83538832, + "maxRssKiB": 400752 + }, + { + "pid": 97974, + "build": 1, + "rootDir": ".cache/integration/fixture-zozeUv", + "cwd": ".cache/integration/session-LdxoM8", + "rss": 312852480, + "heapUsed": 71148600, + "maxRssKiB": 305648 + }, + { + "pid": 97974, + "build": 2, + "rootDir": ".cache/integration/fixture-DfoKnA", + "cwd": ".cache/integration/session-LdxoM8", + "rss": 325795840, + "heapUsed": 72843440, + "maxRssKiB": 318288 + }, + { + "pid": 97974, + "build": 3, + "rootDir": ".cache/integration/fixture-MIVOOz", + "cwd": ".cache/integration/session-LdxoM8", + "rss": 337428480, + "heapUsed": 74472792, + "maxRssKiB": 329648 + }, + { + "pid": 97974, + "build": 4, + "rootDir": ".cache/integration/fixture-4dyGvw", + "cwd": ".cache/integration/session-LdxoM8", + "rss": 347799552, + "heapUsed": 76026192, + "maxRssKiB": 339776 + }, + { + "pid": 97974, + "build": 5, + "rootDir": ".cache/integration/fixture-6e4iEO", + "cwd": ".cache/integration/session-LdxoM8", + "rss": 349290496, + "heapUsed": 76593312, + "maxRssKiB": 341232 + }, + { + "pid": 97974, + "build": 6, + "rootDir": ".cache/integration/fixture-8lDm1m", + "cwd": ".cache/integration/session-LdxoM8", + "rss": 350273536, + "heapUsed": 77131456, + "maxRssKiB": 342192 + }, + { + "pid": 97974, + "build": 7, + "rootDir": ".cache/integration/fixture-nIan0d", + "cwd": ".cache/integration/session-LdxoM8", + "rss": 351535104, + "heapUsed": 77580560, + "maxRssKiB": 343424 + }, + { + "pid": 97974, + "build": 8, + "rootDir": ".cache/integration/fixture-pllKdv", + "cwd": ".cache/integration/session-LdxoM8", + "rss": 358268928, + "heapUsed": 78123272, + "maxRssKiB": 350000 + }, + { + "pid": 97974, + "build": 9, + "rootDir": ".cache/integration/fixture-oHLRQ5", + "cwd": ".cache/integration/session-LdxoM8", + "rss": 359677952, + "heapUsed": 78822560, + "maxRssKiB": 351376 + }, + { + "pid": 97974, + "build": 10, + "rootDir": ".cache/integration/fixture-qeYu1x", + "cwd": ".cache/integration/session-LdxoM8", + "rss": 361086976, + "heapUsed": 79427080, + "maxRssKiB": 352752 + } + ], + "parentSamples": [ + { + "pid": 97969, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115818496, + "heapTotal": 60440576, + "heapUsed": 24852768, + "external": 5723154, + "arrayBuffers": 2062066, + "maxRssKiB": 113344 + }, + { + "pid": 97968, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115834880, + "heapTotal": 60702720, + "heapUsed": 24851896, + "external": 5723124, + "arrayBuffers": 2062066, + "maxRssKiB": 113344 + }, + { + "pid": 97969, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116441088, + "heapTotal": 61227008, + "heapUsed": 25881344, + "external": 5703764, + "arrayBuffers": 2070258, + "maxRssKiB": 113712 + }, + { + "pid": 97968, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116375552, + "heapTotal": 61227008, + "heapUsed": 25879520, + "external": 5703737, + "arrayBuffers": 2070258, + "maxRssKiB": 113648 + }, + { + "pid": 97968, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 116948992, + "heapTotal": 62275584, + "heapUsed": 26938728, + "external": 5703706, + "arrayBuffers": 2070258, + "maxRssKiB": 114208 + }, + { + "pid": 97969, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117063680, + "heapTotal": 62537728, + "heapUsed": 26940760, + "external": 5703733, + "arrayBuffers": 2070258, + "maxRssKiB": 114320 + }, + { + "pid": 97968, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117702656, + "heapTotal": 63586304, + "heapUsed": 28001216, + "external": 5711927, + "arrayBuffers": 2070258, + "maxRssKiB": 114944 + }, + { + "pid": 97969, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117768192, + "heapTotal": 63848448, + "heapUsed": 28001216, + "external": 5711954, + "arrayBuffers": 2070258, + "maxRssKiB": 115008 + }, + { + "pid": 97969, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 118374400, + "heapTotal": 64634880, + "heapUsed": 29057232, + "external": 5703099, + "arrayBuffers": 2070258, + "maxRssKiB": 115600 + }, + { + "pid": 97968, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 118390784, + "heapTotal": 64634880, + "heapUsed": 29048864, + "external": 5703864, + "arrayBuffers": 2070258, + "maxRssKiB": 115616 + }, + { + "pid": 97969, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 119062528, + "heapTotal": 65945600, + "heapUsed": 30087176, + "external": 5711291, + "arrayBuffers": 2070258, + "maxRssKiB": 116272 + }, + { + "pid": 97969, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 119701504, + "heapTotal": 67256320, + "heapUsed": 31110584, + "external": 5702943, + "arrayBuffers": 2070258, + "maxRssKiB": 116896 + }, + { + "pid": 97968, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 119128064, + "heapTotal": 65945600, + "heapUsed": 30078592, + "external": 5712085, + "arrayBuffers": 2070258, + "maxRssKiB": 116336 + }, + { + "pid": 97969, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 120340480, + "heapTotal": 68042752, + "heapUsed": 32132160, + "external": 5711135, + "arrayBuffers": 2070258, + "maxRssKiB": 117520 + }, + { + "pid": 97968, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 119898112, + "heapTotal": 66994176, + "heapUsed": 31102984, + "external": 5711900, + "arrayBuffers": 2070258, + "maxRssKiB": 117088 + }, + { + "pid": 97969, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121012224, + "heapTotal": 69353472, + "heapUsed": 33187664, + "external": 5702942, + "arrayBuffers": 2070258, + "maxRssKiB": 118176 + }, + { + "pid": 97968, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 120487936, + "heapTotal": 68042752, + "heapUsed": 32124832, + "external": 5703737, + "arrayBuffers": 2070258, + "maxRssKiB": 117664 + }, + { + "pid": 97969, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 121716736, + "heapTotal": 70139904, + "heapUsed": 34221576, + "external": 5711136, + "arrayBuffers": 2070258, + "maxRssKiB": 118864 + }, + { + "pid": 97968, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121257984, + "heapTotal": 69353472, + "heapUsed": 33193072, + "external": 5702932, + "arrayBuffers": 2070258, + "maxRssKiB": 118416 + }, + { + "pid": 97968, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 121847808, + "heapTotal": 70664192, + "heapUsed": 34224936, + "external": 5711126, + "arrayBuffers": 2070258, + "maxRssKiB": 118992 + }, + { + "pid": 97969, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 138608640, + "heapTotal": 104529920, + "heapUsed": 29537776, + "external": 7765960, + "arrayBuffers": 2078457, + "maxRssKiB": 135488 + }, + { + "pid": 97968, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 139788288, + "heapTotal": 106889216, + "heapUsed": 29679264, + "external": 7774188, + "arrayBuffers": 2086642, + "maxRssKiB": 136640 + }, + { + "pid": 97969, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 138870784, + "heapTotal": 100859904, + "heapUsed": 30526816, + "external": 5720135, + "arrayBuffers": 2086649, + "maxRssKiB": 135616 + }, + { + "pid": 97969, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 139067392, + "heapTotal": 101908480, + "heapUsed": 31563208, + "external": 5720104, + "arrayBuffers": 2086649, + "maxRssKiB": 135808 + }, + { + "pid": 97969, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 139116544, + "heapTotal": 102957056, + "heapUsed": 32598776, + "external": 5728325, + "arrayBuffers": 2086649, + "maxRssKiB": 135856 + }, + { + "pid": 97969, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 139116544, + "heapTotal": 104005632, + "heapUsed": 33639928, + "external": 5719482, + "arrayBuffers": 2086649, + "maxRssKiB": 135856 + }, + { + "pid": 97969, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 139149312, + "heapTotal": 105054208, + "heapUsed": 34677176, + "external": 5727674, + "arrayBuffers": 2086649, + "maxRssKiB": 135888 + }, + { + "pid": 97969, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 139706368, + "heapTotal": 106364928, + "heapUsed": 35703008, + "external": 5719326, + "arrayBuffers": 2086649, + "maxRssKiB": 136432 + }, + { + "pid": 97969, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 140296192, + "heapTotal": 107151360, + "heapUsed": 36723200, + "external": 5727518, + "arrayBuffers": 2086649, + "maxRssKiB": 137008 + }, + { + "pid": 97969, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 140853248, + "heapTotal": 108462080, + "heapUsed": 37764896, + "external": 5719325, + "arrayBuffers": 2086649, + "maxRssKiB": 137552 + }, + { + "pid": 97969, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 141459456, + "heapTotal": 109510656, + "heapUsed": 38791272, + "external": 5727519, + "arrayBuffers": 2086649, + "maxRssKiB": 138144 + }, + { + "pid": 97968, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 140460032, + "heapTotal": 100859904, + "heapUsed": 30663928, + "external": 5728945, + "arrayBuffers": 2094834, + "maxRssKiB": 137168 + }, + { + "pid": 97968, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 140722176, + "heapTotal": 101908480, + "heapUsed": 31712272, + "external": 5729485, + "arrayBuffers": 2094834, + "maxRssKiB": 137424 + } + ] + }, + { + "id": "9-warmup-8-cli", + "phase": "warmup", + "mode": "cli", + "workers": 8, + "wallSeconds": 6.832721541999999, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "tests": 33, + "leftovers": 0, + "openHandles": 0, + "children": [], + "parentSamples": [ + { + "pid": 98078, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 115884032, + "heapTotal": 61751296, + "heapUsed": 24977848, + "external": 5723160, + "arrayBuffers": 2062059, + "maxRssKiB": 113296 + }, + { + "pid": 98080, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115736576, + "heapTotal": 60702720, + "heapUsed": 24860704, + "external": 5722846, + "arrayBuffers": 2062059, + "maxRssKiB": 113152 + }, + { + "pid": 98081, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115572736, + "heapTotal": 60702720, + "heapUsed": 24859560, + "external": 5722818, + "arrayBuffers": 2062059, + "maxRssKiB": 113088 + }, + { + "pid": 98079, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115441664, + "heapTotal": 60440576, + "heapUsed": 24859248, + "external": 5722807, + "arrayBuffers": 2062059, + "maxRssKiB": 112960 + }, + { + "pid": 98078, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 116670464, + "heapTotal": 62013440, + "heapUsed": 26007512, + "external": 5704362, + "arrayBuffers": 2070251, + "maxRssKiB": 113936 + }, + { + "pid": 98081, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116473856, + "heapTotal": 61227008, + "heapUsed": 25891360, + "external": 5704053, + "arrayBuffers": 2070251, + "maxRssKiB": 113744 + }, + { + "pid": 98079, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116080640, + "heapTotal": 61227008, + "heapUsed": 25892648, + "external": 5704043, + "arrayBuffers": 2070251, + "maxRssKiB": 113360 + }, + { + "pid": 98080, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116359168, + "heapTotal": 61227008, + "heapUsed": 25894040, + "external": 5704079, + "arrayBuffers": 2082055, + "maxRssKiB": 113632 + }, + { + "pid": 98078, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 117047296, + "heapTotal": 62799872, + "heapUsed": 27082048, + "external": 5704902, + "arrayBuffers": 2070251, + "maxRssKiB": 114304 + }, + { + "pid": 98079, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 116441088, + "heapTotal": 62537728, + "heapUsed": 26956168, + "external": 5712204, + "arrayBuffers": 2070251, + "maxRssKiB": 113712 + }, + { + "pid": 98080, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117129216, + "heapTotal": 62275584, + "heapUsed": 26956536, + "external": 5712240, + "arrayBuffers": 2070251, + "maxRssKiB": 114384 + }, + { + "pid": 98081, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117096448, + "heapTotal": 62275584, + "heapUsed": 26957744, + "external": 5712214, + "arrayBuffers": 2070251, + "maxRssKiB": 114352 + }, + { + "pid": 98079, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117161984, + "heapTotal": 63586304, + "heapUsed": 28031536, + "external": 5704041, + "arrayBuffers": 2070251, + "maxRssKiB": 114416 + }, + { + "pid": 98081, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117817344, + "heapTotal": 63586304, + "heapUsed": 28035272, + "external": 5704051, + "arrayBuffers": 2070251, + "maxRssKiB": 115056 + }, + { + "pid": 98080, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117751808, + "heapTotal": 63324160, + "heapUsed": 28033592, + "external": 5704077, + "arrayBuffers": 2070251, + "maxRssKiB": 114992 + }, + { + "pid": 98080, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 118521856, + "heapTotal": 64634880, + "heapUsed": 29104488, + "external": 5711181, + "arrayBuffers": 2070251, + "maxRssKiB": 115744 + }, + { + "pid": 98081, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 118587392, + "heapTotal": 64897024, + "heapUsed": 29103064, + "external": 5711173, + "arrayBuffers": 2070251, + "maxRssKiB": 115808 + }, + { + "pid": 98079, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 117964800, + "heapTotal": 65159168, + "heapUsed": 29089952, + "external": 5712362, + "arrayBuffers": 2070251, + "maxRssKiB": 115200 + }, + { + "pid": 98081, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 119177216, + "heapTotal": 65945600, + "heapUsed": 30132200, + "external": 5702981, + "arrayBuffers": 2070251, + "maxRssKiB": 116400 + }, + { + "pid": 98080, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 119226368, + "heapTotal": 65945600, + "heapUsed": 30133640, + "external": 5702989, + "arrayBuffers": 2070251, + "maxRssKiB": 116432 + }, + { + "pid": 98079, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 118472704, + "heapTotal": 66207744, + "heapUsed": 30121432, + "external": 5712391, + "arrayBuffers": 2070251, + "maxRssKiB": 115696 + }, + { + "pid": 98081, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 119816192, + "heapTotal": 66732032, + "heapUsed": 31156352, + "external": 5711017, + "arrayBuffers": 2070251, + "maxRssKiB": 117008 + }, + { + "pid": 98080, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 119947264, + "heapTotal": 66994176, + "heapUsed": 31157392, + "external": 5711025, + "arrayBuffers": 2070251, + "maxRssKiB": 117152 + }, + { + "pid": 98079, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 119193600, + "heapTotal": 67256320, + "heapUsed": 31145312, + "external": 5704014, + "arrayBuffers": 2070251, + "maxRssKiB": 116400 + }, + { + "pid": 98081, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 120487936, + "heapTotal": 68042752, + "heapUsed": 32179688, + "external": 5702825, + "arrayBuffers": 2070251, + "maxRssKiB": 117664 + }, + { + "pid": 98080, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 120766464, + "heapTotal": 68304896, + "heapUsed": 32177768, + "external": 5702833, + "arrayBuffers": 2070251, + "maxRssKiB": 117936 + }, + { + "pid": 98079, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 119996416, + "heapTotal": 68304896, + "heapUsed": 32170128, + "external": 5712235, + "arrayBuffers": 2070251, + "maxRssKiB": 117184 + }, + { + "pid": 98081, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121323520, + "heapTotal": 69353472, + "heapUsed": 33243184, + "external": 5711016, + "arrayBuffers": 2070251, + "maxRssKiB": 118480 + }, + { + "pid": 98080, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121487360, + "heapTotal": 69615616, + "heapUsed": 33243984, + "external": 5711024, + "arrayBuffers": 2070251, + "maxRssKiB": 118640 + }, + { + "pid": 98079, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 120750080, + "heapTotal": 69615616, + "heapUsed": 33247800, + "external": 5702822, + "arrayBuffers": 2070251, + "maxRssKiB": 117920 + }, + { + "pid": 98081, + "worker": "4", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122028032, + "heapTotal": 70402048, + "heapUsed": 34277680, + "external": 5711016, + "arrayBuffers": 2070251, + "maxRssKiB": 119168 + }, + { + "pid": 98080, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122093568, + "heapTotal": 70664192, + "heapUsed": 34276928, + "external": 5711024, + "arrayBuffers": 2070251, + "maxRssKiB": 119232 + }, + { + "pid": 98079, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 121503744, + "heapTotal": 70926336, + "heapUsed": 34277616, + "external": 5711014, + "arrayBuffers": 2070251, + "maxRssKiB": 118656 + } + ] + }, + { + "id": "10-warmup-8-session", + "phase": "warmup", + "mode": "session", + "workers": 8, + "wallSeconds": 2.545903166999997, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "tests": 33, + "leftovers": 0, + "openHandles": 0, + "children": [ + { + "pid": 98251, + "build": 1, + "rootDir": ".cache/integration/fixture-9ebwYg", + "cwd": ".cache/integration/session-lKWiWQ", + "rss": 342638592, + "heapUsed": 71203504, + "maxRssKiB": 334736 + }, + { + "pid": 98249, + "build": 1, + "rootDir": ".cache/integration/fixture-FW02WI", + "cwd": ".cache/integration/session-5B72nd", + "rss": 342310912, + "heapUsed": 71207032, + "maxRssKiB": 334416 + }, + { + "pid": 98250, + "build": 1, + "rootDir": ".cache/integration/fixture-HDpoYJ", + "cwd": ".cache/integration/session-KVwWk7", + "rss": 342261760, + "heapUsed": 71205000, + "maxRssKiB": 334368 + }, + { + "pid": 98251, + "build": 2, + "rootDir": ".cache/integration/fixture-UKL2vA", + "cwd": ".cache/integration/session-lKWiWQ", + "rss": 355860480, + "heapUsed": 72815144, + "maxRssKiB": 347648 + }, + { + "pid": 98249, + "build": 2, + "rootDir": ".cache/integration/fixture-9ZkJET", + "cwd": ".cache/integration/session-5B72nd", + "rss": 354893824, + "heapUsed": 72815096, + "maxRssKiB": 346704 + }, + { + "pid": 98250, + "build": 2, + "rootDir": ".cache/integration/fixture-ClPzte", + "cwd": ".cache/integration/session-KVwWk7", + "rss": 354713600, + "heapUsed": 72813824, + "maxRssKiB": 346528 + }, + { + "pid": 98251, + "build": 3, + "rootDir": ".cache/integration/fixture-YVx4KR", + "cwd": ".cache/integration/session-lKWiWQ", + "rss": 366526464, + "heapUsed": 74453800, + "maxRssKiB": 358064 + }, + { + "pid": 98249, + "build": 3, + "rootDir": ".cache/integration/fixture-bNITr0", + "cwd": ".cache/integration/session-5B72nd", + "rss": 365101056, + "heapUsed": 74456600, + "maxRssKiB": 356672 + }, + { + "pid": 98250, + "build": 3, + "rootDir": ".cache/integration/fixture-8pG5VK", + "cwd": ".cache/integration/session-KVwWk7", + "rss": 365707264, + "heapUsed": 74451920, + "maxRssKiB": 357264 + }, + { + "pid": 98249, + "build": 4, + "rootDir": ".cache/integration/fixture-KcMucS", + "cwd": ".cache/integration/session-5B72nd", + "rss": 374669312, + "heapUsed": 76006296, + "maxRssKiB": 366016 + }, + { + "pid": 98251, + "build": 4, + "rootDir": ".cache/integration/fixture-u1VjET", + "cwd": ".cache/integration/session-lKWiWQ", + "rss": 374652928, + "heapUsed": 76002120, + "maxRssKiB": 366000 + }, + { + "pid": 98250, + "build": 4, + "rootDir": ".cache/integration/fixture-5FighA", + "cwd": ".cache/integration/session-KVwWk7", + "rss": 375668736, + "heapUsed": 75998400, + "maxRssKiB": 366992 + }, + { + "pid": 98249, + "build": 5, + "rootDir": ".cache/integration/fixture-6FStM4", + "cwd": ".cache/integration/session-5B72nd", + "rss": 375422976, + "heapUsed": 76568632, + "maxRssKiB": 366752 + }, + { + "pid": 98250, + "build": 5, + "rootDir": ".cache/integration/fixture-Wc3MAe", + "cwd": ".cache/integration/session-KVwWk7", + "rss": 376455168, + "heapUsed": 76563888, + "maxRssKiB": 367760 + }, + { + "pid": 98251, + "build": 5, + "rootDir": ".cache/integration/fixture-fYou8p", + "cwd": ".cache/integration/session-lKWiWQ", + "rss": 383778816, + "heapUsed": 77601872, + "maxRssKiB": 374912 + }, + { + "pid": 98249, + "build": 6, + "rootDir": ".cache/integration/fixture-1QOOkv", + "cwd": ".cache/integration/session-5B72nd", + "rss": 376078336, + "heapUsed": 77105992, + "maxRssKiB": 367392 + }, + { + "pid": 98250, + "build": 6, + "rootDir": ".cache/integration/fixture-nPHmOw", + "cwd": ".cache/integration/session-KVwWk7", + "rss": 377110528, + "heapUsed": 77105152, + "maxRssKiB": 368400 + }, + { + "pid": 98249, + "build": 7, + "rootDir": ".cache/integration/fixture-7nUUHz", + "cwd": ".cache/integration/session-5B72nd", + "rss": 377028608, + "heapUsed": 77567064, + "maxRssKiB": 368320 + }, + { + "pid": 98251, + "build": 6, + "rootDir": ".cache/integration/fixture-B4zzLw", + "cwd": ".cache/integration/session-lKWiWQ", + "rss": 392740864, + "heapUsed": 79176464, + "maxRssKiB": 383664 + }, + { + "pid": 98250, + "build": 7, + "rootDir": ".cache/integration/fixture-xXCkN0", + "cwd": ".cache/integration/session-KVwWk7", + "rss": 377847808, + "heapUsed": 77562256, + "maxRssKiB": 369120 + }, + { + "pid": 98249, + "build": 8, + "rootDir": ".cache/integration/fixture-zD5g8j", + "cwd": ".cache/integration/session-5B72nd", + "rss": 377749504, + "heapUsed": 78099080, + "maxRssKiB": 369024 + }, + { + "pid": 98250, + "build": 8, + "rootDir": ".cache/integration/fixture-wUVn9A", + "cwd": ".cache/integration/session-KVwWk7", + "rss": 378372096, + "heapUsed": 78092344, + "maxRssKiB": 369632 + }, + { + "pid": 98251, + "build": 7, + "rootDir": ".cache/integration/fixture-F9SpbT", + "cwd": ".cache/integration/session-lKWiWQ", + "rss": 399409152, + "heapUsed": 80666328, + "maxRssKiB": 390176 + }, + { + "pid": 98249, + "build": 9, + "rootDir": ".cache/integration/fixture-nuHDDE", + "cwd": ".cache/integration/session-5B72nd", + "rss": 378519552, + "heapUsed": 78799096, + "maxRssKiB": 369776 + }, + { + "pid": 98250, + "build": 9, + "rootDir": ".cache/integration/fixture-H65LVO", + "cwd": ".cache/integration/session-KVwWk7", + "rss": 379109376, + "heapUsed": 78792176, + "maxRssKiB": 370352 + }, + { + "pid": 98249, + "build": 10, + "rootDir": ".cache/integration/fixture-xKuNSs", + "cwd": ".cache/integration/session-5B72nd", + "rss": 379273216, + "heapUsed": 79402064, + "maxRssKiB": 370512 + }, + { + "pid": 98251, + "build": 8, + "rootDir": ".cache/integration/fixture-U3S1ek", + "cwd": ".cache/integration/session-lKWiWQ", + "rss": 407797760, + "heapUsed": 82242368, + "maxRssKiB": 398368 + }, + { + "pid": 98250, + "build": 10, + "rootDir": ".cache/integration/fixture-IwDYhk", + "cwd": ".cache/integration/session-KVwWk7", + "rss": 379879424, + "heapUsed": 79397576, + "maxRssKiB": 371104 + }, + { + "pid": 98251, + "build": 9, + "rootDir": ".cache/integration/fixture-WTZyy4", + "cwd": ".cache/integration/session-lKWiWQ", + "rss": 409370624, + "heapUsed": 82941264, + "maxRssKiB": 399904 + }, + { + "pid": 98251, + "build": 10, + "rootDir": ".cache/integration/fixture-OOSpsO", + "cwd": ".cache/integration/session-lKWiWQ", + "rss": 410583040, + "heapUsed": 83545096, + "maxRssKiB": 401088 + } + ], + "parentSamples": [ + { + "pid": 98244, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 115572736, + "heapTotal": 60964864, + "heapUsed": 24978160, + "external": 5723166, + "arrayBuffers": 2101182, + "maxRssKiB": 112992 + }, + { + "pid": 98243, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115490816, + "heapTotal": 60964864, + "heapUsed": 24853152, + "external": 5723130, + "arrayBuffers": 2062066, + "maxRssKiB": 113008 + }, + { + "pid": 98242, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116080640, + "heapTotal": 60964864, + "heapUsed": 24853008, + "external": 5723152, + "arrayBuffers": 2062066, + "maxRssKiB": 113584 + }, + { + "pid": 98241, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116965376, + "heapTotal": 60702720, + "heapUsed": 24856264, + "external": 5723122, + "arrayBuffers": 2062066, + "maxRssKiB": 114400 + }, + { + "pid": 98241, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 117719040, + "heapTotal": 61489152, + "heapUsed": 25881360, + "external": 5703737, + "arrayBuffers": 2070258, + "maxRssKiB": 114960 + }, + { + "pid": 98242, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116834304, + "heapTotal": 61489152, + "heapUsed": 25881176, + "external": 5703764, + "arrayBuffers": 2070258, + "maxRssKiB": 114096 + }, + { + "pid": 98243, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 115965952, + "heapTotal": 61489152, + "heapUsed": 25881600, + "external": 5703744, + "arrayBuffers": 2070258, + "maxRssKiB": 113248 + }, + { + "pid": 98241, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 118243328, + "heapTotal": 62275584, + "heapUsed": 26938712, + "external": 5703706, + "arrayBuffers": 2070258, + "maxRssKiB": 115472 + }, + { + "pid": 98242, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117424128, + "heapTotal": 62799872, + "heapUsed": 26938384, + "external": 5703733, + "arrayBuffers": 2070258, + "maxRssKiB": 114672 + }, + { + "pid": 98243, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 116457472, + "heapTotal": 62799872, + "heapUsed": 26941952, + "external": 5703713, + "arrayBuffers": 2070258, + "maxRssKiB": 113728 + }, + { + "pid": 98242, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 118095872, + "heapTotal": 63848448, + "heapUsed": 28001048, + "external": 5711954, + "arrayBuffers": 2070258, + "maxRssKiB": 115328 + }, + { + "pid": 98241, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 118947840, + "heapTotal": 63586304, + "heapUsed": 28003840, + "external": 5711927, + "arrayBuffers": 2070258, + "maxRssKiB": 116160 + }, + { + "pid": 98243, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117161984, + "heapTotal": 63848448, + "heapUsed": 28001448, + "external": 5711934, + "arrayBuffers": 2070258, + "maxRssKiB": 114416 + }, + { + "pid": 98242, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 118800384, + "heapTotal": 65159168, + "heapUsed": 29059176, + "external": 5703099, + "arrayBuffers": 2070258, + "maxRssKiB": 116016 + }, + { + "pid": 98243, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 117833728, + "heapTotal": 64897024, + "heapUsed": 29058936, + "external": 5703091, + "arrayBuffers": 2070258, + "maxRssKiB": 115072 + }, + { + "pid": 98241, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 119717888, + "heapTotal": 64634880, + "heapUsed": 29048752, + "external": 5703864, + "arrayBuffers": 2070258, + "maxRssKiB": 116912 + }, + { + "pid": 98242, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 119406592, + "heapTotal": 66207744, + "heapUsed": 30088608, + "external": 5711291, + "arrayBuffers": 2070258, + "maxRssKiB": 116608 + }, + { + "pid": 98243, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 118489088, + "heapTotal": 66207744, + "heapUsed": 30088480, + "external": 5711283, + "arrayBuffers": 2089259, + "maxRssKiB": 115712 + }, + { + "pid": 98242, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 120127488, + "heapTotal": 67256320, + "heapUsed": 31110592, + "external": 5702943, + "arrayBuffers": 2070258, + "maxRssKiB": 117312 + }, + { + "pid": 98241, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 120340480, + "heapTotal": 65945600, + "heapUsed": 30079648, + "external": 5712085, + "arrayBuffers": 2070258, + "maxRssKiB": 117520 + }, + { + "pid": 98243, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 119078912, + "heapTotal": 67256320, + "heapUsed": 31109392, + "external": 5702935, + "arrayBuffers": 2070258, + "maxRssKiB": 116288 + }, + { + "pid": 98242, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 120848384, + "heapTotal": 68567040, + "heapUsed": 32133608, + "external": 5711135, + "arrayBuffers": 2070258, + "maxRssKiB": 118016 + }, + { + "pid": 98243, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 119750656, + "heapTotal": 68304896, + "heapUsed": 32132216, + "external": 5711127, + "arrayBuffers": 2070258, + "maxRssKiB": 116944 + }, + { + "pid": 98241, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 120979456, + "heapTotal": 66994176, + "heapUsed": 31101768, + "external": 5711900, + "arrayBuffers": 2070258, + "maxRssKiB": 118144 + }, + { + "pid": 98242, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121602048, + "heapTotal": 69615616, + "heapUsed": 33189064, + "external": 5702942, + "arrayBuffers": 2070258, + "maxRssKiB": 118752 + }, + { + "pid": 98243, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 120504320, + "heapTotal": 69615616, + "heapUsed": 33187720, + "external": 5702934, + "arrayBuffers": 2070258, + "maxRssKiB": 117680 + }, + { + "pid": 98242, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122306560, + "heapTotal": 70926336, + "heapUsed": 34221936, + "external": 5711136, + "arrayBuffers": 2070258, + "maxRssKiB": 119440 + }, + { + "pid": 98241, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 121749504, + "heapTotal": 68042752, + "heapUsed": 32125456, + "external": 5703737, + "arrayBuffers": 2070258, + "maxRssKiB": 118896 + }, + { + "pid": 98243, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 121290752, + "heapTotal": 70664192, + "heapUsed": 34221200, + "external": 5711128, + "arrayBuffers": 2070258, + "maxRssKiB": 118448 + }, + { + "pid": 98241, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 122290176, + "heapTotal": 69091328, + "heapUsed": 33194000, + "external": 5702932, + "arrayBuffers": 2070258, + "maxRssKiB": 119424 + }, + { + "pid": 98244, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 116342784, + "heapTotal": 62013440, + "heapUsed": 26008400, + "external": 5704362, + "arrayBuffers": 2070251, + "maxRssKiB": 113616 + }, + { + "pid": 98241, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 123027456, + "heapTotal": 70402048, + "heapUsed": 34226232, + "external": 5711126, + "arrayBuffers": 2089102, + "maxRssKiB": 120144 + }, + { + "pid": 98244, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 116883456, + "heapTotal": 62799872, + "heapUsed": 27080000, + "external": 5704902, + "arrayBuffers": 2070251, + "maxRssKiB": 114144 + } + ] + }, + { + "id": "11-measured-8-cli", + "phase": "measured", + "mode": "cli", + "workers": 8, + "wallSeconds": 6.86661320800001, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "tests": 33, + "leftovers": 0, + "openHandles": 0, + "children": [], + "parentSamples": [ + { + "pid": 98360, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 117161984, + "heapTotal": 61227008, + "heapUsed": 24981912, + "external": 5723163, + "arrayBuffers": 2062059, + "maxRssKiB": 114640 + }, + { + "pid": 98362, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116031488, + "heapTotal": 60440576, + "heapUsed": 24860584, + "external": 5722821, + "arrayBuffers": 2062059, + "maxRssKiB": 113552 + }, + { + "pid": 98363, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115605504, + "heapTotal": 60440576, + "heapUsed": 24860448, + "external": 5722849, + "arrayBuffers": 2062059, + "maxRssKiB": 113024 + }, + { + "pid": 98361, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 117047296, + "heapTotal": 59916288, + "heapUsed": 24862360, + "external": 5722810, + "arrayBuffers": 2062059, + "maxRssKiB": 114512 + }, + { + "pid": 98360, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 117702656, + "heapTotal": 61751296, + "heapUsed": 26011024, + "external": 5704362, + "arrayBuffers": 2070251, + "maxRssKiB": 114944 + }, + { + "pid": 98363, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116228096, + "heapTotal": 61227008, + "heapUsed": 25892080, + "external": 5704079, + "arrayBuffers": 2070251, + "maxRssKiB": 113504 + }, + { + "pid": 98361, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 117702656, + "heapTotal": 60964864, + "heapUsed": 25893760, + "external": 5704043, + "arrayBuffers": 2070251, + "maxRssKiB": 114944 + }, + { + "pid": 98362, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116768768, + "heapTotal": 61227008, + "heapUsed": 25894016, + "external": 5704053, + "arrayBuffers": 2070251, + "maxRssKiB": 114032 + }, + { + "pid": 98360, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 118112256, + "heapTotal": 62537728, + "heapUsed": 27079896, + "external": 5704902, + "arrayBuffers": 2070251, + "maxRssKiB": 115344 + }, + { + "pid": 98363, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 116752384, + "heapTotal": 62537728, + "heapUsed": 26957760, + "external": 5712240, + "arrayBuffers": 2070251, + "maxRssKiB": 114016 + }, + { + "pid": 98361, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 118095872, + "heapTotal": 62013440, + "heapUsed": 26956288, + "external": 5712204, + "arrayBuffers": 2070251, + "maxRssKiB": 115328 + }, + { + "pid": 98362, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117473280, + "heapTotal": 62275584, + "heapUsed": 26956064, + "external": 5712214, + "arrayBuffers": 2070251, + "maxRssKiB": 114720 + }, + { + "pid": 98362, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117964800, + "heapTotal": 63324160, + "heapUsed": 28032920, + "external": 5704051, + "arrayBuffers": 2070251, + "maxRssKiB": 115200 + }, + { + "pid": 98363, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117260288, + "heapTotal": 63586304, + "heapUsed": 28032568, + "external": 5704077, + "arrayBuffers": 2070251, + "maxRssKiB": 114512 + }, + { + "pid": 98361, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 118800384, + "heapTotal": 63324160, + "heapUsed": 28033144, + "external": 5704041, + "arrayBuffers": 2070251, + "maxRssKiB": 116016 + }, + { + "pid": 98363, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 117948416, + "heapTotal": 64897024, + "heapUsed": 29101816, + "external": 5711181, + "arrayBuffers": 2070251, + "maxRssKiB": 115184 + }, + { + "pid": 98362, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 118718464, + "heapTotal": 64634880, + "heapUsed": 29103976, + "external": 5711173, + "arrayBuffers": 2070251, + "maxRssKiB": 115936 + }, + { + "pid": 98361, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 119570432, + "heapTotal": 64634880, + "heapUsed": 29093208, + "external": 5712362, + "arrayBuffers": 2070251, + "maxRssKiB": 116768 + }, + { + "pid": 98363, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 118603776, + "heapTotal": 65683456, + "heapUsed": 30130792, + "external": 5702989, + "arrayBuffers": 2070251, + "maxRssKiB": 115824 + }, + { + "pid": 98362, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 119357440, + "heapTotal": 65945600, + "heapUsed": 30132872, + "external": 5702981, + "arrayBuffers": 2070251, + "maxRssKiB": 116560 + }, + { + "pid": 98361, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 120274944, + "heapTotal": 65945600, + "heapUsed": 30123408, + "external": 5712391, + "arrayBuffers": 2070251, + "maxRssKiB": 117456 + }, + { + "pid": 98362, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 120176640, + "heapTotal": 66994176, + "heapUsed": 31157920, + "external": 5711017, + "arrayBuffers": 2070251, + "maxRssKiB": 117360 + }, + { + "pid": 98363, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 119390208, + "heapTotal": 66732032, + "heapUsed": 31156848, + "external": 5711025, + "arrayBuffers": 2070251, + "maxRssKiB": 116592 + }, + { + "pid": 98361, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 121012224, + "heapTotal": 66994176, + "heapUsed": 31147032, + "external": 5704014, + "arrayBuffers": 2070251, + "maxRssKiB": 118176 + }, + { + "pid": 98362, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 120733696, + "heapTotal": 68042752, + "heapUsed": 32179736, + "external": 5702825, + "arrayBuffers": 2070251, + "maxRssKiB": 117904 + }, + { + "pid": 98363, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 120111104, + "heapTotal": 68042752, + "heapUsed": 32179080, + "external": 5702833, + "arrayBuffers": 2070251, + "maxRssKiB": 117296 + }, + { + "pid": 98361, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 121716736, + "heapTotal": 68304896, + "heapUsed": 32170520, + "external": 5712235, + "arrayBuffers": 2070251, + "maxRssKiB": 118864 + }, + { + "pid": 98363, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 120864768, + "heapTotal": 69091328, + "heapUsed": 33243936, + "external": 5711024, + "arrayBuffers": 2070251, + "maxRssKiB": 118032 + }, + { + "pid": 98362, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121487360, + "heapTotal": 69353472, + "heapUsed": 33244768, + "external": 5711016, + "arrayBuffers": 2070251, + "maxRssKiB": 118640 + }, + { + "pid": 98361, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 122535936, + "heapTotal": 69353472, + "heapUsed": 33248992, + "external": 5702822, + "arrayBuffers": 2070251, + "maxRssKiB": 119664 + }, + { + "pid": 98363, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 121569280, + "heapTotal": 70402048, + "heapUsed": 34279040, + "external": 5711024, + "arrayBuffers": 2070251, + "maxRssKiB": 118720 + }, + { + "pid": 98362, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122208256, + "heapTotal": 70664192, + "heapUsed": 34278288, + "external": 5711016, + "arrayBuffers": 2070251, + "maxRssKiB": 119344 + }, + { + "pid": 98361, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 123224064, + "heapTotal": 70664192, + "heapUsed": 34279128, + "external": 5711014, + "arrayBuffers": 2070251, + "maxRssKiB": 120336 + } + ] + }, + { + "id": "12-measured-8-session", + "phase": "measured", + "mode": "session", + "workers": 8, + "wallSeconds": 2.3549656250000117, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "tests": 33, + "leftovers": 0, + "openHandles": 0, + "children": [ + { + "pid": 98536, + "build": 1, + "rootDir": ".cache/integration/fixture-8i0Bxp", + "cwd": ".cache/integration/session-19bcmp", + "rss": 315310080, + "heapUsed": 71150280, + "maxRssKiB": 308048 + }, + { + "pid": 98537, + "build": 1, + "rootDir": ".cache/integration/fixture-NrAPo9", + "cwd": ".cache/integration/session-Qmag3W", + "rss": 341082112, + "heapUsed": 71201136, + "maxRssKiB": 333216 + }, + { + "pid": 98538, + "build": 1, + "rootDir": ".cache/integration/fixture-M9cJtZ", + "cwd": ".cache/integration/session-kNMPGt", + "rss": 309329920, + "heapUsed": 71146056, + "maxRssKiB": 302208 + }, + { + "pid": 98538, + "build": 2, + "rootDir": ".cache/integration/fixture-X009X3", + "cwd": ".cache/integration/session-kNMPGt", + "rss": 322224128, + "heapUsed": 72834496, + "maxRssKiB": 314800 + }, + { + "pid": 98536, + "build": 2, + "rootDir": ".cache/integration/fixture-g9M1vD", + "cwd": ".cache/integration/session-19bcmp", + "rss": 328220672, + "heapUsed": 72841920, + "maxRssKiB": 320656 + }, + { + "pid": 98537, + "build": 2, + "rootDir": ".cache/integration/fixture-Ma93Mn", + "cwd": ".cache/integration/session-Qmag3W", + "rss": 354598912, + "heapUsed": 72808160, + "maxRssKiB": 346416 + }, + { + "pid": 98537, + "build": 3, + "rootDir": ".cache/integration/fixture-CT9Tyh", + "cwd": ".cache/integration/session-Qmag3W", + "rss": 363790336, + "heapUsed": 74451856, + "maxRssKiB": 355392 + }, + { + "pid": 98538, + "build": 3, + "rootDir": ".cache/integration/fixture-RCVRKR", + "cwd": ".cache/integration/session-kNMPGt", + "rss": 333889536, + "heapUsed": 74466888, + "maxRssKiB": 326192 + }, + { + "pid": 98536, + "build": 3, + "rootDir": ".cache/integration/fixture-r6JvSX", + "cwd": ".cache/integration/session-19bcmp", + "rss": 340295680, + "heapUsed": 74476000, + "maxRssKiB": 332448 + }, + { + "pid": 98536, + "build": 4, + "rootDir": ".cache/integration/fixture-R9YTqH", + "cwd": ".cache/integration/session-19bcmp", + "rss": 349569024, + "heapUsed": 76026136, + "maxRssKiB": 341504 + }, + { + "pid": 98538, + "build": 4, + "rootDir": ".cache/integration/fixture-Jo9bEm", + "cwd": ".cache/integration/session-kNMPGt", + "rss": 344506368, + "heapUsed": 76020496, + "maxRssKiB": 336560 + }, + { + "pid": 98537, + "build": 4, + "rootDir": ".cache/integration/fixture-QyE4ag", + "cwd": ".cache/integration/session-Qmag3W", + "rss": 372244480, + "heapUsed": 76002200, + "maxRssKiB": 363648 + }, + { + "pid": 98536, + "build": 5, + "rootDir": ".cache/integration/fixture-DnKXky", + "cwd": ".cache/integration/session-19bcmp", + "rss": 350961664, + "heapUsed": 76594232, + "maxRssKiB": 342864 + }, + { + "pid": 98538, + "build": 5, + "rootDir": ".cache/integration/fixture-yISub7", + "cwd": ".cache/integration/session-kNMPGt", + "rss": 345899008, + "heapUsed": 76588304, + "maxRssKiB": 337920 + }, + { + "pid": 98537, + "build": 5, + "rootDir": ".cache/integration/fixture-Za58tJ", + "cwd": ".cache/integration/session-Qmag3W", + "rss": 382255104, + "heapUsed": 77600544, + "maxRssKiB": 373424 + }, + { + "pid": 98538, + "build": 6, + "rootDir": ".cache/integration/fixture-C7QomN", + "cwd": ".cache/integration/session-kNMPGt", + "rss": 347144192, + "heapUsed": 77125584, + "maxRssKiB": 339136 + }, + { + "pid": 98536, + "build": 6, + "rootDir": ".cache/integration/fixture-pkfQmi", + "cwd": ".cache/integration/session-19bcmp", + "rss": 352043008, + "heapUsed": 77131528, + "maxRssKiB": 343920 + }, + { + "pid": 98537, + "build": 6, + "rootDir": ".cache/integration/fixture-98vSUF", + "cwd": ".cache/integration/session-Qmag3W", + "rss": 389480448, + "heapUsed": 79174760, + "maxRssKiB": 380480 + }, + { + "pid": 98536, + "build": 7, + "rootDir": ".cache/integration/fixture-v4gbLX", + "cwd": ".cache/integration/session-19bcmp", + "rss": 353222656, + "heapUsed": 77588472, + "maxRssKiB": 345072 + }, + { + "pid": 98538, + "build": 7, + "rootDir": ".cache/integration/fixture-RfwnmA", + "cwd": ".cache/integration/session-kNMPGt", + "rss": 348356608, + "heapUsed": 77574656, + "maxRssKiB": 340320 + }, + { + "pid": 98536, + "build": 8, + "rootDir": ".cache/integration/fixture-gnvBNu", + "cwd": ".cache/integration/session-19bcmp", + "rss": 360022016, + "heapUsed": 78123616, + "maxRssKiB": 351712 + }, + { + "pid": 98538, + "build": 8, + "rootDir": ".cache/integration/fixture-NIcMDT", + "cwd": ".cache/integration/session-kNMPGt", + "rss": 355139584, + "heapUsed": 78115896, + "maxRssKiB": 346944 + }, + { + "pid": 98537, + "build": 7, + "rootDir": ".cache/integration/fixture-TThHEf", + "cwd": ".cache/integration/session-Qmag3W", + "rss": 399687680, + "heapUsed": 80664640, + "maxRssKiB": 390448 + }, + { + "pid": 98538, + "build": 9, + "rootDir": ".cache/integration/fixture-B3Z495", + "cwd": ".cache/integration/session-kNMPGt", + "rss": 356548608, + "heapUsed": 78816048, + "maxRssKiB": 348320 + }, + { + "pid": 98536, + "build": 9, + "rootDir": ".cache/integration/fixture-1mk5Cq", + "cwd": ".cache/integration/session-19bcmp", + "rss": 361463808, + "heapUsed": 78822504, + "maxRssKiB": 353120 + }, + { + "pid": 98537, + "build": 8, + "rootDir": ".cache/integration/fixture-vZmIAz", + "cwd": ".cache/integration/session-Qmag3W", + "rss": 408633344, + "heapUsed": 82243064, + "maxRssKiB": 399184 + }, + { + "pid": 98538, + "build": 10, + "rootDir": ".cache/integration/fixture-CapMOC", + "cwd": ".cache/integration/session-kNMPGt", + "rss": 357941248, + "heapUsed": 79422120, + "maxRssKiB": 349680 + }, + { + "pid": 98536, + "build": 10, + "rootDir": ".cache/integration/fixture-QFwP1g", + "cwd": ".cache/integration/session-19bcmp", + "rss": 362807296, + "heapUsed": 79429120, + "maxRssKiB": 354432 + }, + { + "pid": 98537, + "build": 9, + "rootDir": ".cache/integration/fixture-jbjzeX", + "cwd": ".cache/integration/session-Qmag3W", + "rss": 410025984, + "heapUsed": 82951752, + "maxRssKiB": 400544 + }, + { + "pid": 98537, + "build": 10, + "rootDir": ".cache/integration/fixture-E3kp1e", + "cwd": ".cache/integration/session-Qmag3W", + "rss": 411353088, + "heapUsed": 83543336, + "maxRssKiB": 401840 + } + ], + "parentSamples": [ + { + "pid": 98531, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 115621888, + "heapTotal": 60964864, + "heapUsed": 24958928, + "external": 5723168, + "arrayBuffers": 2062059, + "maxRssKiB": 113040 + }, + { + "pid": 98529, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 117014528, + "heapTotal": 60440576, + "heapUsed": 24856152, + "external": 5723132, + "arrayBuffers": 2062066, + "maxRssKiB": 114464 + }, + { + "pid": 98528, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116015104, + "heapTotal": 60964864, + "heapUsed": 24852728, + "external": 5723124, + "arrayBuffers": 2062066, + "maxRssKiB": 113504 + }, + { + "pid": 98530, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115818496, + "heapTotal": 60702720, + "heapUsed": 24852360, + "external": 5723154, + "arrayBuffers": 2062066, + "maxRssKiB": 113312 + }, + { + "pid": 98529, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 117669888, + "heapTotal": 61227008, + "heapUsed": 25879528, + "external": 5703744, + "arrayBuffers": 2070258, + "maxRssKiB": 114912 + }, + { + "pid": 98528, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116801536, + "heapTotal": 61751296, + "heapUsed": 25880784, + "external": 5703737, + "arrayBuffers": 2070258, + "maxRssKiB": 114064 + }, + { + "pid": 98530, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116244480, + "heapTotal": 61227008, + "heapUsed": 25880216, + "external": 5703764, + "arrayBuffers": 2081740, + "maxRssKiB": 113520 + }, + { + "pid": 98529, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 118259712, + "heapTotal": 62275584, + "heapUsed": 26938776, + "external": 5703713, + "arrayBuffers": 2070258, + "maxRssKiB": 115488 + }, + { + "pid": 98528, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117456896, + "heapTotal": 63324160, + "heapUsed": 26941480, + "external": 5703706, + "arrayBuffers": 2070258, + "maxRssKiB": 114704 + }, + { + "pid": 98530, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 116867072, + "heapTotal": 62275584, + "heapUsed": 26938576, + "external": 5703733, + "arrayBuffers": 2070258, + "maxRssKiB": 114128 + }, + { + "pid": 98529, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 119029760, + "heapTotal": 63586304, + "heapUsed": 28001304, + "external": 5711934, + "arrayBuffers": 2070258, + "maxRssKiB": 116240 + }, + { + "pid": 98530, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117473280, + "heapTotal": 63324160, + "heapUsed": 28002408, + "external": 5711954, + "arrayBuffers": 2070258, + "maxRssKiB": 114720 + }, + { + "pid": 98528, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117981184, + "heapTotal": 63848448, + "heapUsed": 28001744, + "external": 5711927, + "arrayBuffers": 2070258, + "maxRssKiB": 115216 + }, + { + "pid": 98530, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 118226944, + "heapTotal": 64634880, + "heapUsed": 29060168, + "external": 5703099, + "arrayBuffers": 2070258, + "maxRssKiB": 115456 + }, + { + "pid": 98529, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 119652352, + "heapTotal": 64634880, + "heapUsed": 29058792, + "external": 5703091, + "arrayBuffers": 2070258, + "maxRssKiB": 116848 + }, + { + "pid": 98528, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 118636544, + "heapTotal": 65159168, + "heapUsed": 29050896, + "external": 5703864, + "arrayBuffers": 2070258, + "maxRssKiB": 115856 + }, + { + "pid": 98529, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 120258560, + "heapTotal": 65421312, + "heapUsed": 30088752, + "external": 5711283, + "arrayBuffers": 2070258, + "maxRssKiB": 117440 + }, + { + "pid": 98530, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 118931456, + "heapTotal": 65945600, + "heapUsed": 30088160, + "external": 5711291, + "arrayBuffers": 2070258, + "maxRssKiB": 116144 + }, + { + "pid": 98528, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 119373824, + "heapTotal": 66207744, + "heapUsed": 30079040, + "external": 5712085, + "arrayBuffers": 2070258, + "maxRssKiB": 116576 + }, + { + "pid": 98530, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 119570432, + "heapTotal": 66994176, + "heapUsed": 31110688, + "external": 5702943, + "arrayBuffers": 2070258, + "maxRssKiB": 116768 + }, + { + "pid": 98529, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 120930304, + "heapTotal": 66732032, + "heapUsed": 31112368, + "external": 5702935, + "arrayBuffers": 2070258, + "maxRssKiB": 118096 + }, + { + "pid": 98530, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 120373248, + "heapTotal": 68042752, + "heapUsed": 32134056, + "external": 5711135, + "arrayBuffers": 2070258, + "maxRssKiB": 117552 + }, + { + "pid": 98529, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 121569280, + "heapTotal": 67780608, + "heapUsed": 32132456, + "external": 5711127, + "arrayBuffers": 2070258, + "maxRssKiB": 118720 + }, + { + "pid": 98528, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 120160256, + "heapTotal": 67518464, + "heapUsed": 31103000, + "external": 5711900, + "arrayBuffers": 2070258, + "maxRssKiB": 117344 + }, + { + "pid": 98530, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 120946688, + "heapTotal": 69091328, + "heapUsed": 33189960, + "external": 5702942, + "arrayBuffers": 2075118, + "maxRssKiB": 118112 + }, + { + "pid": 98529, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 122306560, + "heapTotal": 69091328, + "heapUsed": 33189304, + "external": 5702934, + "arrayBuffers": 2080910, + "maxRssKiB": 119440 + }, + { + "pid": 98528, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 120766464, + "heapTotal": 68567040, + "heapUsed": 32125040, + "external": 5703737, + "arrayBuffers": 2070258, + "maxRssKiB": 117936 + }, + { + "pid": 98530, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 121651200, + "heapTotal": 70402048, + "heapUsed": 34221072, + "external": 5711136, + "arrayBuffers": 2070258, + "maxRssKiB": 118800 + }, + { + "pid": 98529, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 123027456, + "heapTotal": 70139904, + "heapUsed": 34221856, + "external": 5711128, + "arrayBuffers": 2070258, + "maxRssKiB": 120144 + }, + { + "pid": 98528, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121454592, + "heapTotal": 69615616, + "heapUsed": 33193104, + "external": 5702932, + "arrayBuffers": 2070258, + "maxRssKiB": 118608 + }, + { + "pid": 98528, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122126336, + "heapTotal": 70926336, + "heapUsed": 34224920, + "external": 5711126, + "arrayBuffers": 2070258, + "maxRssKiB": 119264 + }, + { + "pid": 98531, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 116326400, + "heapTotal": 61489152, + "heapUsed": 25989088, + "external": 5704362, + "arrayBuffers": 2070251, + "maxRssKiB": 113600 + }, + { + "pid": 98531, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 116899840, + "heapTotal": 62799872, + "heapUsed": 27062456, + "external": 5704902, + "arrayBuffers": 2070251, + "maxRssKiB": 114160 + } + ] + }, + { + "id": "13-measured-8-session", + "phase": "measured", + "mode": "session", + "workers": 8, + "wallSeconds": 2.3692148329999907, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "tests": 33, + "leftovers": 0, + "openHandles": 0, + "children": [ + { + "pid": 98592, + "build": 1, + "rootDir": ".cache/integration/fixture-eb9Joj", + "cwd": ".cache/integration/session-cW6NQU", + "rss": 341000192, + "heapUsed": 71202368, + "maxRssKiB": 333136 + }, + { + "pid": 98594, + "build": 1, + "rootDir": ".cache/integration/fixture-Qhu0kq", + "cwd": ".cache/integration/session-jP19hT", + "rss": 344358912, + "heapUsed": 71202080, + "maxRssKiB": 336416 + }, + { + "pid": 98593, + "build": 1, + "rootDir": ".cache/integration/fixture-w8GJlf", + "cwd": ".cache/integration/session-jq1sxt", + "rss": 344850432, + "heapUsed": 71203320, + "maxRssKiB": 336896 + }, + { + "pid": 98592, + "build": 2, + "rootDir": ".cache/integration/fixture-kFaG9c", + "cwd": ".cache/integration/session-cW6NQU", + "rss": 352911360, + "heapUsed": 72812248, + "maxRssKiB": 344768 + }, + { + "pid": 98594, + "build": 2, + "rootDir": ".cache/integration/fixture-ZRpSQK", + "cwd": ".cache/integration/session-jP19hT", + "rss": 356139008, + "heapUsed": 72813408, + "maxRssKiB": 347920 + }, + { + "pid": 98593, + "build": 2, + "rootDir": ".cache/integration/fixture-tw5XPc", + "cwd": ".cache/integration/session-jq1sxt", + "rss": 357400576, + "heapUsed": 72815592, + "maxRssKiB": 349152 + }, + { + "pid": 98593, + "build": 3, + "rootDir": ".cache/integration/fixture-KeLud8", + "cwd": ".cache/integration/session-jq1sxt", + "rss": 368082944, + "heapUsed": 74454072, + "maxRssKiB": 359584 + }, + { + "pid": 98594, + "build": 3, + "rootDir": ".cache/integration/fixture-DSb7lQ", + "cwd": ".cache/integration/session-jP19hT", + "rss": 366264320, + "heapUsed": 74448560, + "maxRssKiB": 357808 + }, + { + "pid": 98592, + "build": 3, + "rootDir": ".cache/integration/fixture-14OHZt", + "cwd": ".cache/integration/session-cW6NQU", + "rss": 361938944, + "heapUsed": 74450992, + "maxRssKiB": 353584 + }, + { + "pid": 98592, + "build": 4, + "rootDir": ".cache/integration/fixture-wAGxml", + "cwd": ".cache/integration/session-cW6NQU", + "rss": 371507200, + "heapUsed": 76000456, + "maxRssKiB": 362928 + }, + { + "pid": 98593, + "build": 4, + "rootDir": ".cache/integration/fixture-kEJqUW", + "cwd": ".cache/integration/session-jq1sxt", + "rss": 376635392, + "heapUsed": 76003304, + "maxRssKiB": 367936 + }, + { + "pid": 98594, + "build": 4, + "rootDir": ".cache/integration/fixture-EDnQdg", + "cwd": ".cache/integration/session-jP19hT", + "rss": 373964800, + "heapUsed": 76000800, + "maxRssKiB": 365328 + }, + { + "pid": 98592, + "build": 5, + "rootDir": ".cache/integration/fixture-Kz6Cs0", + "cwd": ".cache/integration/session-cW6NQU", + "rss": 372342784, + "heapUsed": 76563136, + "maxRssKiB": 363744 + }, + { + "pid": 98594, + "build": 5, + "rootDir": ".cache/integration/fixture-4uc3Id", + "cwd": ".cache/integration/session-jP19hT", + "rss": 374767616, + "heapUsed": 76562896, + "maxRssKiB": 366112 + }, + { + "pid": 98593, + "build": 5, + "rootDir": ".cache/integration/fixture-ktPuEe", + "cwd": ".cache/integration/session-jq1sxt", + "rss": 384827392, + "heapUsed": 77602808, + "maxRssKiB": 375936 + }, + { + "pid": 98592, + "build": 6, + "rootDir": ".cache/integration/fixture-nzMEc3", + "cwd": ".cache/integration/session-cW6NQU", + "rss": 373112832, + "heapUsed": 77102232, + "maxRssKiB": 364496 + }, + { + "pid": 98594, + "build": 6, + "rootDir": ".cache/integration/fixture-q0MWhB", + "cwd": ".cache/integration/session-jP19hT", + "rss": 375341056, + "heapUsed": 77098720, + "maxRssKiB": 366672 + }, + { + "pid": 98593, + "build": 6, + "rootDir": ".cache/integration/fixture-eTJ4S8", + "cwd": ".cache/integration/session-jq1sxt", + "rss": 392445952, + "heapUsed": 79177992, + "maxRssKiB": 383376 + }, + { + "pid": 98594, + "build": 7, + "rootDir": ".cache/integration/fixture-FZWvCW", + "cwd": ".cache/integration/session-jP19hT", + "rss": 376045568, + "heapUsed": 77551344, + "maxRssKiB": 367360 + }, + { + "pid": 98592, + "build": 7, + "rootDir": ".cache/integration/fixture-Bro9UR", + "cwd": ".cache/integration/session-cW6NQU", + "rss": 373915648, + "heapUsed": 77560768, + "maxRssKiB": 365280 + }, + { + "pid": 98594, + "build": 8, + "rootDir": ".cache/integration/fixture-QVKPEg", + "cwd": ".cache/integration/session-jP19hT", + "rss": 376668160, + "heapUsed": 78089184, + "maxRssKiB": 367968 + }, + { + "pid": 98592, + "build": 8, + "rootDir": ".cache/integration/fixture-P7yyjb", + "cwd": ".cache/integration/session-cW6NQU", + "rss": 374554624, + "heapUsed": 78092072, + "maxRssKiB": 365904 + }, + { + "pid": 98593, + "build": 7, + "rootDir": ".cache/integration/fixture-8L41iG", + "cwd": ".cache/integration/session-jq1sxt", + "rss": 400588800, + "heapUsed": 80667728, + "maxRssKiB": 391328 + }, + { + "pid": 98594, + "build": 9, + "rootDir": ".cache/integration/fixture-3V3Pz9", + "cwd": ".cache/integration/session-jP19hT", + "rss": 377274368, + "heapUsed": 78789576, + "maxRssKiB": 368560 + }, + { + "pid": 98592, + "build": 9, + "rootDir": ".cache/integration/fixture-TvCMjJ", + "cwd": ".cache/integration/session-cW6NQU", + "rss": 375357440, + "heapUsed": 78791344, + "maxRssKiB": 366688 + }, + { + "pid": 98593, + "build": 8, + "rootDir": ".cache/integration/fixture-kHFCHu", + "cwd": ".cache/integration/session-jq1sxt", + "rss": 409780224, + "heapUsed": 82246192, + "maxRssKiB": 400304 + }, + { + "pid": 98594, + "build": 10, + "rootDir": ".cache/integration/fixture-Oh4Q4j", + "cwd": ".cache/integration/session-jP19hT", + "rss": 378011648, + "heapUsed": 79395728, + "maxRssKiB": 369280 + }, + { + "pid": 98592, + "build": 10, + "rootDir": ".cache/integration/fixture-SOKtM2", + "cwd": ".cache/integration/session-cW6NQU", + "rss": 376209408, + "heapUsed": 79396512, + "maxRssKiB": 367520 + }, + { + "pid": 98593, + "build": 9, + "rootDir": ".cache/integration/fixture-WrATOi", + "cwd": ".cache/integration/session-jq1sxt", + "rss": 411271168, + "heapUsed": 82956024, + "maxRssKiB": 401760 + }, + { + "pid": 98593, + "build": 10, + "rootDir": ".cache/integration/fixture-AW2ZtN", + "cwd": ".cache/integration/session-jq1sxt", + "rss": 412499968, + "heapUsed": 83557800, + "maxRssKiB": 402960 + } + ], + "parentSamples": [ + { + "pid": 98582, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 115343360, + "heapTotal": 61227008, + "heapUsed": 24957072, + "external": 5723169, + "arrayBuffers": 2062059, + "maxRssKiB": 112768 + }, + { + "pid": 98585, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115539968, + "heapTotal": 60964864, + "heapUsed": 24852056, + "external": 5723155, + "arrayBuffers": 2062066, + "maxRssKiB": 112960 + }, + { + "pid": 98584, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115621888, + "heapTotal": 60964864, + "heapUsed": 24852696, + "external": 5723133, + "arrayBuffers": 2062066, + "maxRssKiB": 113040 + }, + { + "pid": 98583, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115572736, + "heapTotal": 60702720, + "heapUsed": 24851896, + "external": 5723125, + "arrayBuffers": 2062066, + "maxRssKiB": 113088 + }, + { + "pid": 98585, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116457472, + "heapTotal": 61489152, + "heapUsed": 25882880, + "external": 5703764, + "arrayBuffers": 2070258, + "maxRssKiB": 113728 + }, + { + "pid": 98583, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116146176, + "heapTotal": 61227008, + "heapUsed": 25880992, + "external": 5703737, + "arrayBuffers": 2070258, + "maxRssKiB": 113424 + }, + { + "pid": 98584, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116441088, + "heapTotal": 61489152, + "heapUsed": 25879016, + "external": 5703744, + "arrayBuffers": 2070258, + "maxRssKiB": 113712 + }, + { + "pid": 98584, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 116965376, + "heapTotal": 62275584, + "heapUsed": 26938496, + "external": 5703713, + "arrayBuffers": 2070258, + "maxRssKiB": 114240 + }, + { + "pid": 98585, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117276672, + "heapTotal": 63062016, + "heapUsed": 26938536, + "external": 5703733, + "arrayBuffers": 2070258, + "maxRssKiB": 114528 + }, + { + "pid": 98583, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 116621312, + "heapTotal": 62537728, + "heapUsed": 26938616, + "external": 5703706, + "arrayBuffers": 2070258, + "maxRssKiB": 113888 + }, + { + "pid": 98583, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117276672, + "heapTotal": 63586304, + "heapUsed": 28000752, + "external": 5711927, + "arrayBuffers": 2070258, + "maxRssKiB": 114528 + }, + { + "pid": 98585, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117915648, + "heapTotal": 63848448, + "heapUsed": 28001200, + "external": 5711954, + "arrayBuffers": 2070258, + "maxRssKiB": 115152 + }, + { + "pid": 98584, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117620736, + "heapTotal": 63586304, + "heapUsed": 28002200, + "external": 5711934, + "arrayBuffers": 2070258, + "maxRssKiB": 114864 + }, + { + "pid": 98585, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 118669312, + "heapTotal": 64897024, + "heapUsed": 29057392, + "external": 5703099, + "arrayBuffers": 2070258, + "maxRssKiB": 115888 + }, + { + "pid": 98584, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 118292480, + "heapTotal": 64897024, + "heapUsed": 29057032, + "external": 5703091, + "arrayBuffers": 2070258, + "maxRssKiB": 115520 + }, + { + "pid": 98583, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 118112256, + "heapTotal": 64634880, + "heapUsed": 29051024, + "external": 5703864, + "arrayBuffers": 2070258, + "maxRssKiB": 115344 + }, + { + "pid": 98585, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 119324672, + "heapTotal": 66207744, + "heapUsed": 30089080, + "external": 5711291, + "arrayBuffers": 2070258, + "maxRssKiB": 116528 + }, + { + "pid": 98584, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 118980608, + "heapTotal": 65945600, + "heapUsed": 30086800, + "external": 5711283, + "arrayBuffers": 2089259, + "maxRssKiB": 116192 + }, + { + "pid": 98583, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 118784000, + "heapTotal": 65945600, + "heapUsed": 30079408, + "external": 5712085, + "arrayBuffers": 2070258, + "maxRssKiB": 116000 + }, + { + "pid": 98584, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 119734272, + "heapTotal": 66732032, + "heapUsed": 31109136, + "external": 5702935, + "arrayBuffers": 2070258, + "maxRssKiB": 116928 + }, + { + "pid": 98585, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 120094720, + "heapTotal": 67256320, + "heapUsed": 31110744, + "external": 5702943, + "arrayBuffers": 2070258, + "maxRssKiB": 117280 + }, + { + "pid": 98584, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 120455168, + "heapTotal": 67780608, + "heapUsed": 32133496, + "external": 5711127, + "arrayBuffers": 2070258, + "maxRssKiB": 117632 + }, + { + "pid": 98585, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 120864768, + "heapTotal": 68567040, + "heapUsed": 32134432, + "external": 5711135, + "arrayBuffers": 2070258, + "maxRssKiB": 118032 + }, + { + "pid": 98583, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 119537664, + "heapTotal": 67256320, + "heapUsed": 31102040, + "external": 5711900, + "arrayBuffers": 2070258, + "maxRssKiB": 116736 + }, + { + "pid": 98584, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121208832, + "heapTotal": 69091328, + "heapUsed": 33189464, + "external": 5702934, + "arrayBuffers": 2070258, + "maxRssKiB": 118368 + }, + { + "pid": 98585, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121683968, + "heapTotal": 69877760, + "heapUsed": 33188992, + "external": 5702942, + "arrayBuffers": 2070258, + "maxRssKiB": 118832 + }, + { + "pid": 98583, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 120160256, + "heapTotal": 68304896, + "heapUsed": 32124848, + "external": 5703737, + "arrayBuffers": 2070258, + "maxRssKiB": 117360 + }, + { + "pid": 98584, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 121946112, + "heapTotal": 70402048, + "heapUsed": 34221040, + "external": 5711128, + "arrayBuffers": 2070258, + "maxRssKiB": 119088 + }, + { + "pid": 98585, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122355712, + "heapTotal": 70926336, + "heapUsed": 34221128, + "external": 5711136, + "arrayBuffers": 2070258, + "maxRssKiB": 119488 + }, + { + "pid": 98583, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 120963072, + "heapTotal": 69353472, + "heapUsed": 33194064, + "external": 5702932, + "arrayBuffers": 2070258, + "maxRssKiB": 118128 + }, + { + "pid": 98582, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 115998720, + "heapTotal": 61751296, + "heapUsed": 26013536, + "external": 5704362, + "arrayBuffers": 2070251, + "maxRssKiB": 113280 + }, + { + "pid": 98583, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 121733120, + "heapTotal": 70664192, + "heapUsed": 34224856, + "external": 5711126, + "arrayBuffers": 2070258, + "maxRssKiB": 118880 + }, + { + "pid": 98582, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 116654080, + "heapTotal": 63062016, + "heapUsed": 27080008, + "external": 5704902, + "arrayBuffers": 2070251, + "maxRssKiB": 113920 + } + ] + }, + { + "id": "14-measured-8-cli", + "phase": "measured", + "mode": "cli", + "workers": 8, + "wallSeconds": 7.139863834000003, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "tests": 33, + "leftovers": 0, + "openHandles": 0, + "children": [], + "parentSamples": [ + { + "pid": 98699, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 116752384, + "heapTotal": 60440576, + "heapUsed": 24960784, + "external": 5723165, + "arrayBuffers": 2062059, + "maxRssKiB": 114288 + }, + { + "pid": 98701, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 117342208, + "heapTotal": 60178432, + "heapUsed": 24862648, + "external": 5722823, + "arrayBuffers": 2062059, + "maxRssKiB": 114832 + }, + { + "pid": 98700, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115720192, + "heapTotal": 60440576, + "heapUsed": 24860664, + "external": 5722812, + "arrayBuffers": 2062059, + "maxRssKiB": 113136 + }, + { + "pid": 98702, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116195328, + "heapTotal": 60702720, + "heapUsed": 24859960, + "external": 5722851, + "arrayBuffers": 2062059, + "maxRssKiB": 113664 + }, + { + "pid": 98699, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 117309440, + "heapTotal": 60964864, + "heapUsed": 25989584, + "external": 5704362, + "arrayBuffers": 2070251, + "maxRssKiB": 114560 + }, + { + "pid": 98700, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116391936, + "heapTotal": 61227008, + "heapUsed": 25894120, + "external": 5704043, + "arrayBuffers": 2070251, + "maxRssKiB": 113664 + }, + { + "pid": 98701, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 118079488, + "heapTotal": 61227008, + "heapUsed": 25894080, + "external": 5704053, + "arrayBuffers": 2070251, + "maxRssKiB": 115312 + }, + { + "pid": 98702, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116801536, + "heapTotal": 61751296, + "heapUsed": 25893680, + "external": 5704079, + "arrayBuffers": 2070251, + "maxRssKiB": 114064 + }, + { + "pid": 98699, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 117882880, + "heapTotal": 62537728, + "heapUsed": 27061560, + "external": 5704902, + "arrayBuffers": 2070251, + "maxRssKiB": 115120 + }, + { + "pid": 98702, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117424128, + "heapTotal": 62275584, + "heapUsed": 26955792, + "external": 5712240, + "arrayBuffers": 2070251, + "maxRssKiB": 114672 + }, + { + "pid": 98701, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 118439936, + "heapTotal": 62013440, + "heapUsed": 26956160, + "external": 5712214, + "arrayBuffers": 2070251, + "maxRssKiB": 115664 + }, + { + "pid": 98700, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117129216, + "heapTotal": 62537728, + "heapUsed": 26957400, + "external": 5712204, + "arrayBuffers": 2070251, + "maxRssKiB": 114384 + }, + { + "pid": 98700, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117784576, + "heapTotal": 63586304, + "heapUsed": 28033064, + "external": 5704041, + "arrayBuffers": 2082017, + "maxRssKiB": 115024 + }, + { + "pid": 98701, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 119128064, + "heapTotal": 63324160, + "heapUsed": 28033704, + "external": 5704051, + "arrayBuffers": 2082027, + "maxRssKiB": 116336 + }, + { + "pid": 98702, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 118194176, + "heapTotal": 63586304, + "heapUsed": 28033264, + "external": 5704077, + "arrayBuffers": 2070251, + "maxRssKiB": 115424 + }, + { + "pid": 98702, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 118964224, + "heapTotal": 64634880, + "heapUsed": 29103216, + "external": 5711181, + "arrayBuffers": 2070251, + "maxRssKiB": 116176 + }, + { + "pid": 98700, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 118439936, + "heapTotal": 64897024, + "heapUsed": 29093280, + "external": 5712362, + "arrayBuffers": 2070251, + "maxRssKiB": 115664 + }, + { + "pid": 98701, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 119947264, + "heapTotal": 64634880, + "heapUsed": 29104040, + "external": 5711173, + "arrayBuffers": 2070251, + "maxRssKiB": 117136 + }, + { + "pid": 98702, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 119668736, + "heapTotal": 65945600, + "heapUsed": 30133168, + "external": 5702989, + "arrayBuffers": 2070251, + "maxRssKiB": 116864 + }, + { + "pid": 98701, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 120586240, + "heapTotal": 65683456, + "heapUsed": 30132824, + "external": 5702981, + "arrayBuffers": 2070251, + "maxRssKiB": 117760 + }, + { + "pid": 98700, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 119160832, + "heapTotal": 66207744, + "heapUsed": 30123352, + "external": 5712391, + "arrayBuffers": 2070251, + "maxRssKiB": 116368 + }, + { + "pid": 98702, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 120438784, + "heapTotal": 66994176, + "heapUsed": 31157368, + "external": 5711025, + "arrayBuffers": 2070251, + "maxRssKiB": 117616 + }, + { + "pid": 98701, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 121339904, + "heapTotal": 66994176, + "heapUsed": 31156736, + "external": 5711017, + "arrayBuffers": 2070251, + "maxRssKiB": 118496 + }, + { + "pid": 98700, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 119996416, + "heapTotal": 67256320, + "heapUsed": 31147792, + "external": 5704014, + "arrayBuffers": 2070251, + "maxRssKiB": 117184 + }, + { + "pid": 98701, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 122093568, + "heapTotal": 68042752, + "heapUsed": 32179144, + "external": 5702825, + "arrayBuffers": 2070251, + "maxRssKiB": 119232 + }, + { + "pid": 98702, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 121176064, + "heapTotal": 68304896, + "heapUsed": 32178672, + "external": 5702833, + "arrayBuffers": 2070251, + "maxRssKiB": 118336 + }, + { + "pid": 98700, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 120537088, + "heapTotal": 68567040, + "heapUsed": 32170944, + "external": 5712235, + "arrayBuffers": 2070251, + "maxRssKiB": 117712 + }, + { + "pid": 98701, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 122781696, + "heapTotal": 69353472, + "heapUsed": 33243344, + "external": 5711016, + "arrayBuffers": 2070251, + "maxRssKiB": 119904 + }, + { + "pid": 98702, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121962496, + "heapTotal": 69353472, + "heapUsed": 33243592, + "external": 5711023, + "arrayBuffers": 2070251, + "maxRssKiB": 119104 + }, + { + "pid": 98700, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121290752, + "heapTotal": 69615616, + "heapUsed": 33248584, + "external": 5702822, + "arrayBuffers": 2070251, + "maxRssKiB": 118448 + }, + { + "pid": 98702, + "worker": "4", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122699776, + "heapTotal": 70664192, + "heapUsed": 34278632, + "external": 5711024, + "arrayBuffers": 2070251, + "maxRssKiB": 119824 + }, + { + "pid": 98701, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 123404288, + "heapTotal": 70139904, + "heapUsed": 34278304, + "external": 5711016, + "arrayBuffers": 2070251, + "maxRssKiB": 120512 + }, + { + "pid": 98700, + "worker": "2", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122011648, + "heapTotal": 70664192, + "heapUsed": 34279456, + "external": 5711014, + "arrayBuffers": 2088990, + "maxRssKiB": 119152 + } + ] + }, + { + "id": "15-measured-8-cli", + "phase": "measured", + "mode": "cli", + "workers": 8, + "wallSeconds": 6.823447874999998, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "tests": 33, + "leftovers": 0, + "openHandles": 0, + "children": [], + "parentSamples": [ + { + "pid": 98879, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 115638272, + "heapTotal": 61489152, + "heapUsed": 24955776, + "external": 5723164, + "arrayBuffers": 2062059, + "maxRssKiB": 113056 + }, + { + "pid": 98877, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115900416, + "heapTotal": 60702720, + "heapUsed": 24860032, + "external": 5722822, + "arrayBuffers": 2062059, + "maxRssKiB": 113312 + }, + { + "pid": 98878, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115589120, + "heapTotal": 60178432, + "heapUsed": 24860592, + "external": 5722850, + "arrayBuffers": 2062059, + "maxRssKiB": 113104 + }, + { + "pid": 98876, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115802112, + "heapTotal": 60440576, + "heapUsed": 24859200, + "external": 5722811, + "arrayBuffers": 2062059, + "maxRssKiB": 113344 + }, + { + "pid": 98879, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 116604928, + "heapTotal": 62013440, + "heapUsed": 25986152, + "external": 5704362, + "arrayBuffers": 2070251, + "maxRssKiB": 113872 + }, + { + "pid": 98878, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116260864, + "heapTotal": 61227008, + "heapUsed": 25894240, + "external": 5704079, + "arrayBuffers": 2070251, + "maxRssKiB": 113536 + }, + { + "pid": 98876, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116523008, + "heapTotal": 61489152, + "heapUsed": 25894208, + "external": 5704043, + "arrayBuffers": 2070251, + "maxRssKiB": 113792 + }, + { + "pid": 98877, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116555776, + "heapTotal": 61227008, + "heapUsed": 25894216, + "external": 5704053, + "arrayBuffers": 2070251, + "maxRssKiB": 113824 + }, + { + "pid": 98879, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 117194752, + "heapTotal": 62799872, + "heapUsed": 27061128, + "external": 5704902, + "arrayBuffers": 2070251, + "maxRssKiB": 114448 + }, + { + "pid": 98878, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117080064, + "heapTotal": 62537728, + "heapUsed": 26956320, + "external": 5712240, + "arrayBuffers": 2070251, + "maxRssKiB": 114336 + }, + { + "pid": 98876, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117112832, + "heapTotal": 62537728, + "heapUsed": 26956416, + "external": 5712204, + "arrayBuffers": 2070251, + "maxRssKiB": 114368 + }, + { + "pid": 98877, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117145600, + "heapTotal": 62275584, + "heapUsed": 26956184, + "external": 5712214, + "arrayBuffers": 2070251, + "maxRssKiB": 114400 + }, + { + "pid": 98878, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117719040, + "heapTotal": 63848448, + "heapUsed": 28034728, + "external": 5704077, + "arrayBuffers": 2070251, + "maxRssKiB": 114960 + }, + { + "pid": 98877, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117833728, + "heapTotal": 63586304, + "heapUsed": 28035344, + "external": 5704051, + "arrayBuffers": 2070251, + "maxRssKiB": 115072 + }, + { + "pid": 98876, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117751808, + "heapTotal": 63324160, + "heapUsed": 28033096, + "external": 5704041, + "arrayBuffers": 2070251, + "maxRssKiB": 114992 + }, + { + "pid": 98877, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 118571008, + "heapTotal": 64634880, + "heapUsed": 29103616, + "external": 5711173, + "arrayBuffers": 2070251, + "maxRssKiB": 115792 + }, + { + "pid": 98878, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 118276096, + "heapTotal": 64634880, + "heapUsed": 29101832, + "external": 5711181, + "arrayBuffers": 2070251, + "maxRssKiB": 115504 + }, + { + "pid": 98876, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 118505472, + "heapTotal": 64897024, + "heapUsed": 29095608, + "external": 5712362, + "arrayBuffers": 2070251, + "maxRssKiB": 115728 + }, + { + "pid": 98877, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 119144448, + "heapTotal": 65945600, + "heapUsed": 30132496, + "external": 5702981, + "arrayBuffers": 2070251, + "maxRssKiB": 116352 + }, + { + "pid": 98878, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 118996992, + "heapTotal": 65945600, + "heapUsed": 30130904, + "external": 5702989, + "arrayBuffers": 2070251, + "maxRssKiB": 116208 + }, + { + "pid": 98876, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 119226368, + "heapTotal": 66207744, + "heapUsed": 30123360, + "external": 5712391, + "arrayBuffers": 2070251, + "maxRssKiB": 116432 + }, + { + "pid": 98878, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 119619584, + "heapTotal": 66994176, + "heapUsed": 31156752, + "external": 5711025, + "arrayBuffers": 2070251, + "maxRssKiB": 116816 + }, + { + "pid": 98877, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 119865344, + "heapTotal": 66994176, + "heapUsed": 31156968, + "external": 5711017, + "arrayBuffers": 2070251, + "maxRssKiB": 117056 + }, + { + "pid": 98876, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 119898112, + "heapTotal": 67256320, + "heapUsed": 31146696, + "external": 5704014, + "arrayBuffers": 2070251, + "maxRssKiB": 117088 + }, + { + "pid": 98877, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 120504320, + "heapTotal": 67780608, + "heapUsed": 32179408, + "external": 5702825, + "arrayBuffers": 2070251, + "maxRssKiB": 117696 + }, + { + "pid": 98878, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 120438784, + "heapTotal": 68304896, + "heapUsed": 32179608, + "external": 5702833, + "arrayBuffers": 2070251, + "maxRssKiB": 117616 + }, + { + "pid": 98876, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 120635392, + "heapTotal": 68304896, + "heapUsed": 32170616, + "external": 5712235, + "arrayBuffers": 2070251, + "maxRssKiB": 117808 + }, + { + "pid": 98877, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121176064, + "heapTotal": 69353472, + "heapUsed": 33244616, + "external": 5711016, + "arrayBuffers": 2070251, + "maxRssKiB": 118336 + }, + { + "pid": 98878, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121061376, + "heapTotal": 69091328, + "heapUsed": 33243664, + "external": 5711024, + "arrayBuffers": 2070251, + "maxRssKiB": 118224 + }, + { + "pid": 98876, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121389056, + "heapTotal": 69615616, + "heapUsed": 33248592, + "external": 5702822, + "arrayBuffers": 2070251, + "maxRssKiB": 118544 + }, + { + "pid": 98877, + "worker": "2", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 121929728, + "heapTotal": 70402048, + "heapUsed": 34278376, + "external": 5711016, + "arrayBuffers": 2070251, + "maxRssKiB": 119072 + }, + { + "pid": 98878, + "worker": "3", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 121765888, + "heapTotal": 70402048, + "heapUsed": 34279168, + "external": 5711024, + "arrayBuffers": 2070251, + "maxRssKiB": 118912 + }, + { + "pid": 98876, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122208256, + "heapTotal": 70664192, + "heapUsed": 34279832, + "external": 5711014, + "arrayBuffers": 2070251, + "maxRssKiB": 119344 + } + ] + }, + { + "id": "16-measured-8-session", + "phase": "measured", + "mode": "session", + "workers": 8, + "wallSeconds": 2.3346659999999972, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "tests": 33, + "leftovers": 0, + "openHandles": 0, + "children": [ + { + "pid": 99116, + "build": 1, + "rootDir": ".cache/integration/fixture-yntHPt", + "cwd": ".cache/integration/session-24oUfy", + "rss": 315097088, + "heapUsed": 71158960, + "maxRssKiB": 307840 + }, + { + "pid": 99117, + "build": 1, + "rootDir": ".cache/integration/fixture-y83Ywn", + "cwd": ".cache/integration/session-DatcZ3", + "rss": 341360640, + "heapUsed": 71199736, + "maxRssKiB": 333488 + }, + { + "pid": 99118, + "build": 1, + "rootDir": ".cache/integration/fixture-daCsG3", + "cwd": ".cache/integration/session-3oHbpU", + "rss": 344014848, + "heapUsed": 71207208, + "maxRssKiB": 336080 + }, + { + "pid": 99117, + "build": 2, + "rootDir": ".cache/integration/fixture-I9It0z", + "cwd": ".cache/integration/session-DatcZ3", + "rss": 353370112, + "heapUsed": 72809992, + "maxRssKiB": 345216 + }, + { + "pid": 99118, + "build": 2, + "rootDir": ".cache/integration/fixture-W0Fir8", + "cwd": ".cache/integration/session-3oHbpU", + "rss": 356679680, + "heapUsed": 72816928, + "maxRssKiB": 348448 + }, + { + "pid": 99116, + "build": 2, + "rootDir": ".cache/integration/fixture-HcWoEQ", + "cwd": ".cache/integration/session-24oUfy", + "rss": 328974336, + "heapUsed": 72854648, + "maxRssKiB": 321392 + }, + { + "pid": 99118, + "build": 3, + "rootDir": ".cache/integration/fixture-XIkOTT", + "cwd": ".cache/integration/session-3oHbpU", + "rss": 366313472, + "heapUsed": 74456008, + "maxRssKiB": 357856 + }, + { + "pid": 99116, + "build": 3, + "rootDir": ".cache/integration/fixture-3pUXMp", + "cwd": ".cache/integration/session-24oUfy", + "rss": 340197376, + "heapUsed": 74485800, + "maxRssKiB": 332352 + }, + { + "pid": 99117, + "build": 3, + "rootDir": ".cache/integration/fixture-ZBz8RB", + "cwd": ".cache/integration/session-DatcZ3", + "rss": 364052480, + "heapUsed": 74448168, + "maxRssKiB": 355648 + }, + { + "pid": 99118, + "build": 4, + "rootDir": ".cache/integration/fixture-in2MoK", + "cwd": ".cache/integration/session-3oHbpU", + "rss": 376635392, + "heapUsed": 76005208, + "maxRssKiB": 367936 + }, + { + "pid": 99116, + "build": 4, + "rootDir": ".cache/integration/fixture-TFeo9n", + "cwd": ".cache/integration/session-24oUfy", + "rss": 350552064, + "heapUsed": 76037288, + "maxRssKiB": 342464 + }, + { + "pid": 99117, + "build": 4, + "rootDir": ".cache/integration/fixture-hGbdyu", + "cwd": ".cache/integration/session-DatcZ3", + "rss": 374112256, + "heapUsed": 76002280, + "maxRssKiB": 365472 + }, + { + "pid": 99118, + "build": 5, + "rootDir": ".cache/integration/fixture-Sf5uNT", + "cwd": ".cache/integration/session-3oHbpU", + "rss": 377323520, + "heapUsed": 76568088, + "maxRssKiB": 368608 + }, + { + "pid": 99116, + "build": 5, + "rootDir": ".cache/integration/fixture-wHyhGp", + "cwd": ".cache/integration/session-24oUfy", + "rss": 351977472, + "heapUsed": 76605496, + "maxRssKiB": 343856 + }, + { + "pid": 99117, + "build": 5, + "rootDir": ".cache/integration/fixture-cX5YQp", + "cwd": ".cache/integration/session-DatcZ3", + "rss": 383614976, + "heapUsed": 77599336, + "maxRssKiB": 374752 + }, + { + "pid": 99118, + "build": 6, + "rootDir": ".cache/integration/fixture-8DIxal", + "cwd": ".cache/integration/session-3oHbpU", + "rss": 378060800, + "heapUsed": 77105816, + "maxRssKiB": 369328 + }, + { + "pid": 99116, + "build": 6, + "rootDir": ".cache/integration/fixture-TQBaZS", + "cwd": ".cache/integration/session-24oUfy", + "rss": 353075200, + "heapUsed": 77143704, + "maxRssKiB": 344928 + }, + { + "pid": 99118, + "build": 7, + "rootDir": ".cache/integration/fixture-lQHqeJ", + "cwd": ".cache/integration/session-3oHbpU", + "rss": 378912768, + "heapUsed": 77554008, + "maxRssKiB": 370160 + }, + { + "pid": 99117, + "build": 6, + "rootDir": ".cache/integration/fixture-c70DV9", + "cwd": ".cache/integration/session-DatcZ3", + "rss": 392200192, + "heapUsed": 79175384, + "maxRssKiB": 383136 + }, + { + "pid": 99116, + "build": 7, + "rootDir": ".cache/integration/fixture-jIRxjf", + "cwd": ".cache/integration/session-24oUfy", + "rss": 354287616, + "heapUsed": 77603216, + "maxRssKiB": 346112 + }, + { + "pid": 99118, + "build": 8, + "rootDir": ".cache/integration/fixture-739fX1", + "cwd": ".cache/integration/session-3oHbpU", + "rss": 379600896, + "heapUsed": 78095984, + "maxRssKiB": 370832 + }, + { + "pid": 99116, + "build": 8, + "rootDir": ".cache/integration/fixture-j0AxHG", + "cwd": ".cache/integration/session-24oUfy", + "rss": 355434496, + "heapUsed": 78147888, + "maxRssKiB": 347232 + }, + { + "pid": 99117, + "build": 7, + "rootDir": ".cache/integration/fixture-IQACgq", + "cwd": ".cache/integration/session-DatcZ3", + "rss": 400801792, + "heapUsed": 80665384, + "maxRssKiB": 391536 + }, + { + "pid": 99118, + "build": 9, + "rootDir": ".cache/integration/fixture-R6txJT", + "cwd": ".cache/integration/session-3oHbpU", + "rss": 380321792, + "heapUsed": 78796720, + "maxRssKiB": 371536 + }, + { + "pid": 99116, + "build": 9, + "rootDir": ".cache/integration/fixture-CuzvGp", + "cwd": ".cache/integration/session-24oUfy", + "rss": 356892672, + "heapUsed": 78846056, + "maxRssKiB": 348656 + }, + { + "pid": 99117, + "build": 8, + "rootDir": ".cache/integration/fixture-1De76i", + "cwd": ".cache/integration/session-DatcZ3", + "rss": 410894336, + "heapUsed": 82243536, + "maxRssKiB": 401392 + }, + { + "pid": 99118, + "build": 10, + "rootDir": ".cache/integration/fixture-MTlXFV", + "cwd": ".cache/integration/session-3oHbpU", + "rss": 380944384, + "heapUsed": 79401816, + "maxRssKiB": 372144 + }, + { + "pid": 99116, + "build": 10, + "rootDir": ".cache/integration/fixture-N1aej8", + "cwd": ".cache/integration/session-24oUfy", + "rss": 358301696, + "heapUsed": 79440184, + "maxRssKiB": 350032 + }, + { + "pid": 99117, + "build": 9, + "rootDir": ".cache/integration/fixture-yLY2Dt", + "cwd": ".cache/integration/session-DatcZ3", + "rss": 412221440, + "heapUsed": 82952736, + "maxRssKiB": 402688 + }, + { + "pid": 99117, + "build": 10, + "rootDir": ".cache/integration/fixture-eCREBE", + "cwd": ".cache/integration/session-DatcZ3", + "rss": 413384704, + "heapUsed": 83543504, + "maxRssKiB": 403824 + } + ], + "parentSamples": [ + { + "pid": 99111, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 116326400, + "heapTotal": 61227008, + "heapUsed": 24979664, + "external": 5723167, + "arrayBuffers": 2062059, + "maxRssKiB": 113728 + }, + { + "pid": 99108, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115539968, + "heapTotal": 60964864, + "heapUsed": 24853912, + "external": 5723123, + "arrayBuffers": 2062066, + "maxRssKiB": 113056 + }, + { + "pid": 99109, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 115752960, + "heapTotal": 60702720, + "heapUsed": 24853024, + "external": 5723153, + "arrayBuffers": 2062066, + "maxRssKiB": 113216 + }, + { + "pid": 99110, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 116850688, + "heapTotal": 60440576, + "heapUsed": 24856424, + "external": 5723131, + "arrayBuffers": 2062066, + "maxRssKiB": 114336 + }, + { + "pid": 99108, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116097024, + "heapTotal": 61751296, + "heapUsed": 25881472, + "external": 5703737, + "arrayBuffers": 2070258, + "maxRssKiB": 113376 + }, + { + "pid": 99110, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 117489664, + "heapTotal": 61227008, + "heapUsed": 25881408, + "external": 5703744, + "arrayBuffers": 2070258, + "maxRssKiB": 114736 + }, + { + "pid": 99109, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 116310016, + "heapTotal": 61227008, + "heapUsed": 25879136, + "external": 5703764, + "arrayBuffers": 2070258, + "maxRssKiB": 113584 + }, + { + "pid": 99109, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 117129216, + "heapTotal": 62537728, + "heapUsed": 26942456, + "external": 5703733, + "arrayBuffers": 2070258, + "maxRssKiB": 114384 + }, + { + "pid": 99110, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 118095872, + "heapTotal": 62799872, + "heapUsed": 26941464, + "external": 5703713, + "arrayBuffers": 2070258, + "maxRssKiB": 115328 + }, + { + "pid": 99108, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 116719616, + "heapTotal": 62537728, + "heapUsed": 26939400, + "external": 5703706, + "arrayBuffers": 2070258, + "maxRssKiB": 113984 + }, + { + "pid": 99110, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 118816768, + "heapTotal": 63586304, + "heapUsed": 28000928, + "external": 5711934, + "arrayBuffers": 2070258, + "maxRssKiB": 116032 + }, + { + "pid": 99109, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117866496, + "heapTotal": 63586304, + "heapUsed": 28004320, + "external": 5711954, + "arrayBuffers": 2070258, + "maxRssKiB": 115104 + }, + { + "pid": 99108, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 117424128, + "heapTotal": 63848448, + "heapUsed": 28006800, + "external": 5711927, + "arrayBuffers": 2070258, + "maxRssKiB": 114672 + }, + { + "pid": 99109, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 118571008, + "heapTotal": 64897024, + "heapUsed": 29061072, + "external": 5703099, + "arrayBuffers": 2070258, + "maxRssKiB": 115792 + }, + { + "pid": 99110, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 119586816, + "heapTotal": 64897024, + "heapUsed": 29059520, + "external": 5703091, + "arrayBuffers": 2070258, + "maxRssKiB": 116784 + }, + { + "pid": 99108, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 118161408, + "heapTotal": 64897024, + "heapUsed": 29048736, + "external": 5703864, + "arrayBuffers": 2070258, + "maxRssKiB": 115392 + }, + { + "pid": 99110, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 120291328, + "heapTotal": 65945600, + "heapUsed": 30088472, + "external": 5711283, + "arrayBuffers": 2070258, + "maxRssKiB": 117472 + }, + { + "pid": 99109, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 119291904, + "heapTotal": 66207744, + "heapUsed": 30088280, + "external": 5711291, + "arrayBuffers": 2070258, + "maxRssKiB": 116496 + }, + { + "pid": 99110, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 121028608, + "heapTotal": 67256320, + "heapUsed": 31111368, + "external": 5702935, + "arrayBuffers": 2070258, + "maxRssKiB": 118192 + }, + { + "pid": 99108, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 118849536, + "heapTotal": 66207744, + "heapUsed": 30080064, + "external": 5712085, + "arrayBuffers": 2070258, + "maxRssKiB": 116064 + }, + { + "pid": 99109, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 119930880, + "heapTotal": 67256320, + "heapUsed": 31110376, + "external": 5702943, + "arrayBuffers": 2070258, + "maxRssKiB": 117120 + }, + { + "pid": 99110, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 121815040, + "heapTotal": 68304896, + "heapUsed": 32134480, + "external": 5711127, + "arrayBuffers": 2070258, + "maxRssKiB": 118960 + }, + { + "pid": 99109, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 120717312, + "heapTotal": 68304896, + "heapUsed": 32133488, + "external": 5711135, + "arrayBuffers": 2070258, + "maxRssKiB": 117888 + }, + { + "pid": 99108, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 119619584, + "heapTotal": 67518464, + "heapUsed": 31101784, + "external": 5711900, + "arrayBuffers": 2070258, + "maxRssKiB": 116816 + }, + { + "pid": 99110, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 122535936, + "heapTotal": 69615616, + "heapUsed": 33189456, + "external": 5702934, + "arrayBuffers": 2070258, + "maxRssKiB": 119664 + }, + { + "pid": 99109, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 121421824, + "heapTotal": 69615616, + "heapUsed": 33188800, + "external": 5702942, + "arrayBuffers": 2070258, + "maxRssKiB": 118576 + }, + { + "pid": 99108, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 120242176, + "heapTotal": 68567040, + "heapUsed": 32125136, + "external": 5703737, + "arrayBuffers": 2070258, + "maxRssKiB": 117424 + }, + { + "pid": 99109, + "worker": "2", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 122126336, + "heapTotal": 70926336, + "heapUsed": 34220936, + "external": 5711136, + "arrayBuffers": 2070258, + "maxRssKiB": 119264 + }, + { + "pid": 99110, + "worker": "3", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 123273216, + "heapTotal": 70664192, + "heapUsed": 34221240, + "external": 5711128, + "arrayBuffers": 2070258, + "maxRssKiB": 120384 + }, + { + "pid": 99108, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 120897536, + "heapTotal": 69615616, + "heapUsed": 33193568, + "external": 5702932, + "arrayBuffers": 2070258, + "maxRssKiB": 118064 + }, + { + "pid": 99108, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 121683968, + "heapTotal": 70926336, + "heapUsed": 34225864, + "external": 5711126, + "arrayBuffers": 2070258, + "maxRssKiB": 118832 + }, + { + "pid": 99111, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 117161984, + "heapTotal": 61489152, + "heapUsed": 26008272, + "external": 5704362, + "arrayBuffers": 2070251, + "maxRssKiB": 114416 + }, + { + "pid": 99111, + "worker": "4", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 117719040, + "heapTotal": 62799872, + "heapUsed": 27082976, + "external": 5704902, + "arrayBuffers": 2070251, + "maxRssKiB": 114960 + } + ] + }, + { + "id": "17-handles-1-cli", + "phase": "handles", + "mode": "cli", + "workers": 1, + "wallSeconds": 21.403079625000014, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "tests": 33, + "leftovers": 0, + "openHandles": 0, + "children": [], + "parentSamples": [ + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 169082880, + "heapTotal": 107429888, + "heapUsed": 36126856, + "external": 11884685, + "arrayBuffers": 2062056, + "maxRssKiB": 165248 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 169558016, + "heapTotal": 108412928, + "heapUsed": 37381440, + "external": 11899315, + "arrayBuffers": 2070248, + "maxRssKiB": 165584 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 169885696, + "heapTotal": 109461504, + "heapUsed": 38533648, + "external": 11899855, + "arrayBuffers": 2070248, + "maxRssKiB": 165904 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 183336960, + "heapTotal": 113180672, + "heapUsed": 40911136, + "external": 13949848, + "arrayBuffers": 2078440, + "maxRssKiB": 179168 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 183910400, + "heapTotal": 113704960, + "heapUsed": 41948200, + "external": 11915380, + "arrayBuffers": 2086632, + "maxRssKiB": 179600 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 184385536, + "heapTotal": 115015680, + "heapUsed": 42990312, + "external": 11923541, + "arrayBuffers": 2086632, + "maxRssKiB": 180064 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 184926208, + "heapTotal": 116064256, + "heapUsed": 44052464, + "external": 11915378, + "arrayBuffers": 2086632, + "maxRssKiB": 180592 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 185155584, + "heapTotal": 117112832, + "heapUsed": 45089920, + "external": 11923699, + "arrayBuffers": 2086632, + "maxRssKiB": 180816 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 185892864, + "heapTotal": 118161408, + "heapUsed": 46124040, + "external": 11923728, + "arrayBuffers": 2086632, + "maxRssKiB": 181536 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 186073088, + "heapTotal": 118947840, + "heapUsed": 47155176, + "external": 11915351, + "arrayBuffers": 2086632, + "maxRssKiB": 181712 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 187236352, + "heapTotal": 120520704, + "heapUsed": 48176600, + "external": 11923572, + "arrayBuffers": 2086632, + "maxRssKiB": 182848 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 187809792, + "heapTotal": 121307136, + "heapUsed": 49246960, + "external": 11914159, + "arrayBuffers": 2086632, + "maxRssKiB": 183408 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 188350464, + "heapTotal": 122355712, + "heapUsed": 50269168, + "external": 11922351, + "arrayBuffers": 2086632, + "maxRssKiB": 183936 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 201768960, + "heapTotal": 122634240, + "heapUsed": 41464328, + "external": 13958050, + "arrayBuffers": 2078440, + "maxRssKiB": 197616 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 201981952, + "heapTotal": 114245632, + "heapUsed": 42506568, + "external": 11915390, + "arrayBuffers": 2086632, + "maxRssKiB": 197616 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 201981952, + "heapTotal": 115294208, + "heapUsed": 43511736, + "external": 11923551, + "arrayBuffers": 2086632, + "maxRssKiB": 197616 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 202063872, + "heapTotal": 116342784, + "heapUsed": 44564328, + "external": 11915388, + "arrayBuffers": 2086632, + "maxRssKiB": 197616 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 202096640, + "heapTotal": 117653504, + "heapUsed": 45594536, + "external": 11922510, + "arrayBuffers": 2086632, + "maxRssKiB": 197616 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 202113024, + "heapTotal": 118702080, + "heapUsed": 46615472, + "external": 11914318, + "arrayBuffers": 2086632, + "maxRssKiB": 197616 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 207339520, + "heapTotal": 186859520, + "heapUsed": 47639192, + "external": 11922354, + "arrayBuffers": 2086632, + "maxRssKiB": 202480 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 207470592, + "heapTotal": 187908096, + "heapUsed": 48685344, + "external": 11914162, + "arrayBuffers": 2086632, + "maxRssKiB": 202608 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 207929344, + "heapTotal": 188956672, + "heapUsed": 49736912, + "external": 11922353, + "arrayBuffers": 2086632, + "maxRssKiB": 203056 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 208289792, + "heapTotal": 189743104, + "heapUsed": 50765352, + "external": 11922353, + "arrayBuffers": 2086632, + "maxRssKiB": 203408 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 215728128, + "heapTotal": 189743104, + "heapUsed": 41558560, + "external": 13958076, + "arrayBuffers": 2078440, + "maxRssKiB": 211312 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 215990272, + "heapTotal": 181616640, + "heapUsed": 42555592, + "external": 11915416, + "arrayBuffers": 2086940, + "maxRssKiB": 211312 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 216023040, + "heapTotal": 182665216, + "heapUsed": 43596064, + "external": 11923577, + "arrayBuffers": 2086632, + "maxRssKiB": 211312 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 216072192, + "heapTotal": 183451648, + "heapUsed": 44656152, + "external": 11915414, + "arrayBuffers": 2086632, + "maxRssKiB": 211312 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 216072192, + "heapTotal": 184762368, + "heapUsed": 45686632, + "external": 11922518, + "arrayBuffers": 2086632, + "maxRssKiB": 211312 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 216072192, + "heapTotal": 186073088, + "heapUsed": 46711936, + "external": 11914326, + "arrayBuffers": 2086632, + "maxRssKiB": 211312 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 216072192, + "heapTotal": 187383808, + "heapUsed": 47739784, + "external": 11922362, + "arrayBuffers": 2086632, + "maxRssKiB": 211312 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 216104960, + "heapTotal": 188694528, + "heapUsed": 48769968, + "external": 11914170, + "arrayBuffers": 2086632, + "maxRssKiB": 211312 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 216104960, + "heapTotal": 189743104, + "heapUsed": 49818208, + "external": 11922361, + "arrayBuffers": 2086632, + "maxRssKiB": 211312 + }, + { + "pid": 99123, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 216104960, + "heapTotal": 190267392, + "heapUsed": 50846936, + "external": 11922361, + "arrayBuffers": 2086632, + "maxRssKiB": 211312 + } + ] + }, + { + "id": "18-handles-1-session", + "phase": "handles", + "mode": "session", + "workers": 1, + "wallSeconds": 6.103320749999984, + "names": [ + "bookmarks override chrome MV2 build replaces the browser page", + "bookmarks override chrome MV3 build replaces the browser page", + "bookmarks override edge MV2 build replaces the browser page", + "bookmarks override edge MV3 build replaces the browser page", + "bookmarks override firefox MV2 build skips the unsupported page", + "bookmarks override firefox MV3 build skips the unsupported page", + "bookmarks override opera MV2 build skips the unsupported page", + "bookmarks override opera MV3 build skips the unsupported page", + "bookmarks override safari MV2 build skips the unsupported page", + "bookmarks override safari MV3 build skips the unsupported page", + "competing overrides chrome build fails and names both entrypoints", + "competing overrides edge build fails and names both entrypoints", + "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "history override chrome MV2 build replaces the browser page", + "history override chrome MV3 build replaces the browser page", + "history override edge MV2 build replaces the browser page", + "history override edge MV3 build replaces the browser page", + "history override firefox MV2 build skips the unsupported page", + "history override firefox MV3 build skips the unsupported page", + "history override opera MV2 build skips the unsupported page", + "history override opera MV3 build skips the unsupported page", + "history override safari MV2 build skips the unsupported page", + "history override safari MV3 build skips the unsupported page", + "newtab override chrome MV2 build replaces the browser page", + "newtab override chrome MV3 build replaces the browser page", + "newtab override edge MV2 build replaces the browser page", + "newtab override edge MV3 build replaces the browser page", + "newtab override firefox MV2 build replaces the browser page", + "newtab override firefox MV3 build replaces the browser page", + "newtab override opera MV2 build skips the unsupported page", + "newtab override opera MV3 build skips the unsupported page", + "newtab override safari MV2 build replaces the browser page", + "newtab override safari MV3 build replaces the browser page" + ], + "tests": 33, + "leftovers": 0, + "openHandles": 0, + "children": [ + { + "pid": 99666, + "build": 1, + "rootDir": ".cache/integration/fixture-BDVvw4", + "cwd": ".cache/integration/session-6qxS3L", + "rss": 315637760, + "heapUsed": 71151088, + "maxRssKiB": 308368 + }, + { + "pid": 99666, + "build": 2, + "rootDir": ".cache/integration/fixture-WUFKTQ", + "cwd": ".cache/integration/session-6qxS3L", + "rss": 328613888, + "heapUsed": 72847552, + "maxRssKiB": 321040 + }, + { + "pid": 99666, + "build": 3, + "rootDir": ".cache/integration/fixture-FhbYvw", + "cwd": ".cache/integration/session-6qxS3L", + "rss": 339574784, + "heapUsed": 74477600, + "maxRssKiB": 331744 + }, + { + "pid": 99666, + "build": 4, + "rootDir": ".cache/integration/fixture-dmGMpY", + "cwd": ".cache/integration/session-6qxS3L", + "rss": 350371840, + "heapUsed": 76029288, + "maxRssKiB": 342288 + }, + { + "pid": 99666, + "build": 5, + "rootDir": ".cache/integration/fixture-NOvCHb", + "cwd": ".cache/integration/session-6qxS3L", + "rss": 351682560, + "heapUsed": 76596352, + "maxRssKiB": 343568 + }, + { + "pid": 99666, + "build": 6, + "rootDir": ".cache/integration/fixture-f99dmX", + "cwd": ".cache/integration/session-6qxS3L", + "rss": 352911360, + "heapUsed": 77134600, + "maxRssKiB": 344768 + }, + { + "pid": 99666, + "build": 7, + "rootDir": ".cache/integration/fixture-NNdshK", + "cwd": ".cache/integration/session-6qxS3L", + "rss": 354172928, + "heapUsed": 77594416, + "maxRssKiB": 346000 + }, + { + "pid": 99666, + "build": 8, + "rootDir": ".cache/integration/fixture-9rhr4S", + "cwd": ".cache/integration/session-6qxS3L", + "rss": 360792064, + "heapUsed": 78130488, + "maxRssKiB": 352464 + }, + { + "pid": 99666, + "build": 9, + "rootDir": ".cache/integration/fixture-oETlSs", + "cwd": ".cache/integration/session-6qxS3L", + "rss": 362250240, + "heapUsed": 78826488, + "maxRssKiB": 353888 + }, + { + "pid": 99666, + "build": 10, + "rootDir": ".cache/integration/fixture-jIarv7", + "cwd": ".cache/integration/session-6qxS3L", + "rss": 363626496, + "heapUsed": 79431960, + "maxRssKiB": 355232 + }, + { + "pid": 99667, + "build": 1, + "rootDir": ".cache/integration/fixture-kPzp6Z", + "cwd": ".cache/integration/session-we72mE", + "rss": 315408384, + "heapUsed": 71144112, + "maxRssKiB": 308144 + }, + { + "pid": 99667, + "build": 2, + "rootDir": ".cache/integration/fixture-OppZaq", + "cwd": ".cache/integration/session-we72mE", + "rss": 328646656, + "heapUsed": 72836608, + "maxRssKiB": 321072 + }, + { + "pid": 99667, + "build": 3, + "rootDir": ".cache/integration/fixture-xURRtJ", + "cwd": ".cache/integration/session-we72mE", + "rss": 339525632, + "heapUsed": 74472200, + "maxRssKiB": 331696 + }, + { + "pid": 99667, + "build": 4, + "rootDir": ".cache/integration/fixture-veYyM0", + "cwd": ".cache/integration/session-we72mE", + "rss": 350420992, + "heapUsed": 76019760, + "maxRssKiB": 342336 + }, + { + "pid": 99667, + "build": 5, + "rootDir": ".cache/integration/fixture-KPB37P", + "cwd": ".cache/integration/session-we72mE", + "rss": 360595456, + "heapUsed": 77624944, + "maxRssKiB": 352272 + }, + { + "pid": 99667, + "build": 6, + "rootDir": ".cache/integration/fixture-S8uXU9", + "cwd": ".cache/integration/session-we72mE", + "rss": 375439360, + "heapUsed": 79201744, + "maxRssKiB": 366768 + }, + { + "pid": 99667, + "build": 7, + "rootDir": ".cache/integration/fixture-MwFnMi", + "cwd": ".cache/integration/session-we72mE", + "rss": 384548864, + "heapUsed": 80676912, + "maxRssKiB": 375664 + }, + { + "pid": 99667, + "build": 8, + "rootDir": ".cache/integration/fixture-qUsqBh", + "cwd": ".cache/integration/session-we72mE", + "rss": 393723904, + "heapUsed": 82258104, + "maxRssKiB": 384624 + }, + { + "pid": 99667, + "build": 9, + "rootDir": ".cache/integration/fixture-zSQpMj", + "cwd": ".cache/integration/session-we72mE", + "rss": 395198464, + "heapUsed": 82967880, + "maxRssKiB": 386064 + }, + { + "pid": 99667, + "build": 10, + "rootDir": ".cache/integration/fixture-G6aQlU", + "cwd": ".cache/integration/session-we72mE", + "rss": 396541952, + "heapUsed": 83570720, + "maxRssKiB": 387376 + }, + { + "pid": 99737, + "build": 1, + "rootDir": ".cache/integration/fixture-1UtWxy", + "cwd": ".cache/integration/session-sdxa7T", + "rss": 313180160, + "heapUsed": 71158648, + "maxRssKiB": 305968 + }, + { + "pid": 99737, + "build": 2, + "rootDir": ".cache/integration/fixture-oCUVA3", + "cwd": ".cache/integration/session-sdxa7T", + "rss": 325582848, + "heapUsed": 72851696, + "maxRssKiB": 318080 + }, + { + "pid": 99737, + "build": 3, + "rootDir": ".cache/integration/fixture-f6mHj7", + "cwd": ".cache/integration/session-sdxa7T", + "rss": 336494592, + "heapUsed": 74485800, + "maxRssKiB": 328736 + }, + { + "pid": 99737, + "build": 4, + "rootDir": ".cache/integration/fixture-Di9AjT", + "cwd": ".cache/integration/session-sdxa7T", + "rss": 346996736, + "heapUsed": 76037176, + "maxRssKiB": 338992 + }, + { + "pid": 99737, + "build": 5, + "rootDir": ".cache/integration/fixture-yBfHCO", + "cwd": ".cache/integration/session-sdxa7T", + "rss": 348323840, + "heapUsed": 76604504, + "maxRssKiB": 340288 + }, + { + "pid": 99737, + "build": 6, + "rootDir": ".cache/integration/fixture-ieDtLa", + "cwd": ".cache/integration/session-sdxa7T", + "rss": 349487104, + "heapUsed": 77142472, + "maxRssKiB": 341424 + }, + { + "pid": 99737, + "build": 7, + "rootDir": ".cache/integration/fixture-7bQq0S", + "cwd": ".cache/integration/session-sdxa7T", + "rss": 350683136, + "heapUsed": 77591512, + "maxRssKiB": 342592 + }, + { + "pid": 99737, + "build": 8, + "rootDir": ".cache/integration/fixture-WofWO2", + "cwd": ".cache/integration/session-sdxa7T", + "rss": 351682560, + "heapUsed": 78133648, + "maxRssKiB": 343568 + }, + { + "pid": 99737, + "build": 9, + "rootDir": ".cache/integration/fixture-USrgyf", + "cwd": ".cache/integration/session-sdxa7T", + "rss": 353206272, + "heapUsed": 78833048, + "maxRssKiB": 345056 + }, + { + "pid": 99737, + "build": 10, + "rootDir": ".cache/integration/fixture-QsyXTR", + "cwd": ".cache/integration/session-sdxa7T", + "rss": 354598912, + "heapUsed": 79439008, + "maxRssKiB": 346416 + } + ], + "parentSamples": [ + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 168493056, + "heapTotal": 106905600, + "heapUsed": 36132304, + "external": 11888695, + "arrayBuffers": 2062063, + "maxRssKiB": 164672 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 168656896, + "heapTotal": 107429888, + "heapUsed": 37188896, + "external": 11904341, + "arrayBuffers": 2070255, + "maxRssKiB": 164704 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV2 build replaces the browser page", + "gc": true, + "rss": 168787968, + "heapTotal": 108216320, + "heapUsed": 38250968, + "external": 11904310, + "arrayBuffers": 2070255, + "maxRssKiB": 164832 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override edge MV3 build replaces the browser page", + "gc": true, + "rss": 169017344, + "heapTotal": 109264896, + "heapUsed": 39298824, + "external": 11912531, + "arrayBuffers": 2070255, + "maxRssKiB": 165056 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 169738240, + "heapTotal": 110772224, + "heapUsed": 40536656, + "external": 11903676, + "arrayBuffers": 2070255, + "maxRssKiB": 165760 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 170344448, + "heapTotal": 112345088, + "heapUsed": 41572728, + "external": 11911868, + "arrayBuffers": 2070255, + "maxRssKiB": 166352 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 170917888, + "heapTotal": 113393664, + "heapUsed": 42597720, + "external": 11903520, + "arrayBuffers": 2070255, + "maxRssKiB": 166912 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 171491328, + "heapTotal": 114442240, + "heapUsed": 43635040, + "external": 11911712, + "arrayBuffers": 2070255, + "maxRssKiB": 167472 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 171868160, + "heapTotal": 115490816, + "heapUsed": 44695944, + "external": 11903519, + "arrayBuffers": 2070255, + "maxRssKiB": 167840 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/bookmarks.integration.test.ts", + "test": "bookmarks override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 172359680, + "heapTotal": 117063680, + "heapUsed": 45729120, + "external": 11911713, + "arrayBuffers": 2070255, + "maxRssKiB": 168320 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 187908096, + "heapTotal": 119750656, + "heapUsed": 41462632, + "external": 13947596, + "arrayBuffers": 2078454, + "maxRssKiB": 183632 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 188694528, + "heapTotal": 114507776, + "heapUsed": 42464168, + "external": 11920705, + "arrayBuffers": 2086646, + "maxRssKiB": 184272 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV2 build replaces the browser page", + "gc": true, + "rss": 188874752, + "heapTotal": 115294208, + "heapUsed": 43502088, + "external": 11920674, + "arrayBuffers": 2086646, + "maxRssKiB": 184448 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override edge MV3 build replaces the browser page", + "gc": true, + "rss": 188973056, + "heapTotal": 116342784, + "heapUsed": 44552216, + "external": 11928895, + "arrayBuffers": 2086646, + "maxRssKiB": 184544 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV2 build replaces the browser page", + "gc": true, + "rss": 189005824, + "heapTotal": 117391360, + "heapUsed": 45599664, + "external": 11920832, + "arrayBuffers": 2086646, + "maxRssKiB": 184576 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override firefox MV3 build replaces the browser page", + "gc": true, + "rss": 189005824, + "heapTotal": 118439936, + "heapUsed": 46626192, + "external": 11929053, + "arrayBuffers": 2086646, + "maxRssKiB": 184576 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV2 build replaces the browser page", + "gc": true, + "rss": 189005824, + "heapTotal": 119226368, + "heapUsed": 47643752, + "external": 11928868, + "arrayBuffers": 2086646, + "maxRssKiB": 184576 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override safari MV3 build replaces the browser page", + "gc": true, + "rss": 189087744, + "heapTotal": 120537088, + "heapUsed": 48667080, + "external": 11920705, + "arrayBuffers": 2086646, + "maxRssKiB": 184656 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 189136896, + "heapTotal": 121585664, + "heapUsed": 49704736, + "external": 11919900, + "arrayBuffers": 2086646, + "maxRssKiB": 184704 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/newtab.integration.test.ts", + "test": "newtab override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 189317120, + "heapTotal": 122634240, + "heapUsed": 50732408, + "external": 11928094, + "arrayBuffers": 2086646, + "maxRssKiB": 184880 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV2 build replaces the browser page", + "gc": true, + "rss": 203587584, + "heapTotal": 122109952, + "heapUsed": 41591992, + "external": 13972186, + "arrayBuffers": 2086646, + "maxRssKiB": 199392 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override chrome MV3 build replaces the browser page", + "gc": true, + "rss": 208355328, + "heapTotal": 181354496, + "heapUsed": 42616960, + "external": 11928904, + "arrayBuffers": 2094838, + "maxRssKiB": 203472 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV2 build replaces the browser page", + "gc": true, + "rss": 208453632, + "heapTotal": 181878784, + "heapUsed": 43657728, + "external": 11928873, + "arrayBuffers": 2094838, + "maxRssKiB": 203600 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override edge MV3 build replaces the browser page", + "gc": true, + "rss": 208699392, + "heapTotal": 182927360, + "heapUsed": 44705576, + "external": 11937094, + "arrayBuffers": 2094838, + "maxRssKiB": 203808 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV2 build skips the unsupported page", + "gc": true, + "rss": 208863232, + "heapTotal": 183975936, + "heapUsed": 45743168, + "external": 11928251, + "arrayBuffers": 2094838, + "maxRssKiB": 203968 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override firefox MV3 build skips the unsupported page", + "gc": true, + "rss": 209272832, + "heapTotal": 185286656, + "heapUsed": 46778336, + "external": 11936443, + "arrayBuffers": 2094838, + "maxRssKiB": 204368 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV2 build skips the unsupported page", + "gc": true, + "rss": 209633280, + "heapTotal": 186335232, + "heapUsed": 47795272, + "external": 11928095, + "arrayBuffers": 2094838, + "maxRssKiB": 204720 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override safari MV3 build skips the unsupported page", + "gc": true, + "rss": 210190336, + "heapTotal": 187121664, + "heapUsed": 48829448, + "external": 11936287, + "arrayBuffers": 2094838, + "maxRssKiB": 205264 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV2 build skips the unsupported page", + "gc": true, + "rss": 210567168, + "heapTotal": 188432384, + "heapUsed": 49877992, + "external": 11928094, + "arrayBuffers": 2094838, + "maxRssKiB": 205632 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/history.integration.test.ts", + "test": "history override opera MV3 build skips the unsupported page", + "gc": true, + "rss": 211025920, + "heapTotal": 189218816, + "heapUsed": 50906872, + "external": 11936288, + "arrayBuffers": 2094838, + "maxRssKiB": 206080 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides chrome build fails and names both entrypoints", + "gc": true, + "rss": 217792512, + "heapTotal": 189480960, + "heapUsed": 41897952, + "external": 13972220, + "arrayBuffers": 2078447, + "maxRssKiB": 213264 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides edge build fails and names both entrypoints", + "gc": true, + "rss": 217808896, + "heapTotal": 181354496, + "heapUsed": 42905240, + "external": 11921330, + "arrayBuffers": 2086639, + "maxRssKiB": 213264 + }, + { + "pid": 99625, + "worker": "1", + "file": "tests/integration/build/override/competing.integration.test.ts", + "test": "competing overrides firefox build keeps the new tab and its permission without the unsupported history page", + "gc": true, + "rss": 217808896, + "heapTotal": 182403072, + "heapUsed": 43935984, + "external": 11921870, + "arrayBuffers": 2086639, + "maxRssKiB": 213264 + } + ] + } + ] +} diff --git a/tests/benchmarks/2026-09-28-override-session.md b/tests/benchmarks/2026-09-28-override-session.md new file mode 100644 index 00000000..1e1bccc2 --- /dev/null +++ b/tests/benchmarks/2026-09-28-override-session.md @@ -0,0 +1,83 @@ +# Override build-session pilot — 2026-09-28 + +## Decision and scope + +Keep CLI as the default and expose the new pilot through `ADNBN_OVERRIDE_BUILD_MODE=session`. +The previous Jest-worker in-process backend is removed. Production `rootDir` fixes remain, but the +project-hashed virtual-directory helper and its entrypoint/offscreen connections are removed. +No native virtual-module migration, new production concurrency contract, or global worker tuning is included. + +Each of the three page files owns one child process with a private fixed cwd and ten sequential builds. +The child imports the built package once and receives absolute fixture roots. The parent never changes its +cwd, native environment, or module loader. All three competing-entrypoint scenarios still invoke the CLI. +Each request has a parent-owned deadline; a timeout kills the child and rejects only after `close`, without +retrying. Errors preserve their message, cause chain and compiler diagnostics. Normal disposal sends a close +request and waits for process/stdio closure before removing the session directory. IPC disconnect exits the +child even if a build is pending. A hard owner crash can leave files for later cleanup, but not a cooperative +build process waiting indefinitely for its owner. + +The shared-cwd conflict in the external virtual-module plugin remains a documented limitation. Reproduce it +with `node tests/benchmarks/virtual-module-conflict.mjs` after building the package. The diagnostic controls +both compilers with IPC and expects the second to fail after the first removes its shared virtual files. + +## Measurements + +Apple M3 Max, 16 logical CPUs, macOS arm64, Node 24.5.0, Rspack 1.7.11. Fresh package build before the series; +no other build or test run during timing. Each worker limit has one warm-up per backend and three alternating +measured pairs. Both backends use the same GC-enabled instrumentation and macOS `--no-sparkplug` setting. +Times cover the full Jest process, including session startup/disposal, and exclude building the framework. +These are instrumented local observations, not CI performance evidence. + +| Worker limit | CLI median (range) | Session median (range) | Ratio | +| ------------ | --------------------- | ---------------------- | ----- | +| 2 | 12.94 s (12.86–13.10) | 3.53 s (3.51–3.65) | 3.67× | +| 8 | 6.87 s (6.82–7.14) | 2.35 s (2.33–2.37) | 2.92× | + +All 18 runs pass the same 33 unique full scenario names. Those names also match the raw results of the previous +pilot. Each session run records exactly three child PIDs and ten responses per child. With three session-backed +files the eight-worker setting starts at most three session children; it does not model eight concurrent sessions. + +## Child memory and cleanup + +The figures below are measured in the build child after each build, with forced GC, not in the Jest worker. +Across the measured series, after build ten: + +- RSS: 341.36–394.23 MiB per child. +- JavaScript heap: 75.72–79.70 MiB per child. +- Highest reported process peak RSS across the measured samples: 394.36 MiB. + +The process exits at the file boundary, so retained compiler memory does not follow a Jest worker into its next +file. This bounds the number of builds per child, not peak bytes, and does not repair Rspack retention. More +simultaneous files would increase aggregate memory. CLI child memory is not instrumented, so these values must +not be read as a total-memory comparison between backends. + +Every run verifies zero leftover fixture/session directories and that all reported session PIDs have exited. +Separate `--detectOpenHandles` runs for both backends pass all 33 cases and report zero open handles. Directories +left by deliberately interrupted development diagnostics were removed before the benchmark; they are not part +of the measured cleanup results. + +## Validation + +- Full `npm run check`: package build, framework typecheck, fixture builds/typechecks, 184 suites / 2008 tests. + This preceded one additional pending-disposal regression; the final session file passes all eight tests. +- Final Node 24.5.0 session lifecycle tests: 8/8. +- Local Node 22.18.0: all 33 override cases, two absolute-root cases and eight session lifecycle cases pass (43/43). +- Chrome/Firefox: 31 suites / 42 tests pass on two workers. +- Inventory: 215 files, each in exactly one project; no local module mocks. +- The lifecycle cases cover fixed-cwd A → B → A, overlap rejection, config causes, compiler diagnostics, + recovery, timeout, unexpected exit, disposal while pending, and IPC disconnect both idle and during a build. +- Linux/Windows CI remains unverified; local Node 22/24 checks do not replace that matrix. + +## Reproduction + +```bash +npm run build +node tests/benchmarks/override-pilot.mjs +``` + +The runner writes logs, parent/child JSONL and Jest output under `.cache/benchmarks/override-session`. It refuses +to start with unaccounted fixture/session directories. Parent instrumentation also checks that environment values, +object identity and cwd are unchanged after each test. No global memory limit or worker-count change is applied. + +[Raw results](2026-09-28-override-session.json) include scenario names and all child samples. +The [previous report](2026-09-28-override-pilot.md) records the superseded backend, not current behavior. diff --git a/tests/benchmarks/2026-09-28-rspack-retention.json b/tests/benchmarks/2026-09-28-rspack-retention.json new file mode 100644 index 00000000..5129a525 --- /dev/null +++ b/tests/benchmarks/2026-09-28-rspack-retention.json @@ -0,0 +1,105 @@ +{ + "node": "v24.5.0", + "platform": "darwin", + "arch": "arm64", + "rspack": "1.7.11", + "iterations": 12, + "results": [ + { + "variant": "baseline", + "alive": 0, + "heapUsed": 8744624, + "rss": 147259392, + "external": 2006085, + "arrayBuffers": 11656 + }, + { + "variant": "rule-test-regexp", + "alive": 0, + "heapUsed": 8922544, + "rss": 164577280, + "external": 2014277, + "arrayBuffers": 19848 + }, + { + "variant": "rule-test-function", + "alive": 12, + "heapUsed": 14572240, + "rss": 163151872, + "external": 2014277, + "arrayBuffers": 19848 + }, + { + "variant": "rule-include-function", + "alive": 0, + "heapUsed": 8763512, + "rss": 148520960, + "external": 2006085, + "arrayBuffers": 11656 + }, + { + "variant": "rule-use-function", + "alive": 12, + "heapUsed": 14569976, + "rss": 157761536, + "external": 2014277, + "arrayBuffers": 19848 + }, + { + "variant": "rule-query-function", + "alive": 12, + "heapUsed": 14571880, + "rss": 164724736, + "external": 2014277, + "arrayBuffers": 19848 + }, + { + "variant": "cache-group-regexp", + "alive": 0, + "heapUsed": 8746896, + "rss": 150061056, + "external": 2007212, + "arrayBuffers": 12783 + }, + { + "variant": "cache-group-function", + "alive": 12, + "heapUsed": 14248744, + "rss": 153731072, + "external": 2007213, + "arrayBuffers": 12784 + }, + { + "variant": "html-file", + "alive": 0, + "heapUsed": 8770376, + "rss": 152862720, + "external": 2006085, + "arrayBuffers": 11656 + }, + { + "variant": "html-string", + "alive": 0, + "heapUsed": 8768248, + "rss": 149749760, + "external": 2006085, + "arrayBuffers": 11656 + }, + { + "variant": "html-content-function", + "alive": 12, + "heapUsed": 11720416, + "rss": 151650304, + "external": 2006085, + "arrayBuffers": 11656 + }, + { + "variant": "html-parameters-function", + "alive": 12, + "heapUsed": 11718440, + "rss": 154468352, + "external": 2006085, + "arrayBuffers": 11656 + } + ] +} diff --git a/tests/benchmarks/override-pilot-metrics.cjs b/tests/benchmarks/override-pilot-metrics.cjs new file mode 100644 index 00000000..61eb134f --- /dev/null +++ b/tests/benchmarks/override-pilot-metrics.cjs @@ -0,0 +1,32 @@ +const fs = require("node:fs"); +const assert = require("node:assert/strict"); +const nodeProcess = process.getBuiltinModule("process"); +const environment = nodeProcess.env; +const values = {...environment}; +const testValues = {...process.env}; +const cwd = nodeProcess.cwd(); + +afterEach(() => { + // Registered after the harness cleanup: both environment layers must be back to their initial state. + assert.equal(nodeProcess.env, environment); + assert.deepEqual({...nodeProcess.env}, values); + assert.deepEqual({...process.env}, testValues); + assert.equal(nodeProcess.cwd(), cwd); + + if (global.gc) { + global.gc(); + } + + fs.appendFileSync( + process.env.ADNBN_PILOT_METRICS, + JSON.stringify({ + pid: process.pid, + worker: process.env.JEST_WORKER_ID, + file: expect.getState().testPath, + test: expect.getState().currentTestName, + gc: typeof global.gc === "function", + ...process.memoryUsage(), + maxRssKiB: process.resourceUsage().maxRSS, + }) + "\n" + ); +}); diff --git a/tests/benchmarks/override-pilot.mjs b/tests/benchmarks/override-pilot.mjs new file mode 100644 index 00000000..9d86c6e0 --- /dev/null +++ b/tests/benchmarks/override-pilot.mjs @@ -0,0 +1,133 @@ +import assert from "node:assert/strict"; +import {spawnSync} from "node:child_process"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import {performance} from "node:perf_hooks"; + +const root = path.resolve(import.meta.dirname, "../.."); +const output = path.join(root, ".cache/benchmarks/override-session"); +fs.mkdirSync(output, {recursive: true}); +const flags = ["--expose-gc", ...(process.platform === "darwin" && process.arch === "arm64" ? ["--no-sparkplug"] : [])]; +const report = { + node: process.version, + platform: process.platform, + arch: process.arch, + cpus: os.cpus().length, + flags, + runs: [], +}; +let expectedNames; + +const leftovers = () => + fs.readdirSync(path.join(root, ".cache/integration")).filter(name => /^(fixture|session)-/.test(name)); +assert.deepEqual(leftovers(), [], "Account for existing test directories before running the benchmark"); + +const readSamples = filename => fs.readFileSync(filename, "utf8").trim().split("\n").filter(Boolean).map(JSON.parse); +const run = (mode, workers, phase, extra = []) => { + const id = `${report.runs.length + 1}-${phase}-${workers}-${mode}`; + const metrics = path.join(output, `${id}.jsonl`); + const childMetrics = path.join(output, `${id}-children.jsonl`); + const resultPath = path.join(output, `${id}.json`); + const log = path.join(output, `${id}.log`); + fs.writeFileSync(metrics, ""); + fs.writeFileSync(childMetrics, ""); + const started = performance.now(); + const result = spawnSync( + process.execPath, + [ + ...flags, + "node_modules/jest/bin/jest.js", + "--selectProjects", + "build", + "--testPathPatterns=tests/integration/build/override", + `--maxWorkers=${workers}`, + "--logHeapUsage", + "--json", + `--outputFile=${resultPath}`, + "--setupFilesAfterEnv", + path.join(root, "tests/jest.setup.ts"), + path.join(import.meta.dirname, "override-pilot-metrics.cjs"), + ...extra, + ], + { + cwd: root, + env: { + ...process.env, + ADNBN_OVERRIDE_BUILD_MODE: mode, + ADNBN_PILOT_METRICS: metrics, + ADNBN_SESSION_METRICS: childMetrics, + }, + encoding: "utf8", + timeout: 180_000, + maxBuffer: 8 * 1024 * 1024, + } + ); + const wallSeconds = (performance.now() - started) / 1000; + fs.writeFileSync(log, result.stdout + result.stderr); + + if (result.error || result.status !== 0) { + throw new Error(`${id} failed: ${result.error ?? result.status}; see ${log}`); + } + + const data = JSON.parse(fs.readFileSync(resultPath, "utf8")); + const names = data.testResults.flatMap(file => file.assertionResults.map(test => test.fullName)).sort(); + expectedNames ??= names; + assert.equal(names.length, 33); + assert.equal(new Set(names).size, 33); + assert.deepEqual(names, expectedNames); + assert.deepEqual(leftovers(), []); + const children = readSamples(childMetrics); + const pids = [...new Set(children.map(sample => sample.pid))]; + + if (mode === "session") { + assert.equal(children.length, 30); + assert.equal(pids.length, 3); + + for (const pid of pids) { + assert.deepEqual( + children.filter(sample => sample.pid === pid).map(sample => sample.build), + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + ); + assert.throws(() => process.kill(pid, 0), {code: "ESRCH"}); + } + } + + report.runs.push({ + id, + phase, + mode, + workers, + wallSeconds, + names, + tests: data.numPassedTests, + leftovers: leftovers().length, + openHandles: data.openHandles?.length ?? null, + children: children.map(sample => ({ + ...sample, + rootDir: path.relative(root, sample.rootDir), + cwd: path.relative(root, sample.cwd), + })), + parentSamples: readSamples(metrics).map(sample => ({...sample, file: path.relative(root, sample.file)})), + }); + fs.writeFileSync(path.join(output, "results.json"), JSON.stringify(report, null, 2) + "\n"); + console.log( + `${id}: ${wallSeconds.toFixed(2)} s; 33 tests, ${pids.length} session children, no leftover processes/directories` + ); +}; + +for (const workers of [2, 8]) { + for (const mode of ["cli", "session"]) { + run(mode, workers, "warmup"); + } + + for (let round = 0; round < 3; round++) { + for (const mode of round % 2 ? ["session", "cli"] : ["cli", "session"]) { + run(mode, workers, "measured"); + } + } +} + +for (const mode of ["cli", "session"]) { + run(mode, 1, "handles", ["--detectOpenHandles"]); +} diff --git a/tests/benchmarks/rspack-retention.mjs b/tests/benchmarks/rspack-retention.mjs new file mode 100644 index 00000000..eeca0163 --- /dev/null +++ b/tests/benchmarks/rspack-retention.mjs @@ -0,0 +1,183 @@ +import assert from "node:assert/strict"; +import {spawnSync} from "node:child_process"; +import {mkdtemp, readFile, rm, writeFile} from "node:fs/promises"; +import {createRequire} from "node:module"; +import os from "node:os"; +import path from "node:path"; +import {setImmediate} from "node:timers/promises"; +import {fileURLToPath} from "node:url"; +import {HtmlRspackPlugin, rspack} from "@rspack/core"; + +// Each variant runs in a fresh process. No framework code, Jest, or retained Stats. +const variants = [ + "baseline", + "rule-test-regexp", + "rule-test-function", + "rule-include-function", + "rule-use-function", + "rule-query-function", + "cache-group-regexp", + "cache-group-function", + "html-file", + "html-string", + "html-content-function", + "html-parameters-function", +]; +const [variant] = process.argv.slice(2); +const iterations = 12; + +if (!variant) { + const results = variants.map(name => { + const child = spawnSync(process.execPath, ["--expose-gc", fileURLToPath(import.meta.url), name], { + encoding: "utf8", + timeout: 60_000, + }); + assert.equal(child.error, undefined); + assert.equal(child.status, 0, child.stderr); + + return JSON.parse(child.stdout); + }); + const require = createRequire(import.meta.url); + console.log( + JSON.stringify( + { + node: process.version, + platform: process.platform, + arch: process.arch, + rspack: require("@rspack/core/package.json").version, + iterations, + results, + }, + null, + 2 + ) + ); +} else { + assert.ok(variants.includes(variant), `Unknown variant: ${variant}`); + assert.equal(typeof global.gc, "function", "Run a single variant with node --expose-gc"); + const directory = await mkdtemp(path.join(os.tmpdir(), "rspack-retention-")); + const references = []; + + try { + const imports = []; + + for (let index = 0; index < 24; index++) { + await writeFile(path.join(directory, `module-${index}.js`), `export default ${index};\n`); + imports.push(`import value${index} from "./module-${index}.js"; console.log(value${index});`); + } + + await writeFile(path.join(directory, "entry.js"), imports.join("\n")); + await writeFile(path.join(directory, "loader.cjs"), "module.exports = source => source;\n"); + await writeFile(path.join(directory, "template.html"), "Retention probe"); + + const configure = () => { + const config = { + mode: "production", + context: directory, + entry: path.join(directory, "entry.js"), + devtool: false, + output: {path: path.join(directory, "dist"), filename: "[name].js"}, + module: {rules: []}, + optimization: {}, + plugins: [], + }; + const use = [path.join(directory, "loader.cjs")]; + + if (variant === "rule-test-regexp") { + config.module.rules.push({test: /\.js$/, use}); + } else if (variant === "rule-test-function") { + config.module.rules.push({test: resource => resource.endsWith(".js"), use}); + } else if (variant === "rule-include-function") { + config.module.rules.push({include: resource => resource.startsWith(directory), use}); + } else if (variant === "rule-use-function") { + config.module.rules.push({test: /\.js$/, use: () => use}); + } else if (variant === "rule-query-function") { + config.module.rules.push({resourceQuery: query => query === "", use}); + } else if (variant.startsWith("cache-group-")) { + config.optimization.splitChunks = { + chunks: "all", + cacheGroups: { + probe: { + test: + variant === "cache-group-regexp" + ? /module-\d+\.js$/ + : module => /module-\d+\.js$/.test(module.resource ?? ""), + name: "probe", + minSize: 0, + }, + }, + }; + } else if (variant === "html-file") { + config.plugins.push(new HtmlRspackPlugin({template: path.join(directory, "template.html")})); + } else if (variant === "html-string") { + config.plugins.push( + new HtmlRspackPlugin({templateContent: "Retention probe"}) + ); + } else if (variant === "html-content-function") { + config.plugins.push( + new HtmlRspackPlugin({templateContent: () => "Retention probe"}) + ); + } else if (variant === "html-parameters-function") { + config.plugins.push( + new HtmlRspackPlugin({ + template: path.join(directory, "template.html"), + templateParameters: params => params, + }) + ); + } + + return config; + }; + const build = async () => { + const compiler = rspack(configure()); + references.push(new WeakRef(compiler)); + + try { + await new Promise((resolve, reject) => { + compiler.run((error, stats) => { + if (error || !stats || stats.hasErrors()) { + reject(error ?? new Error(stats?.toString({all: false, errors: true}) ?? "No stats")); + } else { + resolve(); + } + }); + }); + } finally { + await new Promise((resolve, reject) => { + compiler.close(error => { + if (error) { + reject(error); + } else { + resolve(); + } + }); + }); + } + }; + + for (let index = 0; index < iterations; index++) { + await build(); + } + + // Cross event-loop turns before collecting; do not dereference between collections. + for (let index = 0; index < 4; index++) { + await setImmediate(); + global.gc(); + } + + await readFile(path.join(directory, "dist/main.js")); + const memory = process.memoryUsage(); + console.log( + JSON.stringify({ + variant, + alive: references.filter(reference => reference.deref()).length, + heapUsed: memory.heapUsed, + rss: memory.rss, + external: memory.external, + arrayBuffers: memory.arrayBuffers, + }) + ); + } finally { + await rm(directory, {recursive: true, force: true, maxRetries: 5, retryDelay: 200}); + } +} diff --git a/tests/benchmarks/virtual-module-conflict.mjs b/tests/benchmarks/virtual-module-conflict.mjs new file mode 100644 index 00000000..a456dbde --- /dev/null +++ b/tests/benchmarks/virtual-module-conflict.mjs @@ -0,0 +1,74 @@ +import assert from "node:assert/strict"; +import {fork} from "node:child_process"; +import {mkdirSync, mkdtempSync, rmSync} from "node:fs"; +import os from "node:os"; +import path from "node:path"; + +const reproduce = async () => { + const directory = mkdtempSync(path.join(os.tmpdir(), "adnbn-root-dir-parallel-")); + const projectRoot = path.resolve(import.meta.dirname, "../.."); + const sharedCwd = path.join(directory, "caller"); + mkdirSync(path.join(sharedCwd, "node_modules", "entrypoint"), {recursive: true}); + mkdirSync(path.join(sharedCwd, "node_modules", "virtual")); + + const start = name => { + const root = path.join(directory, name); + mkdirSync(root); + const child = fork( + path.join(projectRoot, "tests/integration/build/cli/scripts/verify-root-dir.mjs"), + [projectRoot, root, "parallel", sharedCwd], + { + execArgv: [], + stdio: ["ignore", "pipe", "pipe", "ipc"], + timeout: 60_000, + killSignal: "SIGKILL", + } + ); + let stderr = ""; + child.stdout.resume(); + child.stderr.on("data", data => { + stderr += data; + }); + const completed = new Promise(resolve => + child.once("close", (code, signal) => resolve({code, signal, stderr})) + ); + const ready = new Promise((resolve, reject) => { + child.once("message", message => { + if (message === "ready") { + resolve(); + } else { + reject(new Error(`Unexpected build message: ${message}`)); + } + }); + child.once("error", reject); + child.once("close", () => reject(new Error(`Build stopped before ready: ${stderr}`))); + }); + + return {child, ready, completed}; + }; + const builds = [start("first"), start("second")]; + + try { + await Promise.all(builds.map(build => build.ready)); + // Both compilers have installed their virtual modules. Finish and close one + // before the other's compilation starts, so shared-directory deletion is observable. + builds[0].child.send("build"); + assert.deepEqual(await builds[0].completed, {code: 0, signal: null, stderr: ""}); + builds[1].child.send("build"); + const failed = await builds[1].completed; + assert.notEqual(failed.code, 0); + assert.match(failed.stderr, /Cannot find module|Module not found/); + console.log("Confirmed external-plugin limitation: closing A removes B virtual modules"); + } finally { + for (const {child} of builds) { + if (child.exitCode === null && child.signalCode === null) { + child.kill("SIGKILL"); + } + } + + await Promise.all(builds.map(build => build.completed)); + rmSync(directory, {recursive: true, force: true, maxRetries: 5, retryDelay: 200}); + } +}; + +await reproduce(); diff --git a/tests/browser-harness-dom.test.ts b/tests/browser-harness-dom.test.ts new file mode 100644 index 00000000..b5aa93e6 --- /dev/null +++ b/tests/browser-harness-dom.test.ts @@ -0,0 +1,26 @@ +import {getManifest} from "@addon-core/browser"; + +import {getBrowserTest} from "./browser-harness/session"; + +const originalWindow = window; +const originalDocument = document; +const originalLocation = location; +const originalNavigator = navigator; + +test("preserves jsdom identities through context changes and teardown", async () => { + const session = getBrowserTest(); + const assertDom = () => { + expect(window).toBe(originalWindow); + expect(document).toBe(originalDocument); + expect(location).toBe(originalLocation); + expect(navigator).toBe(originalNavigator); + }; + + assertDom(); + expect(jest.isMockFunction(getManifest)).toBe(false); + expect(getManifest()).toEqual(session.harness.runtime.manifest); + session.useContext(session.harness.contexts.create({kind: "background"})); + assertDom(); + await session.dispose(); + assertDom(); +}); diff --git a/tests/browser-harness.test.ts b/tests/browser-harness.test.ts new file mode 100644 index 00000000..f9c4d491 --- /dev/null +++ b/tests/browser-harness.test.ts @@ -0,0 +1,290 @@ +jest.mock("#adnbn/locale", () => require("../src/locale/providers/tests/fixtures/dynamic")); + +import {executeScript, getBrowserInfo, getManifest, sendMessage, sendTabMessage} from "@addon-core/browser"; +import {createTabFixture} from "@addon-core/browser/testing"; +import {Storage} from "@addon-core/storage"; + +import NativeLocale from "@locale/providers/NativeLocale"; +import {ObservableLocale} from "@locale/observers"; +import {MemoryLocaleStorage} from "@locale/tests/fixtures"; +import OffscreenBridge from "@offscreen/OffscreenBridge"; +import SandboxMessage from "@sandbox/SandboxMessage"; +import {getApp, getBrowser, getManifestVersion, isBrowser} from "@main/env"; +import {Browser} from "@typing/browser"; +import MessageManager from "@message/MessageManager"; +import Message from "@message/providers/Message"; +import RelayManager from "@relay/RelayManager"; +import {MessageGlobalKey} from "@typing/message"; +import {RelayGlobalKey} from "@typing/relay"; + +import BrowserTestSession from "./browser-harness/BrowserTestSession"; +import {getBrowserTest, stopBrowserTest} from "./browser-harness/session"; + +const hostSetTimeout = setTimeout; +const initialBrowser = process.env.BROWSER; +const initialChrome = Object.getOwnPropertyDescriptor(globalThis, "chrome"); + +test.each([1, 2])("starts test %i with fresh API state, call history and framework singletons", async () => { + const {harness} = getBrowserTest(); + + expect(harness.calls).toEqual([]); + expect(harness.storage.local.data).toEqual({}); + expect(globalThis[MessageGlobalKey]).toBeUndefined(); + expect(globalThis[RelayGlobalKey]).toBeUndefined(); + expect(jest.isMockFunction(getManifest)).toBe(false); + expect(jest.isMockFunction(Storage.Local)).toBe(false); + + await harness.chrome.storage.local.set({probe: 42}); + MessageManager.getInstance(); + RelayManager.getInstance(); + + expect(harness.calls.length).toBeGreaterThan(0); +}); + +test("reads through the real Storage package and preserves browser failures", async () => { + const {harness} = getBrowserTest(); + const storage = Storage.Local<{locale: string}>({namespace: "adnbn"}); + + await expect(storage.get("locale")).resolves.toBeUndefined(); + harness.storage.local.get.failNext(new Error("Storage denied")); + await expect(storage.get("locale")).rejects.toThrow("Storage denied"); +}); + +test("registers real Message listeners in one context and restores the caller context", async () => { + const session = getBrowserTest(); + const receiver = session.harness.contexts.create({kind: "background"}); + const restore = session.useContext(receiver); + const message = new Message<{probe: () => number}>(); + + message.watch("probe", () => 42); + restore(); + + await expect(message.send("probe", undefined)).resolves.toBe(42); + await session.dispose(); + expect(receiver.onMessage.listenerCount()).toBe(0); + expect(globalThis[MessageGlobalKey]).toBeUndefined(); +}); + +test("disposes pending guest execution, listeners, framework caches and restores global descriptors", async () => { + const session = getBrowserTest(); + + await session.dispose(); + + const previousChrome = Object.getOwnPropertyDescriptor(globalThis, "chrome"); + const current = new BrowserTestSession({ + tabs: [createTabFixture({id: 7})], + documents: [{documentId: "main", tabId: 7, url: "https://example.test/"}], + }); + + try { + const runtime = current.createScriptRuntime({clock: true}); + const pending = executeScript({ + target: {tabId: 7}, + func: () => new Promise(resolve => setTimeout(() => resolve("late"), 300)), + }); + const rejected = expect(pending).rejects.toThrow(/disposed|cancelled by reset/); + const manager = RelayManager.getInstance(); + + manager.add("probe", {value: 42}); + current.harness.configurable.chrome.i18n.getMessage.setResult("en"); + + const locale = NativeLocale.getInstance(); + const message = Message.getInstance(); + + await current.dispose(); + await rejected; + + expect(runtime.realmCount).toBe(0); + expect(runtime.pendingExecutions).toBe(0); + expect(() => runtime.clock!.advance(300)).toThrow(/disposed/); + expect(manager.has("probe")).toBe(false); + expect(Object.getOwnPropertyDescriptor(globalThis, "chrome")).toEqual(previousChrome); + + const next = new BrowserTestSession(); + + try { + next.harness.configurable.chrome.i18n.getMessage.setResult("fr"); + expect(NativeLocale.getInstance()).not.toBe(locale); + expect(NativeLocale.getInstance().lang()).toBe("fr"); + expect(Message.getInstance()).not.toBe(message); + expect(RelayManager.getInstance()).not.toBe(manager); + } finally { + await next.dispose(); + } + } finally { + await current.dispose(); + } +}); + +test("continues teardown after a consumer cleanup fails and rejects pending message channels", async () => { + const session = getBrowserTest(); + const receiver = session.harness.contexts.create({kind: "background"}); + const cleaned = jest.fn(); + + receiver.onMessage.on(() => true); + + const pending = expect(sendMessage("probe")).rejects.toThrow(); + + session.addCleanup(cleaned); + session.addCleanup(() => { + throw new Error("Consumer cleanup failed"); + }); + + await expect(stopBrowserTest()).rejects.toThrow("Browser test cleanup failed"); + await pending; + expect(cleaned).toHaveBeenCalledTimes(1); + expect(session.harness.contexts.list()).toEqual([]); +}); + +test("keeps Firefox profile and runtime environment consistent and restores all environment values", async () => { + const session = getBrowserTest(); + const original = [process.env.BROWSER, process.env.APP, process.env.MANIFEST_VERSION]; + const restore = session.useContext(session.context, "firefox"); + + session.harness.runtime.getBrowserInfo.setResult({ + name: "Firefox", + vendor: "Mozilla", + version: "153.0", + buildID: "test", + }); + + expect(isBrowser(Browser.Firefox)).toBe(true); + await expect(getBrowserInfo()).resolves.toMatchObject({version: "153.0"}); + restore(); + expect(getBrowser()).toBe(Browser.Chrome); + + const nested = new BrowserTestSession({ + profile: "firefox", + app: "fixture", + manifest: {manifest_version: 2, name: "Fixture", version: "1.0.0"}, + }); + + try { + expect(getBrowser()).toBe(Browser.Firefox); + expect(getApp()).toBe("fixture"); + expect(getManifestVersion()).toBe(2); + process.env.APP = "changed-during-test"; + expect(getApp()).toBe("changed-during-test"); + process.env.MANIFEST_VERSION = "3"; + expect(getManifestVersion()).toBe(3); + } finally { + await nested.dispose(); + } + + expect([process.env.BROWSER, process.env.APP, process.env.MANIFEST_VERSION]).toEqual(original); +}); + +test("delivers runtime and tab messages through bound endpoints without advancing host fake timers", async () => { + jest.useFakeTimers(); + + const {harness} = getBrowserTest(); + + harness.tabs.set([createTabFixture({id: 7})]); + + const worker = harness.contexts.create({kind: "background"}); + const content = harness.contexts.create({ + kind: "contentScript", + tabId: 7, + frameId: 0, + url: "https://example.test/", + }); + const endpoint = harness.messaging.forContext(worker); + + endpoint.chrome.runtime.onMessage.addListener((_message, _sender, reply) => { + void Promise.resolve().then(() => reply("runtime response")); + + return true; + }); + content.onMessage.on((_message, _sender, reply) => reply("tab response")); + + await expect(sendMessage("probe")).resolves.toBe("runtime response"); + await expect(sendTabMessage(7, "probe", {frameId: 0})).resolves.toBe("tab response"); + expect(jest.getTimerCount()).toBe(0); + // Leave fake timers installed deliberately; shared teardown must restore them. +}); + +test("restores host timers before the next test", () => { + expect(setTimeout).toBe(hostSetTimeout); +}); + +test("advances guest clocks independently of frozen host time", async () => { + jest.useFakeTimers({now: 5000}); + + const session = getBrowserTest(); + + session.harness.tabs.set([createTabFixture({id: 7})]); + session.harness.contexts.documents.create({documentId: "clock", tabId: 7, url: "https://example.test/"}); + + const runtime = session.createScriptRuntime({clock: true}); + const pending = executeScript({ + target: {tabId: 7}, + func: () => new Promise(resolve => setTimeout(() => resolve(Date.now()), 300)), + }); + + runtime.clock!.advance(299); + expect(runtime.pendingExecutions).toBe(1); + runtime.clock!.advance(1); + await expect(pending).resolves.toEqual([expect.objectContaining({result: 300})]); + expect(Date.now()).toBe(5000); + expect(jest.getTimerCount()).toBe(0); +}); + +test("keeps download-validation delay on the host clock", async () => { + jest.useFakeTimers(); + + const {harness} = getBrowserTest(); + let completed = false; + const pending = harness.delays.downloadValidation.api(100).then(() => { + completed = true; + }); + + await Promise.resolve(); + expect(completed).toBe(false); + expect(jest.getTimerCount()).toBe(1); + await jest.advanceTimersByTimeAsync(100); + await pending; + expect(completed).toBe(true); +}); + +test("resets all ObservableLocale caches, OffscreenBridge and cached Sandbox hosts", async () => { + const driver = new MemoryLocaleStorage(); + const previous = [ + ObservableLocale.getInstance(), + ObservableLocale.getInstance(false), + ObservableLocale.getInstance(driver), + ]; + const bridge = OffscreenBridge.getInstance(); + const host = SandboxMessage.for("probe", {url: "sandbox.html"}); + + await getBrowserTest().dispose(); + + const next = new BrowserTestSession(); + + try { + const current = [ + ObservableLocale.getInstance(), + ObservableLocale.getInstance(false), + ObservableLocale.getInstance(driver), + ]; + + current.forEach((instance, index) => expect(instance).not.toBe(previous[index])); + expect(OffscreenBridge.getInstance()).not.toBe(bridge); + expect(SandboxMessage.for("probe", {url: "sandbox.html"})).not.toBe(host); + } finally { + await next.dispose(); + } +}); + +test("reports an unknown framework global without skipping global and environment restoration", async () => { + const unknownKey = "adnbnUnregisteredTestState"; + + Reflect.set(globalThis, unknownKey, {}); + + try { + await expect(stopBrowserTest()).rejects.toThrow("Browser test cleanup failed"); + expect(Object.getOwnPropertyDescriptor(globalThis, "chrome")).toEqual(initialChrome); + expect(process.env.BROWSER).toBe(initialBrowser); + } finally { + Reflect.deleteProperty(globalThis, unknownKey); + } +}); diff --git a/tests/browser-harness/BrowserTestSession.ts b/tests/browser-harness/BrowserTestSession.ts new file mode 100644 index 00000000..2f3fc751 --- /dev/null +++ b/tests/browser-harness/BrowserTestSession.ts @@ -0,0 +1,147 @@ +import { + createBrowserHarness, + installBrowserGlobals, + type BrowserContext, + type BrowserHarness, + type BrowserHarnessOptions, + type BrowserProfile, +} from "@addon-core/browser/testing"; +import { + createNodeScriptRuntime, + type NodeScriptRuntime, + type NodeScriptRuntimeOptions, +} from "@addon-core/browser/testing/node"; + +import {resetFrameworkState} from "./framework-state"; + +type BrowserTestCleanup = () => void | Promise; +type BrowserTestRuntimeOptions = Omit; +type BrowserTestProfile = Exclude; + +export interface BrowserTestSessionOptions extends BrowserHarnessOptions { + profile?: BrowserTestProfile; + app?: string; +} + +/** One test owns its globals, contexts, guest runtimes and consumer resources. */ +export default class BrowserTestSession { + public readonly harness: BrowserHarness; + public readonly context: BrowserContext; + + private readonly restoreGlobals: () => void; + private readonly restoreEnvironment: () => void; + private profile: BrowserTestProfile; + private readonly cleanups: BrowserTestCleanup[] = []; + private disposal?: Promise; + + public constructor({profile = "chrome", app = "test", ...options}: BrowserTestSessionOptions = {}) { + this.profile = profile; + const environment = ["BROWSER", "APP", "MANIFEST_VERSION"].map(key => [key, process.env[key]] as const); + + this.restoreEnvironment = () => { + for (const [key, value] of environment) { + if (value === undefined) { + delete process.env[key]; + } else { + process.env[key] = value; + } + } + }; + + this.harness = createBrowserHarness(options); + this.context = this.harness.contexts.create({kind: "extensionPage"}); + this.restoreGlobals = installBrowserGlobals(this.harness, { + environment: "preserve", + profile, + messageContext: this.context, + }); + process.env.BROWSER = profile; + process.env.APP = app; + process.env.MANIFEST_VERSION = String(this.harness.runtime.manifest.manifest_version); + } + + public addCleanup(cleanup: BrowserTestCleanup): void { + if (this.disposal) { + throw new Error("Browser test session is disposed."); + } + + this.cleanups.push(cleanup); + } + + /** Nested context installations must be restored in reverse order. */ + public useContext(context: BrowserContext, profile: BrowserTestProfile = this.profile): () => void { + if (this.disposal) { + throw new Error("Browser test session is disposed."); + } + + const previousProfile = this.profile; + const previousBrowser = process.env.BROWSER; + const restore = installBrowserGlobals(this.harness, { + environment: "preserve", + profile, + messageContext: context, + }); + + this.profile = profile; + process.env.BROWSER = profile; + + let restored = false; + const cleanup = () => { + if (!restored) { + restore(); + this.profile = previousProfile; + + if (previousBrowser === undefined) { + delete process.env.BROWSER; + } else { + process.env.BROWSER = previousBrowser; + } + + restored = true; + } + }; + + this.addCleanup(cleanup); + + return cleanup; + } + + public createScriptRuntime(options: BrowserTestRuntimeOptions = {}): NodeScriptRuntime { + if (this.disposal) { + throw new Error("Browser test session is disposed."); + } + + const runtime = createNodeScriptRuntime({...options, documents: this.harness.contexts.documents}); + + this.harness.scripting.setExecutor(runtime.executor); + this.addCleanup(() => runtime.dispose()); + + return runtime; + } + + public dispose(): Promise { + return (this.disposal ??= Promise.resolve().then(() => this.cleanup())); + } + + private async cleanup(): Promise { + const errors: unknown[] = []; + + for (const cleanup of [ + ...this.cleanups.reverse(), + resetFrameworkState, + () => this.harness.reset(), + this.restoreGlobals, + this.restoreEnvironment, + ]) { + try { + await cleanup(); + } catch (error) { + errors.push(error); + } + } + + if (errors.length > 0) { + throw new AggregateError(errors, "Browser test cleanup failed."); + } + } +} diff --git a/tests/browser-harness/framework-state-inventory.json b/tests/browser-harness/framework-state-inventory.json new file mode 100644 index 00000000..08a82647 --- /dev/null +++ b/tests/browser-harness/framework-state-inventory.json @@ -0,0 +1,82 @@ +{ + "TsResolver": { + "file": "src/cli/entrypoint/file/resolvers/TsResolver.ts", + "fields": ["instances"], + "getInstance": false, + "retained": "CLI TypeScript resolution cache; outside browser-session ownership." + }, + "BackgroundEntry": { + "file": "src/cli/plugins/background/BackgroundEntry.ts", + "fields": ["name"], + "getInstance": false, + "retained": "Static entrypoint name, not mutable session state." + }, + "NativeLocale": { + "file": "src/locale/providers/NativeLocale.ts", + "fields": ["instance"], + "getInstance": true + }, + "ObservableLocale": { + "file": "src/locale/observers/ObservableLocale.ts", + "fields": [ + "defaultInstance", + "memoryInstance", + "drivers" + ], + "getInstance": true + }, + "MessageManager": { + "file": "src/message/MessageManager.ts", + "fields": [], + "getInstance": true + }, + "Message": { + "file": "src/message/providers/Message.ts", + "fields": [ + "FirefoxDocumentIdMinVersion", + "instance" + ], + "getInstance": true + }, + "OffscreenBridge": { + "file": "src/offscreen/OffscreenBridge.ts", + "fields": ["instance"], + "getInstance": true + }, + "OffscreenManager": { + "file": "src/offscreen/OffscreenManager.ts", + "fields": [], + "getInstance": true + }, + "RelayManager": { + "file": "src/relay/RelayManager.ts", + "fields": [], + "getInstance": true + }, + "RelayPermission": { + "file": "src/relay/RelayPermission.ts", + "fields": [], + "getInstance": true + }, + "ProxyRelay": { + "file": "src/relay/providers/ProxyRelay.ts", + "fields": ["SelectorKeys"], + "getInstance": false, + "retained": "Static selector key list, not mutable session state." + }, + "SandboxManager": { + "file": "src/sandbox/SandboxManager.ts", + "fields": [], + "getInstance": true + }, + "SandboxMessage": { + "file": "src/sandbox/SandboxMessage.ts", + "fields": ["hosts"], + "getInstance": false + }, + "ServiceManager": { + "file": "src/service/ServiceManager.ts", + "fields": [], + "getInstance": true + } +} diff --git a/tests/browser-harness/framework-state.ts b/tests/browser-harness/framework-state.ts new file mode 100644 index 00000000..3b9c0380 --- /dev/null +++ b/tests/browser-harness/framework-state.ts @@ -0,0 +1,105 @@ +import {MessageGlobalKey} from "@typing/message"; +import {OffscreenGlobalAccess, OffscreenGlobalKey} from "@typing/offscreen"; +import {RelayGlobalKey} from "@typing/relay"; +import {SandboxGlobalAccess, SandboxGlobalKey} from "@typing/sandbox"; +import {ServiceGlobalKey} from "@typing/service"; +import {RelayPermissionGlobalKey} from "@relay/RelayPermission"; + +interface TestManager { + clear(): unknown; +} + +function clearManager(key: string): void { + try { + (globalThis[key] as TestManager | undefined)?.clear(); + } finally { + Reflect.deleteProperty(globalThis, key); + } +} + +// Explicit owners are audited by test:inventory. Resolve modules after each test's +// virtual-module fixtures, never while the shared setup is loading. +export const frameworkStateResetters = { + MessageManager: () => clearManager(MessageGlobalKey), + RelayManager: () => clearManager(RelayGlobalKey), + OffscreenManager: () => clearManager(OffscreenGlobalKey), + ServiceManager: () => clearManager(ServiceGlobalKey), + SandboxManager: () => clearManager(SandboxGlobalKey), + RelayPermission: () => { + // Browser event subscriptions are removed by harness.reset(). + Reflect.deleteProperty(globalThis, RelayPermissionGlobalKey); + }, + NativeLocale: () => { + const {default: NativeLocale} = jest.requireActual( + "@locale/providers/NativeLocale" + ); + + Reflect.deleteProperty(NativeLocale, "instance"); + }, + Message: () => { + const {default: Message} = + jest.requireActual("@message/providers/Message"); + + Reflect.deleteProperty(Message, "instance"); + }, + ObservableLocale: () => { + const {default: ObservableLocale} = jest.requireActual( + "@locale/observers/ObservableLocale" + ); + + Reflect.deleteProperty(ObservableLocale, "defaultInstance"); + Reflect.deleteProperty(ObservableLocale, "memoryInstance"); + Reflect.set(ObservableLocale, "drivers", new WeakMap()); + }, + OffscreenBridge: () => { + const {default: OffscreenBridge} = + jest.requireActual("@offscreen/OffscreenBridge"); + + Reflect.deleteProperty(OffscreenBridge, "instance"); + }, + SandboxMessage: () => { + const {default: SandboxMessage} = + jest.requireActual("@sandbox/SandboxMessage"); + const hosts = Reflect.get(SandboxMessage, "hosts") as Map>; + const errors: unknown[] = []; + + for (const host of hosts.values()) { + try { + host.dispose(); + } catch (error) { + errors.push(error); + } + } + + hosts.clear(); + + if (errors.length) { + throw new AggregateError(errors, "Sandbox host cleanup failed."); + } + }, +}; + +export function resetFrameworkState(): void { + const errors: unknown[] = []; + + for (const reset of Object.values(frameworkStateResetters)) { + try { + reset(); + } catch (error) { + errors.push(error); + } + } + + Reflect.deleteProperty(globalThis, OffscreenGlobalAccess); + Reflect.deleteProperty(globalThis, SandboxGlobalAccess); + + const remaining = Object.getOwnPropertyNames(globalThis).filter(key => key.startsWith("adnbn")); + + if (remaining.length) { + errors.push(new Error(`Unregistered framework globals after teardown: ${remaining.join(", ")}`)); + } + + if (errors.length) { + throw new AggregateError(errors, "Framework state cleanup failed."); + } +} diff --git a/tests/browser-harness/local-mocks.json b/tests/browser-harness/local-mocks.json new file mode 100644 index 00000000..7beee656 --- /dev/null +++ b/tests/browser-harness/local-mocks.json @@ -0,0 +1,3 @@ +{ + "localMocks": {} +} diff --git a/tests/browser-harness/session.ts b/tests/browser-harness/session.ts new file mode 100644 index 00000000..03a56f23 --- /dev/null +++ b/tests/browser-harness/session.ts @@ -0,0 +1,27 @@ +import BrowserTestSession from "./BrowserTestSession"; + +let session: BrowserTestSession | undefined; + +export function startBrowserTest(): void { + if (session) { + throw new Error("The previous browser test session has not been stopped."); + } + + session = new BrowserTestSession(); +} + +export function getBrowserTest(): BrowserTestSession { + if (!session) { + throw new Error("Browser harness is unavailable. Use it inside a test or hook."); + } + + return session; +} + +export async function stopBrowserTest(): Promise { + try { + await session?.dispose(); + } finally { + session = undefined; + } +} diff --git a/tests/pre-push.ts b/tests/check.ts similarity index 95% rename from tests/pre-push.ts rename to tests/check.ts index 80190928..13f53460 100644 --- a/tests/pre-push.ts +++ b/tests/check.ts @@ -4,7 +4,9 @@ import {runQueue} from "./integration/utils/queue"; const root = path.resolve(import.meta.dirname, ".."); const npm = process.env.npm_execpath; -if (!npm) throw new Error("Run this check with npm run test:pre-push"); +if (!npm) { + throw new Error("Run this check with npm run check"); +} const phase = (tasks: {name: string; args: string[]; stdio?: "inherit"}[]) => runQueue(tasks, tasks.length, async ({name, args, stdio}) => { diff --git a/tests/integration/README.md b/tests/integration/README.md index 9104ae0f..4a0dcffb 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -1,6 +1,6 @@ # Integration tests -These tests cover application builds, browser execution, and generated TypeScript contracts. Build checks through the public CLI live in `build`; tests that launch Chrome or Firefox live in `browser`; declaration and consumer type checks live in `types`. Each area keeps its tests beside their fixtures. +These tests cover application builds, browser execution, and generated TypeScript contracts. Build checks through the CLI and the built package live in `build`; tests that launch Chrome or Firefox live in `browser`; declaration and consumer type checks live in `types`. Each area keeps its tests beside their fixtures. ## Layout @@ -9,6 +9,20 @@ Selected scenario files and shared infrastructure: ```text tests/integration/ ├── build/ +│ ├── cli/ +│ │ ├── cli.integration.test.ts +│ │ ├── build-api.integration.test.ts +│ │ ├── dotenv.integration.test.ts +│ │ ├── BuildSession.integration.test.ts +│ │ ├── root-dir.integration.test.ts +│ │ ├── fixtures/ +│ │ │ ├── build-session/ +│ │ │ ├── exit-code/ +│ │ │ ├── environment/ +│ │ │ ├── dotenv/ +│ │ │ ├── root-dir/ +│ │ │ └── root-dir-b/ +│ │ └── scripts/ │ ├── locale/ │ │ ├── locale.integration.test.ts │ │ ├── dynamic.integration.test.ts @@ -23,15 +37,30 @@ tests/integration/ │ │ ├── options.integration.test.ts │ │ └── embedded/ │ ├── override/ -│ │ ├── override.integration.test.ts +│ │ ├── newtab.integration.test.ts +│ │ ├── bookmarks.integration.test.ts +│ │ ├── history.integration.test.ts +│ │ ├── competing.integration.test.ts +│ │ ├── override-utils.ts │ │ ├── newtab/ │ │ ├── bookmarks/ │ │ └── history/ │ ├── permissions/ │ │ ├── permissions.integration.test.ts │ │ └── views/ +│ ├── style/ +│ │ ├── style-merge.integration.test.ts +│ │ ├── compiler.ts +│ │ └── fixtures/ +│ │ ├── multi-app/ +│ │ └── multi-app-updates/ │ └── … ├── browser/ +│ ├── relay/ +│ │ ├── scripting.integration.test.ts +│ │ ├── scripting.firefox.integration.test.ts +│ │ ├── scripting-utils.ts +│ │ └── scripting/ │ ├── content/ │ │ ├── entrypoint-assets.integration.test.ts │ │ ├── entrypoint-assets.firefox.integration.test.ts @@ -65,13 +94,16 @@ tests/integration/ ├── types/ │ ├── content.integration.test.ts │ ├── registries.integration.test.ts +│ ├── relay.integration.test.ts │ ├── view.integration.test.ts │ └── fixtures/ │ ├── content/ │ ├── registries/ +│ ├── relay/ │ └── view/ ├── utils/ │ ├── fixture.ts +│ ├── fixture.test.ts │ ├── process.ts │ ├── process.test.ts │ ├── queue.ts @@ -82,14 +114,16 @@ tests/integration/ Use kebab-case for directory names and for filenames containing multiple words, including helpers and scenario tests. Files whose primary export is a class or React component use PascalCase matching that export. Tests for a specific class also preserve its name, for example `ContentManager.test.ts`. Keep framework entrypoint suffixes such as `.content.ts` and `.page.ts`. Put an entrypoint or component with its own styles in one directory; standalone entrypoints can remain single files. -Each application retains its own `package.json`, `adnbn.config.ts`, and `tsconfig.json`. The package boundary prevents the framework package's `sideEffects: false` setting from discarding fixture CSS imports. +The preparation/typecheck inventory recognizes standalone applications by both `adnbn.config.ts` and +`package.json`. Internal CLI configuration fixtures are exercised by their owning tests and are not prepared +as consumer applications. Each application retains its own `package.json`, `adnbn.config.ts`, and `tsconfig.json`. The package boundary prevents the framework package's `sideEffects: false` setting from discarding fixture CSS imports. ## Prepare the editor environment Install dependencies once at the repository root, then run: ```bash -npm run prepare:integration +npm run fixtures:prepare ``` This builds the framework, links each fixture's declared dependencies to the local framework or root `node_modules`, and builds all fixture applications. The generated `.adnbn` configuration and declarations remain beside their sources, so the editor can resolve `adnbn`, virtual imports such as `adnbn/browser`, CSS/SVG modules, and generated transport contracts. @@ -101,40 +135,110 @@ The generated `.adnbn`, `node_modules`, and `dist` directories are ignored by Gi To prepare and typecheck every fixture, including its configuration: ```bash -npm run typecheck:integration +npm run typecheck:fixtures ``` -The root `typecheck` checks framework and test-runner code; `typecheck:integration` additionally checks the fixture applications against their generated declarations. CI runs both. +The root `typecheck` checks framework and test-runner code; `typecheck:fixtures` additionally checks the fixture applications against their generated declarations. CI runs both. -The fixtures in `types` are checked by their Jest tests with isolated TypeScript programs against source and built package APIs. They do not have application configs and are not part of `prepare:integration` or `typecheck:integration`. +The fixtures in `types` are checked by their Jest tests with isolated TypeScript programs against source and built package APIs. They do not have application configs and are not part of `fixtures:prepare` or `typecheck:fixtures`. ## Run tests Run these commands from the repository root: ```bash -npm run test:integration -npm run test:integration:build -npm run test:chrome -npm run test:firefox +npm run test:build +npm run test:types +npm run test:browser +npm run test:browser:chrome +npm run test:browser:firefox ``` -They run all integration tests, only build checks, only Chrome checks, or only Firefox checks respectively. Each command builds the framework first. Browser tests require Chrome with `Extensions.loadUnpacked` support and Firefox with WebDriver BiDi `webExtension.install` support. Set `ADNBN_CHROME_BIN` or `ADNBN_FIREFOX_BIN` to the browser's absolute executable path if automatic discovery selects the wrong browser. Node must provide the built-in `WebSocket` API. CI installs both browsers on Linux; Windows runs the non-browser suite. +These commands run build integrations, declaration tests, both browsers, or an individual browser respectively. Each command builds the framework first. Browser tests require Chrome with `Extensions.loadUnpacked` support and Firefox with WebDriver BiDi `webExtension.install` support. Set `ADNBN_CHROME_BIN` or `ADNBN_FIREFOX_BIN` to the browser's absolute executable path if automatic discovery selects the wrong browser. Node must provide the built-in `WebSocket` API. CI installs both browsers on Linux; Windows runs the non-browser suite. These groups run test files in parallel, with up to eight workers by default (one fewer than the available CPUs on smaller machines). Pass `-- --maxWorkers=N` to tune the pool. Preparation and fixture typechecks run up to four independent applications concurrently; `ADNBN_TEST_WORKERS` overrides that limit. See [the test guide](../README.md) for project selection, hooks, coverage and the complete validation commands. Tests copy application inputs to unique directories under `.cache/integration`. Prepared dependencies and generated files are excluded from the copy and recreated there. Cleanup removes only the run's copy and temporary Chrome profile; it does not remove the editor environment in the source fixture or change the `addon` playground. +### Browser startup + +Every browser integration uses `startBrowserSession` in `browser/utils/session.ts`. It owns the browser process, +protocol connection and profile cleanup, including failed startup. The default `startupTimeout` is 30 seconds: +one budget from spawning the process through Chrome's `Browser.getVersion` or Firefox's `session.new` response. +The Chrome HTTP probe is limited to the smaller of five seconds and the remaining budget; protocol connection +attempts and readiness requests also use the remaining budget. Extension installation and page readiness are +separate operations. Scenario `waitFor` calls retain their 15-second default and test deadlines are unchanged. + +Tests that manage multiple targets use `{createPage: false}` and the session's `chrome` or `firefox` client, +`port` and `extensionId`. Firefox subscriptions needed before extension installation go in `firefoxEvents`. +Always close the session in `finally`; its `output` getter supplies browser stderr for failure diagnostics. + +The Jest reporter writes `.cache/integration/browser-startup.json` after each run. It contains per-start test +names, files, durations and failures, plus successful-start median, nearest-rank p95 and maximum per browser. +Durations exclude binary discovery, profile allocation, extension installation and cleanup. A successful startup +does not imply that extension installation or the test passed. CI uploads this report even when browser tests fail. +For comparison runs, set `ADNBN_BROWSER_STARTUP_REPORT` to distinct output paths; the default file describes only +the latest completed Jest invocation, including an empty report when it starts no browsers. Collection uses +buffered console diagnostics, so keep the default `--verbose=false` and do not pass Jest `--silent` when measuring startup. An interrupted/killed Jest +worker may not deliver its diagnostics. Use repeated CI reports to assess distributions and failure frequency; +a larger timeout or one green local run does not establish stability. + ## Coverage +The one-shot build lifecycle is checked with real Rspack compilers in `src/cli/builders/app/build.test.ts`. +`build` returns `Stats` only after compiler shutdown and attempts `close` after compilation errors too. +`BuildError` retains available statistics and the original cause; simultaneous compilation and close failures +are both retained in an `AggregateError` cause. A close failure is reported, not treated as successful cleanup. +The CLI owns statistics formatting, colors and the exit code. `build/cli/cli.integration.test.ts` launches the +real CLI to check exit codes, diagnostics and shutdown before success output. `build-api.integration.test.ts` +checks the built app API, including awaiting shutdown without printing statistics. Their application fixtures +live in `build/cli/fixtures`; Node subprocess drivers live in `build/cli/scripts`. These tests deliberately load +`dist` to exercise Node's real module/config loader. The source-level `build.test.ts` above imports `./build` +directly and has no dependency on `dist`. Watch still has a separate lifecycle and +remains covered through the CLI, including `isolation-watch`. + +The internal `buildApp(config): Promise` entry in `src/cli/builders/app/index.ts` is a sequential +one-shot pilot. It snapshots the existing `process.env` object before config resolution and restores its values +and identity in `finally`, including added, changed and deleted keys. Config, startup, bundler, compilation and +shutdown hooks see the build environment until compiler shutdown settles. A second `buildApp` call rejects +before reading config or modifying the active build's environment, including while shutdown is pending. + +This is a transitional environment transaction, not process isolation. Do not mix it with watch, direct calls +to the config resolver, or unrelated asynchronous work that writes `process.env`. Plugins must await their own +work; detached tasks, module caches and other global state are not restored. The CLI and integration fixtures +continue to use their existing process boundary; watch is outside the pilot. + +`build/cli/build-api.integration.test.ts` exercises real builds with TypeScript configs: A → B → A after a dotenv change, observations +inside config and plugin hooks, concurrent-call rejection at startup and shutdown, and recovery after config, +startup, bundler, run, compilation and close failures. The pilot accepts only `.ts`, `.mts` and `.cts` configs; +other formats reject before config execution. Native JS configs may retain evaluated exports in Node's module +cache across builds; environment restoration alone does not refresh them. Use the ordinary CLI for those +projects. This restriction belongs only to `buildApp`, not the CLI. Repeated builds are checked for all three +TypeScript extensions; imported native modules still retain their normal Node cache semantics. + +`build/cli/root-dir.integration.test.ts` builds two projects in the sequence A → B → A through both the CLI +and `buildApp`, from an unrelated working directory. Absolute roots, paths containing spaces, relative +imports and the generated `@/` alias are covered. Compiler context, source aliases and fallback package +resolution use the project root; the caller's working directory is unchanged. The external virtual-module +plugin still creates temporary files under the caller's `node_modules` and removes its module directory on +compiler shutdown; these builds therefore require a writable working directory. The sequence includes +Chrome and Firefox with Offscreen, including its generated background module. + +The external plugin does not isolate overlapping builds sharing a cwd: closing one compiler can remove +another compiler's virtual modules. This remains an upstream limitation, not a supported concurrency contract. +After `npm run build`, run `node tests/benchmarks/virtual-module-conflict.mjs` to reproduce the controlled +creation/close race. The diagnostic expects the second build to fail and is deliberately outside Jest. +Parallel test sessions use distinct working directories; production directory naming is unchanged. + - `types/content`: shared Content and adapter render types through the public source and built package APIs, callback props inference, and iframe-navigation restrictions for both define functions. - `types/view`: shared View and adapter render types through the public source and built package APIs, render and container props inference, the render contract adopted by Offscreen and Sandbox, and the rejection of Promise and plain-object render values. - `types/registries`: generated registry augmentation, empty fallbacks, public and internal type agreement, and message contracts against source and built package APIs. Compiler-host path checks cover both slash styles. - `build/options/embedded`: ten manifest checks covering explicit `openInTab: false` across Chrome, Edge, Opera, Safari, and Firefox in MV2 and MV3. No browser is launched. - `browser/view`: one Chrome MV3 case covering React offscreen and sandbox views rendered by the injected builder with their props and titles, a headless offscreen without a view container, and strings rendered as text by the React view, Vanilla view and React content adapters. - `browser/options`: two Chrome MV3 cases covering Vanilla and React rendering, CSS, state/events, opening Options from background, and a View chunk shared with a Page. -- `build/override`: thirty manifest checks covering the New Tab, Bookmarks, and History applications across Chrome, Edge, Opera, Safari, and Firefox in MV2 and MV3. Supporting browsers receive `chrome_url_overrides`, the page HTML, its CSP, and the permission declared by the entrypoint; the others receive none of them. Three more cases add the History entrypoint to a copy of the New Tab application: Chrome and Edge builds fail naming both entrypoints, while Firefox keeps the New Tab with its own permission and without the History one. No browser is launched. +- `build/override`: separate New Tab, Bookmarks, History and competing-entrypoint files let Jest schedule the scenarios across workers. The files share assertions, while each case keeps its own application copy and build. Thirty manifest checks cover the New Tab, Bookmarks, and History applications across Chrome, Edge, Opera, Safari, and Firefox in MV2 and MV3. Supporting browsers receive `chrome_url_overrides`, the page HTML, its CSP, and the permission declared by the entrypoint; the others receive none of them. Three more cases add the History entrypoint to a copy of the New Tab application: Chrome and Edge builds fail naming both entrypoints, while Firefox keeps the New Tab with its own permission and without the History one. No browser is launched. - `build/permissions/views`: four manifest checks of one application with two popups, two sidebars, and an options page that each declare permissions. Every built view contributes, including a popup and a sidebar that are not applied by default; Chrome MV2 has no sidebar, so its permissions are not requested; Firefox builds the sidebar action without `sidePanel`; MV2 declares hosts as permissions. No browser is launched. +- `build/style`: shared and app stylesheet merging with `mergeStyles` through the production style and asset rules. Two applications with the same layout and different fonts each receive one CSS module per destination, the same class identifier in ordinary and `?unisolated` output, and their own font. App overrides with a namespace conflict, a missing module, or invalid SCSS fail the build. Watch mode tracks override creation, edits, imported partials, resources, removal, and recreation. No browser is launched. - `browser/override`: three Chrome MV3 cases opening `chrome://newtab`, `chrome://bookmarks`, and `chrome://history`. They verify that Chrome serves the extension page, React and Vanilla rendering, CSS, state/events, custom `as` and `htmlDir` naming, and a View chunk shared with a Page. - `browser/offscreen/service`: one Chrome MV3 round trip from background through Offscreen to a registered background service. - `browser/content/entrypoint-assets`: one Chrome MV3 case and two Firefox cases (MV2 and MV3) using the same application and probe assertions. They cover current asset getters, rejecting the full-map getter outside background, common chunks, dynamic imports, CSS/SVG resource URLs, world separation, and top/child frames. The Chrome case also reads the full-map readiness flag in background. @@ -150,3 +254,58 @@ Browser execution covers the installed Chrome in MV3 and Firefox in MV2/MV3. It `browser/content/entrypoint-assets/site` is the ordinary website receiving the content scripts, not an extension entrypoint. A local HTTP server serves `top.html`, `frames.html`, and `child.html` with a restrictive CSP: `default-src 'none'; frame-src 'self'; img-src 'self'`. Same-origin image requests allow the browser's automatic favicon request, which receives an empty 204 response; scripts and styles remain restricted by `default-src`. The frame pages exercise `allFrames` and independent execution in the top document and its child iframe. Keep these HTML files outside the application's `src` directory. They need no separate package or build tool. The server uses an ephemeral loopback port and stops after the test. + +- `browser/relay/scripting`: Chrome MV3 and Firefox MV2/MV3 check real Relay scripting success, undefined/null results, remote errors, immediate missing-manager errors and exhausted retries (#109). + +## Build environment contract + +Only the framework loads dotenv files. Within each directory it reads, in order: +`.env...local`, `.env..`, `.env..local`, `.env.`, +`.env..local`, `.env.`, `.env.local`, `.env`. It reads all eight names in the app source +directory first, then the app directory, then the project root. The first file value wins. +Existing process variables take precedence in the host process; `APP`, `BROWSER`, `MODE`, and +`MANIFEST_VERSION` are always replaced with the current build target. + +Loading happens before the user config executes and again after its overrides are applied. On the second +pass, existing host variables remain, but newly selected file values replace earlier values for the bundled +`process.env` substitution. The bundle receives filtered file values and the four reserved values, not a copy +of the host environment. `${VAR}` references remain literal; the framework does not interpolate dotenv values. +`build/cli/dotenv.integration.test.ts` verifies both host values and actual Rspack substitution. + +The extra dotenv pass in `c12` is disabled. **Behavior change:** running the CLI from a directory outside the +project no longer reads that directory's `.env`, and its interpolated values no longer reach user config or +plugins. Put build variables in the app/project dotenv files or export them explicitly in the calling process. + +## Override build-session pilot + +Override page tests can select `ADNBN_OVERRIDE_BUILD_MODE=session`; the default is `cli`. +Each page test file owns one child Node process through `BuildSession`. The process loads the built package +once and handles its ten builds sequentially. Application copies, scenario names and assertions are unchanged; +the three competing-entrypoint scenarios always use the CLI. + +The session has a private, fixed cwd and receives absolute fixture roots. Its environment is copied for each +request and restored inside the child. Jest's environment, cwd and loader are never replaced. Neither concurrent +requests nor automatic retries are supported. Errors preserve their message, cause chain and compiler diagnostics. +The parent owns the deadline, kills a timed-out process, and waits for `close` before rejecting the request. +Normal disposal asks the child to exit, waits for `close`, and removes the session directory. If the owner dies, +the child's IPC `disconnect` handler exits immediately, even during a pending build. An abrupt owner death may +leave a temporary directory, but must not leave a running build process. + +```bash +npm run build +npx cross-env ADNBN_OVERRIDE_BUILD_MODE=session npm run test:run -- --selectProjects build --testPathPatterns=tests/integration/build/override --maxWorkers=2 +node tests/benchmarks/override-pilot.mjs +``` + +The child exits after each file, bounding compiler retention to that file's builds. This does not fix Rspack's +retention or establish a peak-memory bound. The benchmark compares two/eight workers, records child PID, +heap/RSS and peak RSS after each build, checks the same 33 scenario names, and verifies process and directory +cleanup. With three session-backed files there are at most three build children, even with eight Jest workers. +Run benchmarks exclusively, without another build or test run. + +See the [session measurements](../benchmarks/2026-09-28-override-session.md) for timings and child memory. +The [earlier in-process report](../benchmarks/2026-09-28-override-pilot.md) describes the superseded experiment; +its timings do not apply to the session backend. CI explicitly runs the session override suite on Linux and +Windows with Node 22 and 24, after the ordinary CLI build integrations, using two workers. This reuses the +existing package build and does not replace the CLI coverage run. The local default remains CLI; switching +it requires successful CI results and fresh measurements on the target machines. diff --git a/tests/integration/browser/content/entrypoint-assets.firefox.integration.test.ts b/tests/integration/browser/content/entrypoint-assets.firefox.integration.test.ts index 6620689b..407a79a1 100644 --- a/tests/integration/browser/content/entrypoint-assets.firefox.integration.test.ts +++ b/tests/integration/browser/content/entrypoint-assets.firefox.integration.test.ts @@ -1,11 +1,9 @@ -import {mkdtemp, readFile, rm, stat} from "fs/promises"; -import os from "os"; +import {readFile, stat} from "fs/promises"; import path from "path"; -import {spawn, type ChildProcess} from "child_process"; import BidiClient from "../utils/BidiClient"; -import {findFirefoxBinary} from "../utils/firefox"; -import {getFreePort, stop, waitFor} from "../utils/browser"; +import {startBrowserSession, type BrowserSession} from "../utils/session"; +import {waitFor} from "../utils/browser"; import {startIntegrationSite, type IntegrationSite} from "../utils/site"; import {createIntegrationFixture, type IntegrationFixture} from "../../utils/fixture"; import {documentStateExpression, expectLoadedProbe, type DocumentState} from "./utils"; @@ -23,20 +21,10 @@ jest.setTimeout(90_000); test.each([2, 3] as const)( "Firefox MV%s loads entrypoint assets in the effective execution world", async manifestVersion => { - const firefoxBinary = findFirefoxBinary(); - - if (!firefoxBinary || !path.isAbsolute(firefoxBinary)) { - throw new Error( - "Firefox is not installed. Install Firefox or set ADNBN_FIREFOX_BIN to its absolute executable path." - ); - } - - const userDataDir = await mkdtemp(path.join(os.tmpdir(), "adnbn-content-firefox-")); - let firefox: ChildProcess | undefined; + let session: BrowserSession | undefined; let browser: BidiClient | undefined; let fixture: IntegrationFixture | undefined; let site: IntegrationSite | undefined; - let output = ""; let lastState: DocumentState | FrameState | undefined; const expectDocument = (state: DocumentState, frame: string): void => { @@ -54,6 +42,7 @@ test.each([2, 3] as const)( const extensionDir = await fixture.build({browser: "firefox", manifestVersion}); const manifest = JSON.parse(await readFile(path.join(extensionDir, "manifest.json"), "utf8")); const scripts = manifest.content_scripts as Array<{js: string[]; css?: string[]; world?: string}>; + const resources = manifestVersion === 2 ? (manifest.web_accessible_resources as string[]) @@ -68,13 +57,16 @@ test.each([2, 3] as const)( if (manifestVersion === 2) { expect(scripts.every(script => script.world === undefined)).toBe(true); + expect( manifest.web_accessible_resources.every((resource: unknown) => typeof resource === "string") ).toBe(true); + const common = scripts[0].js.filter(file => scripts.every(script => script.js.includes(file))); expect(common).toEqual([expect.stringMatching(/^js\/common\.content\..+\.js$/)]); } else { expect(scripts.map(script => script.world).sort()).toEqual(["ISOLATED", "ISOLATED", "MAIN", "MAIN"]); + expect( manifest.web_accessible_resources.every( (entry: {resources: string[]; matches: string[]}) => @@ -95,39 +87,27 @@ test.each([2, 3] as const)( } site = await startIntegrationSite(path.join(fixture.directory, "site")); - const port = await getFreePort(); - firefox = spawn( - firefoxBinary, - [ - "--headless", - "--no-remote", - "--profile", - userDataDir, - "--remote-debugging-port", - String(port), - "about:blank", - ], - {stdio: ["ignore", "ignore", "pipe"]} - ); - firefox.stderr?.on("data", chunk => (output += chunk)); - - browser = await waitFor(() => BidiClient.connect(`ws://127.0.0.1:${port}/session`)); - const session = await browser.send("session.new", {capabilities: {alwaysMatch: {}}}); - console.info(`Firefox ${session.capabilities.browserVersion}: testing Manifest V${manifestVersion}`); - await browser.send("session.subscribe", {events: ["log.entryAdded"]}); - const extension = await browser.send("webExtension.install", { - extensionData: {path: extensionDir, type: "path"}, + + session = await startBrowserSession("firefox", rootDir, extensionDir, { + createPage: false, + firefoxEvents: ["log.entryAdded"], }); - expect(typeof extension.extension).toBe("string"); + + browser = session.firefox!; + console.info(`${session.version}: testing Manifest V${manifestVersion}`); + expect(typeof session.extensionId).toBe("string"); const {context} = await browser.send("browsingContext.create", {type: "tab"}); await browser.send("browsingContext.navigate", {context, url: `${site.origin}/top.html`, wait: "complete"}); + const top = await waitFor(async () => { const state = await browser!.evaluate(context, `${documentStateExpression}(document)`); lastState = state; const statuses = [state?.isolated?.async, state?.main?.async]; + return statuses.every(status => status !== undefined && status !== "pending") ? state : undefined; }); + expectDocument(top, "top"); await browser.send("browsingContext.navigate", { @@ -135,6 +115,7 @@ test.each([2, 3] as const)( url: `${site.origin}/frames.html`, wait: "complete", }); + const framed = await waitFor(async () => { const state = await browser!.evaluate( context, @@ -144,36 +125,31 @@ test.each([2, 3] as const)( return {top: ${documentStateExpression}(document), child: ${documentStateExpression}(child)}; })()` ); + lastState = state; + const statuses = [ state?.top?.isolated?.async, state?.top?.main?.async, state?.child?.isolated?.async, state?.child?.main?.async, ]; + return statuses.every(status => status !== undefined && status !== "pending") ? state : undefined; }); + expectDocument(framed.top, "top"); expectDocument(framed.child, "child"); expect(browser.runtimeErrors).toEqual([]); } catch (error) { throw new Error( - `${error instanceof Error ? error.message : String(error)}; last state: ${JSON.stringify(lastState)}; Runtime errors: ${JSON.stringify(browser?.runtimeErrors ?? [])}; Firefox output: ${output}`, + `${error instanceof Error ? error.message : String(error)}; last state: ${JSON.stringify(lastState)}; Runtime errors: ${JSON.stringify(browser?.runtimeErrors ?? [])}; Firefox output: ${session?.output ?? ""}`, {cause: error} ); } finally { - if (browser) { - try { - await browser.send("session.end", {}, 2_000); - } catch { - // Firefox may close the socket before returning the session.end response. - } - await browser.close(); - } - if (firefox) await stop(firefox); + await session?.close(); await site?.close(); await fixture?.dispose(); - await rm(userDataDir, {recursive: true, force: true, maxRetries: 5, retryDelay: 200}); } } ); diff --git a/tests/integration/browser/content/entrypoint-assets.integration.test.ts b/tests/integration/browser/content/entrypoint-assets.integration.test.ts index e7783f9c..125cd11d 100644 --- a/tests/integration/browser/content/entrypoint-assets.integration.test.ts +++ b/tests/integration/browser/content/entrypoint-assets.integration.test.ts @@ -1,11 +1,10 @@ -import {mkdtemp, readFile, rm, stat} from "fs/promises"; -import os from "os"; +import {readFile, stat} from "fs/promises"; import path from "path"; -import {spawn, type ChildProcess} from "child_process"; -import {browserVersion, findChromeBinary, targets} from "../utils/chrome"; +import {targets} from "../utils/chrome"; +import {startBrowserSession, type BrowserSession} from "../utils/session"; import CdpClient from "../utils/CdpClient"; -import {getFreePort, stop, waitFor} from "../utils/browser"; +import {waitFor} from "../utils/browser"; import {startIntegrationSite, type IntegrationSite} from "../utils/site"; import {createIntegrationFixture, type IntegrationFixture} from "../../utils/fixture"; @@ -13,7 +12,6 @@ import {documentStateExpression, expectLoadedProbe, type DocumentState} from "./ const rootDir = path.resolve(__dirname, "..", "..", "..", ".."); const fixtureDir = path.join(__dirname, "entrypoint-assets"); -const chromeBinary = findChromeBinary(rootDir); const evaluate = async (browser: CdpClient, sessionId: string, expression: string): Promise => { const result = await browser.send( @@ -54,19 +52,10 @@ const waitForLoadedDocument = async (browser: CdpClient, sessionId: string): Pro jest.setTimeout(90_000); test("Chrome MV3 loads ISOLATED chunks lazily and keeps MAIN dynamic imports in the initial graph", async () => { - if (!chromeBinary || !path.isAbsolute(chromeBinary)) { - throw new Error( - "Chrome is not installed or could not be found. Install Chrome or set ADNBN_CHROME_BIN to its absolute executable path." - ); - } - - const userDataDir = await mkdtemp(path.join(os.tmpdir(), "adnbn-content-entrypoint-assets-")); - const debuggingPort = await getFreePort(); - let chrome: ChildProcess | undefined; + let session: BrowserSession | undefined; let browser: CdpClient | undefined; let site: IntegrationSite | undefined; let fixture: IntegrationFixture | undefined; - let chromeOutput = ""; try { fixture = await createIntegrationFixture(rootDir, fixtureDir); @@ -75,6 +64,7 @@ test("Chrome MV3 loads ISOLATED chunks lazily and keeps MAIN dynamic imports in const manifest = JSON.parse(await readFile(path.join(extensionDir, "manifest.json"), "utf8")); const contentScripts = manifest.content_scripts as Array<{css?: string[]; js: string[]; world?: string}>; const backgroundFile = manifest.background.service_worker as string; + const resources = (manifest.web_accessible_resources ?? []).flatMap( (item: {resources?: string[]}) => item.resources ?? [] ); @@ -85,6 +75,7 @@ test("Chrome MV3 loads ISOLATED chunks lazily and keeps MAIN dynamic imports in const isolatedScripts = contentScripts.filter(script => script.world === "ISOLATED"); const mainScripts = contentScripts.filter(script => script.world === "MAIN"); + const sharedFiles = (scripts: Array<{js: string[]}>): string[] => { return scripts[0].js.filter(file => scripts.slice(1).every(script => script.js.includes(file))); }; @@ -97,6 +88,7 @@ test("Chrome MV3 loads ISOLATED chunks lazily and keeps MAIN dynamic imports in expect(isolated.js.filter(file => main.js.includes(file))).toEqual([]); } } + expect(resources.some((file: string) => /^assets\/probe-[a-f0-9]{4}\.svg$/.test(file))).toBe(true); expect(resources.some((file: string) => /^js\/.+\.js$/.test(file))).toBe(true); expect(resources.some((file: string) => /^css\/.+\.css$/.test(file))).toBe(true); @@ -108,27 +100,10 @@ test("Chrome MV3 loads ISOLATED chunks lazily and keeps MAIN dynamic imports in } site = await startIntegrationSite(path.join(fixture.directory, "site")); - chrome = spawn( - chromeBinary, - [ - "--headless=new", - "--no-sandbox", - "--no-first-run", - "--no-default-browser-check", - "--enable-logging=stderr", - "--v=0", - `--remote-debugging-port=${debuggingPort}`, - `--user-data-dir=${userDataDir}`, - "about:blank", - ], - {stdio: ["ignore", "ignore", "pipe"]} - ); - chrome.stderr?.on("data", chunk => (chromeOutput += chunk)); - - const {webSocketDebuggerUrl} = await waitFor(() => browserVersion(debuggingPort)); - browser = await CdpClient.connect(webSocketDebuggerUrl); - const extension = await browser.send("Extensions.loadUnpacked", {path: extensionDir}); - const extensionId = extension.id as string | undefined; + session = await startBrowserSession("chrome", rootDir, extensionDir, {createPage: false}); + browser = session.chrome!; + const debuggingPort = session.port; + const extensionId = session.extensionId; if (!extensionId) { throw new Error("Chrome did not return an extension ID after loading the content fixture"); @@ -141,12 +116,14 @@ test("Chrome MV3 loads ISOLATED chunks lazily and keeps MAIN dynamic imports in target.url === `chrome-extension://${extensionId}/${backgroundFile}` ); }); + const {sessionId: workerSessionId} = await browser.send("Target.attachToTarget", { targetId: worker.id, flatten: true, }); await browser.send("Runtime.enable", {}, workerSessionId); + await waitFor(async () => { return (await evaluate( browser!, @@ -191,6 +168,7 @@ test("Chrome MV3 loads ISOLATED chunks lazily and keeps MAIN dynamic imports in }; })()` ); + const statuses = [ state?.top?.isolated?.async, state?.top?.main?.async, @@ -218,18 +196,13 @@ test("Chrome MV3 loads ISOLATED chunks lazily and keeps MAIN dynamic imports in throw new Error( `${error instanceof Error ? error.message : String(error)}; Runtime errors: ${JSON.stringify( browser?.runtimeErrors ?? [] - )}; Chrome output: ${chromeOutput}`, + )}; Chrome output: ${session?.output ?? ""}`, {cause: error} ); } finally { - await browser?.close(); - - if (chrome) { - await stop(chrome); - } + await session?.close(); await site?.close(); await fixture?.dispose(); - await rm(userDataDir, {recursive: true, force: true, maxRetries: 5, retryDelay: 200}); } }); diff --git a/tests/integration/browser/content/isolated-styles-utils.ts b/tests/integration/browser/content/isolated-styles-utils.ts index 6bf1eb1b..c27c1a46 100644 --- a/tests/integration/browser/content/isolated-styles-utils.ts +++ b/tests/integration/browser/content/isolated-styles-utils.ts @@ -1,13 +1,10 @@ -import {spawn, type ChildProcess} from "child_process"; -import {mkdtemp, readFile, rm, stat, writeFile} from "fs/promises"; -import os from "os"; +import {readFile, stat, writeFile} from "fs/promises"; import path from "path"; -import {getFreePort, stop, waitFor} from "../utils/browser"; -import {browserVersion, findChromeBinary} from "../utils/chrome"; +import {waitFor} from "../utils/browser"; +import {startBrowserSession, type BrowserSession} from "../utils/session"; import CdpClient from "../utils/CdpClient"; import BidiClient from "../utils/BidiClient"; -import {findFirefoxBinary} from "../utils/firefox"; import {startIntegrationSite, type IntegrationSite} from "../utils/site"; import {createIntegrationFixture, type IntegrationFixture} from "../../utils/fixture"; @@ -142,19 +139,11 @@ export const runIsolatedStylesIntegration = async ( fixtureName = "isolation-shadow" ): Promise => { const rootDir = path.resolve(__dirname, "..", "..", "..", ".."); - const binary = name === "chrome" ? findChromeBinary(rootDir) : findFirefoxBinary(); - - if (!binary || !path.isAbsolute(binary)) { - throw new Error("Install " + name + " or set its ADNBN_*_BIN path"); - } - - const profile = await mkdtemp(path.join(os.tmpdir(), "adnbn-isolation-integration-")); - let browserProcess: ChildProcess | undefined; + let session: BrowserSession | undefined; let chrome: CdpClient | undefined; let firefox: BidiClient | undefined; let fixture: IntegrationFixture | undefined; let site: IntegrationSite | undefined; - let processOutput = ""; let lastState: unknown; try { @@ -203,80 +192,12 @@ export const runIsolatedStylesIntegration = async ( await expect(stat(path.join(extensionDir, file))).resolves.toBeDefined(); } - const port = await getFreePort(); - let navigate: (url: string) => Promise; - let evaluate: (expression: string) => Promise; - let version: string; - - if (name === "chrome") { - browserProcess = spawn( - binary, - [ - "--headless=new", - "--no-sandbox", - "--no-first-run", - "--no-default-browser-check", - "--remote-debugging-port=" + port, - "--user-data-dir=" + profile, - "about:blank", - ], - {stdio: ["ignore", "ignore", "pipe"]} - ); - - browserProcess.stderr?.on("data", chunk => (processOutput += chunk)); - const {webSocketDebuggerUrl} = await waitFor(() => browserVersion(port)); - chrome = await CdpClient.connect(webSocketDebuggerUrl); - version = (await chrome.send("Browser.getVersion")).product; - const extension = await chrome.send("Extensions.loadUnpacked", {path: extensionDir}); - expect(typeof extension.id).toBe("string"); - const {targetId} = await chrome.send("Target.createTarget", {url: "about:blank"}); - const {sessionId} = await chrome.send("Target.attachToTarget", {targetId, flatten: true}); - await chrome.send("Runtime.enable", {}, sessionId); - await chrome.send("Page.enable", {}, sessionId); - navigate = url => chrome!.send("Page.navigate", {url}, sessionId); - - evaluate = async expression => { - const result = await chrome!.send( - "Runtime.evaluate", - {expression, awaitPromise: true, returnByValue: true}, - sessionId - ); + session = await startBrowserSession(name, rootDir, extensionDir); + chrome = session.chrome; + firefox = session.firefox; + const {navigate, evaluate, version} = session; - if (result.exceptionDetails) { - throw new Error(JSON.stringify(result.exceptionDetails)); - } - - return result.result.value; - }; - } else { - browserProcess = spawn( - binary, - [ - "--headless", - "--no-remote", - "--profile", - profile, - "--remote-debugging-port", - String(port), - "about:blank", - ], - {stdio: ["ignore", "ignore", "pipe"]} - ); - - browserProcess.stderr?.on("data", chunk => (processOutput += chunk)); - firefox = await waitFor(() => BidiClient.connect("ws://127.0.0.1:" + port + "/session")); - const session = await firefox.send("session.new", {capabilities: {alwaysMatch: {}}}); - version = "Firefox/" + session.capabilities.browserVersion; - - const extension = await firefox.send("webExtension.install", { - extensionData: {path: extensionDir, type: "path"}, - }); - - expect(typeof extension.extension).toBe("string"); - const {context} = await firefox.send("browsingContext.create", {type: "tab"}); - navigate = url => firefox!.send("browsingContext.navigate", {context, url, wait: "complete"}); - evaluate = expression => firefox!.evaluate(context, expression); - } + expect(typeof session.extensionId).toBe("string"); const measurements: Array<{policy: string; top: ShadowDocumentState; frames: FrameState}> = []; @@ -472,28 +393,12 @@ export const runIsolatedStylesIntegration = async ( expect(name === "chrome" ? chrome?.runtimeErrors : firefox?.runtimeErrors).toEqual([]); } catch (error) { throw new Error( - `${error instanceof Error ? error.message : String(error)}; last state: ${JSON.stringify(lastState)}; runtime errors: ${JSON.stringify(chrome?.runtimeErrors ?? firefox?.runtimeErrors ?? [])}; browser output: ${processOutput}`, + `${error instanceof Error ? error.message : String(error)}; last state: ${JSON.stringify(lastState)}; runtime errors: ${JSON.stringify(chrome?.runtimeErrors ?? firefox?.runtimeErrors ?? [])}; browser output: ${session?.output ?? ""}`, {cause: error} ); } finally { - await chrome?.close(); - - if (firefox) { - try { - await firefox.send("session.end", {}, 2_000); - } catch { - // Firefox may close before replying. - } - - await firefox.close(); - } - - if (browserProcess) { - await stop(browserProcess); - } - + await session?.close(); await site?.close(); await fixture?.dispose(); - await rm(profile, {recursive: true, force: true, maxRetries: 5, retryDelay: 200}); } }; diff --git a/tests/integration/browser/locale/chunks.firefox.integration.test.ts b/tests/integration/browser/locale/chunks.firefox.integration.test.ts index 854ed316..beb9e79e 100644 --- a/tests/integration/browser/locale/chunks.firefox.integration.test.ts +++ b/tests/integration/browser/locale/chunks.firefox.integration.test.ts @@ -1,55 +1,47 @@ -import {spawn, type ChildProcess} from "child_process"; -import {mkdtemp, rm} from "fs/promises"; -import os from "os"; import path from "path"; import {createIntegrationFixture} from "../../utils/fixture"; import BidiClient from "../utils/BidiClient"; -import {findFirefoxBinary} from "../utils/firefox"; -import {getFreePort, stop, waitFor} from "../utils/browser"; +import {startBrowserSession, type BrowserSession} from "../utils/session"; +import {waitFor} from "../utils/browser"; import {startIntegrationSite, type IntegrationSite} from "../utils/site"; import {changeLanguage, expectPanels, localeFixtureDirectory, readPanels} from "./utils"; jest.setTimeout(90_000); test("Firefox MV3 loads locale in ISOLATED and shares the MAIN catalogue through common content", async () => { - const binary = findFirefoxBinary(); - if (!binary) throw new Error("Install Firefox or set ADNBN_FIREFOX_BIN"); const fixture = await createIntegrationFixture(ADNBN_TEST_ROOT, localeFixtureDirectory); - const profile = await mkdtemp(path.join(os.tmpdir(), "adnbn-locale-firefox-")); - let process: ChildProcess | undefined; + let session: BrowserSession | undefined; let client: BidiClient | undefined; let site: IntegrationSite | undefined; try { const directory = await fixture.build({browser: "firefox"}); - const port = await getFreePort(); - process = spawn( - binary, - ["--headless", "--no-remote", "--profile", profile, "--remote-debugging-port", String(port), "about:blank"], - {stdio: "ignore"} - ); - client = await waitFor(() => BidiClient.connect(`ws://127.0.0.1:${port}/session`)); - await client.send("session.new", {capabilities: {alwaysMatch: {}}}); - await client.send("session.subscribe", {events: ["log.entryAdded"]}); - await client.send("webExtension.install", {extensionData: {path: directory, type: "path"}}); + + session = await startBrowserSession("firefox", ADNBN_TEST_ROOT, directory, { + createPage: false, + firefoxEvents: ["log.entryAdded"], + }); + + client = session.firefox!; site = await startIntegrationSite(path.join(fixture.directory, "site")); const {context} = await client.send("browsingContext.create", {type: "tab"}); await client.send("browsingContext.navigate", {context, url: site.origin, wait: "complete"}); const contexts = ["isolated", "isolated-secondary", "main", "main-secondary"]; + await waitFor(async () => { expectPanels(await client!.evaluate(context, readPanels), contexts); + return true; }); + expect(await client.evaluate(context, "typeof globalThis.browser?.i18n")).toBe("undefined"); await client.evaluate(context, changeLanguage); expectPanels(await client.evaluate(context, readPanels), contexts, "fr"); expect(client.runtimeErrors).toEqual([]); } finally { - await client?.close(); - if (process) await stop(process); + await session?.close(); await site?.close(); await fixture.dispose(); - await rm(profile, {recursive: true, force: true, maxRetries: 5, retryDelay: 200}); } }); diff --git a/tests/integration/browser/locale/chunks.integration.test.ts b/tests/integration/browser/locale/chunks.integration.test.ts index 0383a9ee..959ba86f 100644 --- a/tests/integration/browser/locale/chunks.integration.test.ts +++ b/tests/integration/browser/locale/chunks.integration.test.ts @@ -1,53 +1,27 @@ -import {spawn, type ChildProcess} from "child_process"; -import {mkdtemp, readFile, rm} from "fs/promises"; -import os from "os"; +import {readFile} from "fs/promises"; import path from "path"; import {createIntegrationFixture} from "../../utils/fixture"; -import {browserVersion, findChromeBinary} from "../utils/chrome"; +import {startBrowserSession, type BrowserSession} from "../utils/session"; import CdpClient from "../utils/CdpClient"; -import {getFreePort, stop, waitFor} from "../utils/browser"; +import {waitFor} from "../utils/browser"; import {startIntegrationSite, type IntegrationSite} from "../utils/site"; import {changeLanguage, expectPanels, localeFixtureDirectory, readPanels} from "./utils"; jest.setTimeout(90_000); test("Chrome shares locale with views and ISOLATED, and loads the MAIN catalogue through common content", async () => { - const binary = findChromeBinary(ADNBN_TEST_ROOT); - - if (!binary) { - throw new Error("Install Chrome for Testing or set ADNBN_CHROME_BIN"); - } - const fixture = await createIntegrationFixture(ADNBN_TEST_ROOT, localeFixtureDirectory); - const profile = await mkdtemp(path.join(os.tmpdir(), "adnbn-locale-chrome-")); - let process: ChildProcess | undefined; + let session: BrowserSession | undefined; let client: CdpClient | undefined; let site: IntegrationSite | undefined; try { const directory = await fixture.build(); const manifest = JSON.parse(await readFile(path.join(directory, "manifest.json"), "utf8")); - const port = await getFreePort(); - - process = spawn( - binary, - [ - "--headless=new", - "--no-sandbox", - "--no-first-run", - "--no-default-browser-check", - `--user-data-dir=${profile}`, - `--remote-debugging-port=${port}`, - "about:blank", - ], - {stdio: "ignore"} - ); - - const {webSocketDebuggerUrl} = await waitFor(() => browserVersion(port)); - client = await CdpClient.connect(webSocketDebuggerUrl); - const extension = await client.send("Extensions.loadUnpacked", {path: directory}); - const origin = `chrome-extension://${extension.id}`; + session = await startBrowserSession("chrome", ADNBN_TEST_ROOT, directory, {createPage: false}); + client = session.chrome!; + const origin = `chrome-extension://${session.extensionId}`; const attach = async (targetId: string): Promise => { const {sessionId} = await client!.send("Target.attachToTarget", {targetId, flatten: true}); @@ -114,14 +88,9 @@ test("Chrome shares locale with views and ISOLATED, and loads the MAIN catalogue expect(client.runtimeErrors).toEqual([]); } finally { - await client?.close(); - - if (process) { - await stop(process); - } + await session?.close(); await site?.close(); await fixture.dispose(); - await rm(profile, {recursive: true, force: true, maxRetries: 5, retryDelay: 200}); } }); diff --git a/tests/integration/browser/locale/dynamic.firefox.integration.test.ts b/tests/integration/browser/locale/dynamic.firefox.integration.test.ts index 96c46e5f..adda23e5 100644 --- a/tests/integration/browser/locale/dynamic.firefox.integration.test.ts +++ b/tests/integration/browser/locale/dynamic.firefox.integration.test.ts @@ -1,39 +1,33 @@ -import {spawn, type ChildProcess} from "child_process"; -import {mkdtemp, rm} from "fs/promises"; -import os from "os"; import path from "path"; import {createIntegrationFixture} from "../../utils/fixture"; import BidiClient from "../utils/BidiClient"; -import {findFirefoxBinary} from "../utils/firefox"; -import {getFreePort, stop, waitFor} from "../utils/browser"; +import {startBrowserSession, type BrowserSession} from "../utils/session"; +import {waitFor} from "../utils/browser"; import {startIntegrationSite, type IntegrationSite} from "../utils/site"; jest.setTimeout(90_000); test("Firefox uses DynamicLocale in both worlds and restores stored selection only in ISOLATED without requests", async () => { - const binary = findFirefoxBinary(); - if (!binary) throw new Error("Install Firefox or set ADNBN_FIREFOX_BIN"); const fixture = await createIntegrationFixture( ADNBN_TEST_ROOT, path.join(ADNBN_TEST_ROOT, "tests/integration/build/locale/dynamic-fixture") ); - const profile = await mkdtemp(path.join(os.tmpdir(), "adnbn-dynamic-locale-firefox-")); - let browser: ChildProcess | undefined; + + let session: BrowserSession | undefined; let client: BidiClient | undefined; let site: IntegrationSite | undefined; + try { const directory = await fixture.build({browser: "firefox"}); - const port = await getFreePort(); - browser = spawn( - binary, - ["--headless", "--no-remote", "--profile", profile, "--remote-debugging-port", String(port), "about:blank"], - {stdio: "ignore"} - ); - client = await waitFor(() => BidiClient.connect(`ws://127.0.0.1:${port}/session`)); - await client.send("session.new", {capabilities: {alwaysMatch: {}}}); - await client.send("session.subscribe", {events: ["log.entryAdded", "network.beforeRequestSent"]}); - await client.send("webExtension.install", {extensionData: {path: directory, type: "path"}}); + + session = await startBrowserSession("firefox", ADNBN_TEST_ROOT, directory, { + createPage: false, + firefoxEvents: ["log.entryAdded", "network.beforeRequestSent"], + }); + + client = session.firefox!; site = await startIntegrationSite(path.join(fixture.directory, "site")); + const expectLanguage = async (context: string, language: string) => waitFor(async () => { expect( @@ -42,11 +36,14 @@ test("Firefox uses DynamicLocale in both worlds and restores stored selection on `document.querySelector('[data-locale-context="isolated"]')?.dataset.language` ) ).toBe(language); + return true; }); + const {context} = await client.send("browsingContext.create", {type: "tab"}); await client.send("browsingContext.navigate", {context, url: site.origin, wait: "complete"}); await expectLanguage(context, "en"); + await waitFor(async () => { expect( await client!.evaluate( @@ -54,9 +51,12 @@ test("Firefox uses DynamicLocale in both worlds and restores stored selection on `document.querySelector('[data-locale-context="main"]')?.dataset.language` ) ).toBe("en"); + return true; }); + const before = client.requests.length; + const greeting = await client.evaluate( context, `(() => { @@ -67,10 +67,12 @@ test("Firefox uses DynamicLocale in both worlds and restores stored selection on return panel.querySelector('p').textContent; })()` ); + expect(greeting).toBe("Bonjour depuis DynamicLocale !"); const second = await client.send("browsingContext.create", {type: "tab"}); await client.send("browsingContext.navigate", {context: second.context, url: site.origin, wait: "complete"}); await expectLanguage(second.context, "fr"); + await waitFor(async () => { expect( await client!.evaluate( @@ -78,8 +80,10 @@ test("Firefox uses DynamicLocale in both worlds and restores stored selection on `document.querySelector('[data-locale-context="main"]')?.dataset.language` ) ).toBe("en"); + return true; }); + expect( await client.evaluate( context, @@ -92,6 +96,7 @@ test("Firefox uses DynamicLocale in both worlds and restores stored selection on })()` ) ).toEqual({lang: "fr", message: "Bonjour depuis DynamicLocale !"}); + await client.evaluate( second.context, `(() => { @@ -100,18 +105,19 @@ test("Firefox uses DynamicLocale in both worlds and restores stored selection on select.dispatchEvent(new Event('change', {bubbles: true})); })()` ); + await expectLanguage(context, "en"); + expect( await client.evaluate(context, `document.querySelector('[data-locale-context="main"]').dataset.language`) ).toBe("fr"); + expect(client.requests.slice(before).filter(url => url.startsWith("moz-extension://"))).toEqual([]); expect(client.requests.filter(url => /_locales\/|messages\.json/.test(url))).toEqual([]); expect(client.runtimeErrors).toEqual([]); } finally { - await client?.close(); - if (browser) await stop(browser); + await session?.close(); await site?.close(); await fixture.dispose(); - await rm(profile, {recursive: true, force: true, maxRetries: 5, retryDelay: 200}); } }); diff --git a/tests/integration/browser/locale/dynamic.integration.test.ts b/tests/integration/browser/locale/dynamic.integration.test.ts index 33f347f5..75cf4162 100644 --- a/tests/integration/browser/locale/dynamic.integration.test.ts +++ b/tests/integration/browser/locale/dynamic.integration.test.ts @@ -1,55 +1,29 @@ -import {spawn, type ChildProcess} from "child_process"; -import {mkdtemp, readFile, rm} from "fs/promises"; -import os from "os"; +import {readFile} from "fs/promises"; import path from "path"; import {createIntegrationFixture} from "../../utils/fixture"; -import {browserVersion, findChromeBinary} from "../utils/chrome"; +import {startBrowserSession, type BrowserSession} from "../utils/session"; import CdpClient from "../utils/CdpClient"; -import {getFreePort, stop, waitFor} from "../utils/browser"; +import {waitFor} from "../utils/browser"; import {startIntegrationSite, type IntegrationSite} from "../utils/site"; jest.setTimeout(90_000); test("Chrome switches DynamicLocale in both worlds and synchronizes extension storage without loading translations", async () => { - const binary = findChromeBinary(ADNBN_TEST_ROOT); - - if (!binary) { - throw new Error("Install Chrome for Testing or set ADNBN_CHROME_BIN"); - } - const fixture = await createIntegrationFixture( ADNBN_TEST_ROOT, path.join(ADNBN_TEST_ROOT, "tests/integration/build/locale/dynamic-fixture") ); - const profile = await mkdtemp(path.join(os.tmpdir(), "adnbn-dynamic-locale-chrome-")); - let browser: ChildProcess | undefined; + let session: BrowserSession | undefined; let client: CdpClient | undefined; let site: IntegrationSite | undefined; try { const directory = await fixture.build(); const manifest = JSON.parse(await readFile(path.join(directory, "manifest.json"), "utf8")); - const port = await getFreePort(); - - browser = spawn( - binary, - [ - "--headless=new", - "--no-sandbox", - "--no-first-run", - "--no-default-browser-check", - `--user-data-dir=${profile}`, - `--remote-debugging-port=${port}`, - "about:blank", - ], - {stdio: "ignore"} - ); - - const {webSocketDebuggerUrl} = await waitFor(() => browserVersion(port)); - client = await CdpClient.connect(webSocketDebuggerUrl); - const extension = await client.send("Extensions.loadUnpacked", {path: directory}); - const origin = `chrome-extension://${extension.id}`; + session = await startBrowserSession("chrome", ADNBN_TEST_ROOT, directory, {createPage: false}); + client = session.chrome!; + const origin = `chrome-extension://${session.extensionId}`; const attach = async (targetId: string): Promise => { const {sessionId} = await client!.send("Target.attachToTarget", {targetId, flatten: true}); @@ -218,14 +192,9 @@ test("Chrome switches DynamicLocale in both worlds and synchronizes extension st expect(client.requests.filter(url => /_locales\/|messages\.json/.test(url))).toEqual([]); expect(client.runtimeErrors).toEqual([]); } finally { - await client?.close(); - - if (browser) { - await stop(browser); - } + await session?.close(); await site?.close(); await fixture.dispose(); - await rm(profile, {recursive: true, force: true, maxRetries: 5, retryDelay: 200}); } }); diff --git a/tests/integration/browser/offscreen/service.integration.test.ts b/tests/integration/browser/offscreen/service.integration.test.ts index 9f7380a0..7703b922 100644 --- a/tests/integration/browser/offscreen/service.integration.test.ts +++ b/tests/integration/browser/offscreen/service.integration.test.ts @@ -1,58 +1,29 @@ -import {mkdtemp, rm} from "fs/promises"; -import os from "os"; import path from "path"; -import {spawn, type ChildProcess} from "child_process"; -import {browserVersion, type CdpTarget, findChromeBinary, targets} from "../utils/chrome"; +import {type CdpTarget, targets} from "../utils/chrome"; +import {startBrowserSession, type BrowserSession} from "../utils/session"; import CdpClient from "../utils/CdpClient"; -import {getFreePort, stop, waitFor} from "../utils/browser"; +import {waitFor} from "../utils/browser"; import {createIntegrationFixture, type IntegrationFixture} from "../../utils/fixture"; const rootDir = path.resolve(__dirname, "..", "..", "..", ".."); const fixtureDir = path.join(__dirname, "service"); -const chromeBinary = findChromeBinary(rootDir); jest.setTimeout(60_000); test("Chrome MV3 offscreen calls the registered background service", async () => { - if (!chromeBinary || !path.isAbsolute(chromeBinary)) { - throw new Error( - "Chrome is not installed or could not be found. Install Chrome or set ADNBN_CHROME_BIN to its absolute executable path." - ); - } - - const userDataDir = await mkdtemp(path.join(os.tmpdir(), "adnbn-offscreen-service-")); - const debuggingPort = await getFreePort(); - let chrome: ChildProcess | undefined; + let session: BrowserSession | undefined; let browser: CdpClient | undefined; let fixture: IntegrationFixture | undefined; - let chromeOutput = ""; try { fixture = await createIntegrationFixture(rootDir, fixtureDir); const extensionDir = await fixture.build(); - chrome = spawn( - chromeBinary, - [ - "--headless=new", - "--no-sandbox", - "--no-first-run", - "--no-default-browser-check", - "--enable-logging=stderr", - "--v=0", - `--remote-debugging-port=${debuggingPort}`, - `--user-data-dir=${userDataDir}`, - "about:blank", - ], - {stdio: ["ignore", "ignore", "pipe"]} - ); - chrome.stderr?.on("data", chunk => (chromeOutput += chunk)); - - const {webSocketDebuggerUrl} = await waitFor(() => browserVersion(debuggingPort)); - browser = await CdpClient.connect(webSocketDebuggerUrl); - const extension = await browser.send("Extensions.loadUnpacked", {path: extensionDir}); - const extensionId = extension.id as string | undefined; + session = await startBrowserSession("chrome", rootDir, extensionDir, {createPage: false}); + browser = session.chrome!; + const debuggingPort = session.port; + const extensionId = session.extensionId; if (!extensionId) { throw new Error("Chrome did not return an extension ID after loading the MV3 fixture"); @@ -75,7 +46,7 @@ test("Chrome MV3 offscreen calls the registered background service", async () => throw new Error( `${error instanceof Error ? error.message : String(error)}; CDP targets: ${JSON.stringify( chromeTargets.map(target => ({type: target.type, url: target.url})) - )}; Chrome output: ${chromeOutput}` + )}; Chrome output: ${session?.output ?? ""}` ); } @@ -123,13 +94,8 @@ test("Chrome MV3 offscreen calls the registered background service", async () => expect(result.exceptionDetails).toBeUndefined(); expect(result.result.value).toBe("background:ping"); } finally { - await browser?.close(); - - if (chrome) { - await stop(chrome); - } + await session?.close(); await fixture?.dispose(); - await rm(userDataDir, {recursive: true, force: true, maxRetries: 5, retryDelay: 200}); } }); diff --git a/tests/integration/browser/options/README.md b/tests/integration/browser/options/README.md index 9d12d33d..c2306a8f 100644 --- a/tests/integration/browser/options/README.md +++ b/tests/integration/browser/options/README.md @@ -15,4 +15,4 @@ Each Options entrypoint keeps its `styles.css` in the same directory. `backgroun Both cases open Options from the real background service worker in Chrome MV3. They check the exact `options_ui` object, the absence of `options_page`, a View chunk shared with the Help page, CSS application, state changes, and runtime errors. Expected pages are `options.html` and `ui/preferences.options.html`. -The embedded Options manifest matrix lives separately in `tests/integration/build/options/embedded` and runs without Chrome through `npm run test:integration:build`. It does not verify embedded settings UI in a browser. +The embedded Options manifest matrix lives separately in `tests/integration/build/options/embedded` and runs without Chrome through `npm run test:build`. It does not verify embedded settings UI in a browser. diff --git a/tests/integration/browser/options/options.integration.test.ts b/tests/integration/browser/options/options.integration.test.ts index 89eaa4d8..150ad11b 100644 --- a/tests/integration/browser/options/options.integration.test.ts +++ b/tests/integration/browser/options/options.integration.test.ts @@ -1,15 +1,13 @@ -import {mkdtemp, readFile, rm} from "fs/promises"; -import os from "os"; +import {readFile} from "fs/promises"; import path from "path"; -import {spawn, type ChildProcess} from "child_process"; -import {browserVersion, findChromeBinary, targets} from "../utils/chrome"; +import {targets} from "../utils/chrome"; +import {startBrowserSession, type BrowserSession} from "../utils/session"; import CdpClient from "../utils/CdpClient"; -import {getFreePort, stop, waitFor} from "../utils/browser"; +import {waitFor} from "../utils/browser"; import {createIntegrationFixture, type IntegrationFixture} from "../../utils/fixture"; const rootDir = path.resolve(__dirname, "..", "..", "..", ".."); -const chromeBinary = findChromeBinary(rootDir); jest.setTimeout(90_000); @@ -17,19 +15,10 @@ test.each([ {adapter: "vanilla", page: "options.html", title: "Vanilla Options", help: "help.html"}, {adapter: "react", page: "ui/preferences.options.html", title: "React Options", help: "ui/help.html"}, ])("Chrome MV3 opens and renders $adapter options from the background", async ({adapter, page, title, help}) => { - if (!chromeBinary || !path.isAbsolute(chromeBinary)) { - throw new Error( - "Chrome is not installed or could not be found. Install Chrome or set ADNBN_CHROME_BIN to its absolute executable path." - ); - } - const fixtureDir = path.join(__dirname, adapter); - const userDataDir = await mkdtemp(path.join(os.tmpdir(), `adnbn-options-${adapter}-`)); - const debuggingPort = await getFreePort(); - let chrome: ChildProcess | undefined; + let session: BrowserSession | undefined; let browser: CdpClient | undefined; let fixture: IntegrationFixture | undefined; - let chromeOutput = ""; try { fixture = await createIntegrationFixture(rootDir, fixtureDir); @@ -45,27 +34,10 @@ test.each([ expect(optionsHtml).toContain("common.view.js"); expect(helpHtml).toContain("common.view.js"); - chrome = spawn( - chromeBinary, - [ - "--headless=new", - "--no-sandbox", - "--no-first-run", - "--no-default-browser-check", - "--enable-logging=stderr", - "--v=0", - `--remote-debugging-port=${debuggingPort}`, - `--user-data-dir=${userDataDir}`, - "about:blank", - ], - {stdio: ["ignore", "ignore", "pipe"]} - ); - chrome.stderr?.on("data", chunk => (chromeOutput += chunk)); - - const {webSocketDebuggerUrl} = await waitFor(() => browserVersion(debuggingPort)); - browser = await CdpClient.connect(webSocketDebuggerUrl); - const extension = await browser.send("Extensions.loadUnpacked", {path: extensionDir}); - const extensionId = extension.id as string | undefined; + session = await startBrowserSession("chrome", rootDir, extensionDir, {createPage: false}); + browser = session.chrome!; + const debuggingPort = session.port; + const extensionId = session.extensionId; if (!extensionId) { throw new Error("Chrome did not return an extension ID after loading the Options fixture"); @@ -92,15 +64,18 @@ test.each([ target.url === `chrome-extension://${extensionId}/${manifest.background.service_worker}` ); }); + const {sessionId: workerSessionId} = await browser.send("Target.attachToTarget", { targetId: worker.id, flatten: true, }); await browser.send("Runtime.enable", {}, workerSessionId); + await waitFor(async () => { return (await evaluate(workerSessionId, "globalThis.__adnbnOptionsReady === true")) ? true : undefined; }); + await evaluate(workerSessionId, "chrome.runtime.openOptionsPage()"); const optionsTarget = await waitFor(async () => { @@ -108,12 +83,14 @@ test.each([ target => target.type === "page" && target.url === `chrome-extension://${extensionId}/${page}` ); }); + const {sessionId: optionsSessionId} = await browser.send("Target.attachToTarget", { targetId: optionsTarget.id, flatten: true, }); await browser.send("Runtime.enable", {}, optionsSessionId); + const rendered = await waitFor(async () => { return ( (await evaluate( @@ -136,6 +113,7 @@ test.each([ expect(rendered).toEqual({adapter, title, count: "0", color: "rgb(31, 78, 121)", topLevel: true}); await evaluate(optionsSessionId, "document.querySelector('[data-testid=increment]').click()"); + const count = await waitFor(async () => { const value = await evaluate(optionsSessionId, "document.querySelector('[data-testid=count]').textContent"); @@ -148,17 +126,12 @@ test.each([ throw new Error( `${error instanceof Error ? error.message : String(error)}; Runtime errors: ${JSON.stringify( browser?.runtimeErrors ?? [] - )}; Chrome output: ${chromeOutput}`, + )}; Chrome output: ${session?.output ?? ""}`, {cause: error} ); } finally { - await browser?.close(); - - if (chrome) { - await stop(chrome); - } + await session?.close(); await fixture?.dispose(); - await rm(userDataDir, {recursive: true, force: true, maxRetries: 5, retryDelay: 200}); } }); diff --git a/tests/integration/browser/override/README.md b/tests/integration/browser/override/README.md index 722d0705..7b648041 100644 --- a/tests/integration/browser/override/README.md +++ b/tests/integration/browser/override/README.md @@ -15,4 +15,4 @@ Each entrypoint keeps its `styles.css` in the same directory. Chromium accepts a The cases navigate a real Chrome MV3 tab to `chrome://newtab`, `chrome://bookmarks`, and `chrome://history`. They check the exact `chrome_url_overrides` object, that the loaded document is the extension page, CSS application, state changes, and runtime errors. Expected pages are `ui/dashboard.newtab.html`, `bookmarks.html`, and `history.html`. -The browser support matrix and the competing-entrypoints failure live separately in `tests/integration/build/override` and run without Chrome through `npm run test:integration:build`. +The browser support matrix and the competing-entrypoints failure live separately in `tests/integration/build/override` and run without Chrome through `npm run test:build`. diff --git a/tests/integration/browser/relay/scripting-utils.ts b/tests/integration/browser/relay/scripting-utils.ts new file mode 100644 index 00000000..21a0e8c0 --- /dev/null +++ b/tests/integration/browser/relay/scripting-utils.ts @@ -0,0 +1,43 @@ +import path from "node:path"; +import {createIntegrationFixture} from "@tests/integration/utils/fixture"; +import {startBrowserSession} from "@tests/integration/browser/utils/session"; +import {startIntegrationSite} from "@tests/integration/browser/utils/site"; +import {waitFor} from "@tests/integration/browser/utils/browser"; + +type BrowserSession = Awaited>; + +export async function expectRelayScripting(browser: "chrome" | "firefox", manifestVersion: 2 | 3): Promise { + const root = path.resolve(__dirname, "../../../.."); + const fixture = await createIntegrationFixture(root, path.join(__dirname, "scripting")); + const site = await startIntegrationSite(path.join(fixture.directory, "site"), null); + let session: BrowserSession | undefined; + + try { + const extension = await fixture.build({browser, manifestVersion}); + + session = await startBrowserSession(browser, root, extension); + await session.navigate(`${site.origin}/top.html`); + + const result = await waitFor(async () => { + const value = await session!.evaluate("document.body?.dataset.result"); + + return value ? JSON.parse(value) : undefined; + }).catch(error => { + throw new Error(`${error}; browser errors: ${JSON.stringify(session!.errors)}`); + }); + + expect(result).toEqual({ + empty: true, + nullable: true, + thrown: {name: "TypeError", message: "Remote failure"}, + rejected: {name: "RangeError", message: "Async failure"}, + any: [{status: "rejected", kind: "remote", name: "Error", message: "Relay manager not found."}], + missing: {name: "Error", message: "Relay manager not found after 10 attempts."}, + }); + expect(session.errors).toEqual([]); + } finally { + await session?.close(); + await site.close(); + await fixture.dispose(); + } +} diff --git a/tests/integration/browser/relay/scripting.firefox.integration.test.ts b/tests/integration/browser/relay/scripting.firefox.integration.test.ts new file mode 100644 index 00000000..4533bca7 --- /dev/null +++ b/tests/integration/browser/relay/scripting.firefox.integration.test.ts @@ -0,0 +1,6 @@ +import {expectRelayScripting} from "./scripting-utils"; + +jest.setTimeout(90_000); +test.each([2, 3] as const)("Firefox MV%s preserves Relay scripting responses and missing-manager errors", version => + expectRelayScripting("firefox", version) +); diff --git a/tests/integration/browser/relay/scripting.integration.test.ts b/tests/integration/browser/relay/scripting.integration.test.ts new file mode 100644 index 00000000..811ab25a --- /dev/null +++ b/tests/integration/browser/relay/scripting.integration.test.ts @@ -0,0 +1,5 @@ +import {expectRelayScripting} from "./scripting-utils"; + +jest.setTimeout(90_000); +test("Chrome MV3 preserves Relay scripting responses and missing-manager errors", () => + expectRelayScripting("chrome", 3)); diff --git a/tests/integration/browser/relay/scripting/adnbn.config.ts b/tests/integration/browser/relay/scripting/adnbn.config.ts new file mode 100644 index 00000000..0afafe19 --- /dev/null +++ b/tests/integration/browser/relay/scripting/adnbn.config.ts @@ -0,0 +1,8 @@ +import {defineConfig} from "adnbn"; + +export default defineConfig({ + name: "Relay scripting protocol", + version: "1.0.0", + manifest: {permissions: ["tabs"], host_permissions: ["http://127.0.0.1/*"]}, + specific: {gecko: {id: "relay-scripting@adnbn.test"}}, +}); diff --git a/tests/integration/browser/relay/scripting/package.json b/tests/integration/browser/relay/scripting/package.json new file mode 100644 index 00000000..22aa8402 --- /dev/null +++ b/tests/integration/browser/relay/scripting/package.json @@ -0,0 +1,9 @@ +{ + "name": "adnbn-test-relay-scripting", + "private": true, + "type": "module", + "version": "1.0.0", + "dependencies": { + "adnbn": "file:../../../../.." + } +} diff --git a/tests/integration/browser/relay/scripting/site/empty.html b/tests/integration/browser/relay/scripting/site/empty.html new file mode 100644 index 00000000..d0d0cd31 --- /dev/null +++ b/tests/integration/browser/relay/scripting/site/empty.html @@ -0,0 +1,6 @@ + + + + No Relay registration + + diff --git a/tests/integration/browser/relay/scripting/site/top.html b/tests/integration/browser/relay/scripting/site/top.html new file mode 100644 index 00000000..e60ecf47 --- /dev/null +++ b/tests/integration/browser/relay/scripting/site/top.html @@ -0,0 +1,6 @@ + + + + Relay scripting probe + + diff --git a/tests/integration/browser/relay/scripting/src/background.ts b/tests/integration/browser/relay/scripting/src/background.ts new file mode 100644 index 00000000..46a0a8a7 --- /dev/null +++ b/tests/integration/browser/relay/scripting/src/background.ts @@ -0,0 +1,95 @@ +import {createTab, getTab, removeTab, queryTabs, onTabUpdated} from "adnbn/browser"; +import {defineBackground, getRelay, RelayAllFrames} from "adnbn"; + +const errorDetails = (error: unknown) => { + if (error instanceof Error) { + return {name: error.name, message: error.message}; + } + + throw error; +}; + +export default defineBackground({ + main() { + const started = new Set(); + const run = (tabId: number, url?: string) => { + if (!url?.startsWith("http://127.0.0.1:") || !url.endsWith("/top.html") || started.has(tabId)) { + return; + } + + started.add(tabId); + + void (async () => { + const relay = getRelay("probe", tabId); + + await relay.ready(); + + const empty = await relay.empty(); + const nullable = await relay.nullable(); + const thrown = await relay.fail().catch(errorDetails); + const rejected = await relay.reject().catch(errorDetails); + const emptyUrl = url.replace("/top.html", "/empty.html"); + const blank = await createTab({url: emptyUrl, active: true}); + + try { + const deadline = Date.now() + 10000; + + while (true) { + const current = await getTab(blank.id!); + + if (current.status === "complete" && current.url === emptyUrl) { + break; + } + + if (Date.now() >= deadline) { + throw new Error("Empty Relay target did not finish navigation"); + } + + await new Promise(resolve => setTimeout(resolve, 20)); + } + + const any = await getRelay("probe", {tabId: blank.id!, allFrames: RelayAllFrames.Any}).ready(); + const missing = await getRelay("probe", blank.id!).ready().catch(errorDetails); + + await relay.report({ + empty: empty === undefined, + nullable: nullable === null, + thrown, + rejected, + any: any.map(outcome => + outcome.status === "rejected" + ? { + status: outcome.status, + kind: outcome.error.kind, + name: outcome.error.name, + message: outcome.error.message, + } + : {status: outcome.status} + ), + missing, + }); + } finally { + await removeTab(blank.id!); + } + })().catch(async error => { + await getRelay("probe", tabId) + .report({error: String(error)}) + .catch(() => {}); + console.error("Relay scripting probe failed", error); + }); + }; + + onTabUpdated((tabId, change, tab) => { + if (change.status === "complete") { + run(tabId, tab.url); + } + }); + void queryTabs({}).then(tabs => { + for (const tab of tabs) { + if (tab.id !== undefined) { + run(tab.id, tab.url); + } + } + }); + }, +}); diff --git a/tests/integration/browser/relay/scripting/src/probe.relay.ts b/tests/integration/browser/relay/scripting/src/probe.relay.ts new file mode 100644 index 00000000..123ecc99 --- /dev/null +++ b/tests/integration/browser/relay/scripting/src/probe.relay.ts @@ -0,0 +1,21 @@ +import {defineRelay, RelayMethod} from "adnbn"; + +export default defineRelay({ + name: "probe", + method: RelayMethod.Scripting, + declarative: true, + matches: ["http://127.0.0.1/*"], + excludeMatches: ["http://127.0.0.1/empty.html"], + init: () => ({ + ready: () => true, + empty: (): void => {}, + nullable: (): null => null, + fail: (): never => { + throw new TypeError("Remote failure"); + }, + reject: (): Promise => Promise.reject(new RangeError("Async failure")), + report: (result: object): void => { + document.body.dataset.result = JSON.stringify(result); + }, + }), +}); diff --git a/tests/integration/browser/relay/scripting/tsconfig.json b/tests/integration/browser/relay/scripting/tsconfig.json new file mode 100644 index 00000000..983db6c9 --- /dev/null +++ b/tests/integration/browser/relay/scripting/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "./.adnbn/tsconfig.json", + "include": [ + "src", + ".adnbn/**/*.d.ts", + "adnbn.config.ts" + ] +} diff --git a/tests/integration/browser/utils/chrome.ts b/tests/integration/browser/utils/chrome.ts index 9fb72127..93e6b1a5 100644 --- a/tests/integration/browser/utils/chrome.ts +++ b/tests/integration/browser/utils/chrome.ts @@ -17,6 +17,7 @@ export const findChromeBinary = (rootDir: string): string | undefined => { [path.join(rootDir, "node_modules", "chrome-launcher", "bin", "print-chrome-path.cjs")], {encoding: "utf8"} ); + const chromePath = result.status === 0 ? result.stdout.trim() : ""; return chromePath || undefined; @@ -28,8 +29,8 @@ export const targets = async (port: number): Promise => { return response.json() as Promise; }; -export const browserVersion = async (port: number): Promise<{webSocketDebuggerUrl: string}> => { - const response = await fetch(`http://127.0.0.1:${port}/json/version`, {signal: AbortSignal.timeout(5_000)}); +export const browserVersion = async (port: number, timeout = 5_000): Promise<{webSocketDebuggerUrl: string}> => { + const response = await fetch(`http://127.0.0.1:${port}/json/version`, {signal: AbortSignal.timeout(timeout)}); return response.json() as Promise<{webSocketDebuggerUrl: string}>; }; diff --git a/tests/integration/browser/utils/session.ts b/tests/integration/browser/utils/session.ts index a3523846..d0dacb5a 100644 --- a/tests/integration/browser/utils/session.ts +++ b/tests/integration/browser/utils/session.ts @@ -3,18 +3,35 @@ import {mkdtemp, rm} from "fs/promises"; import os from "os"; import path from "path"; -import {getFreePort, stop, waitFor} from "./browser"; +import {getFreePort, stop} from "./browser"; import {browserVersion, findChromeBinary} from "./chrome"; import CdpClient from "./CdpClient"; import BidiClient from "./BidiClient"; import {findFirefoxBinary} from "./firefox"; +import {waitForBrowserStartup} from "./startup"; -/** A real extension browser session; owns its process, connection and temporary profile. */ -export const startBrowserSession = async (name: "chrome" | "firefox", rootDir: string, extensionDir: string) => { +interface BrowserSessionOptions { + startupTimeout?: number; + createPage?: boolean; + firefoxEvents?: string[]; +} + +/** Owns the browser process, protocol connection and temporary profile. */ +export const startBrowserSession = async ( + name: "chrome" | "firefox", + rootDir: string, + extensionDir: string, + {startupTimeout = 30_000, createPage = true, firefoxEvents = []}: BrowserSessionOptions = {} +) => { const binary = name === "chrome" ? findChromeBinary(rootDir) : findFirefoxBinary(); - if (!binary || !path.isAbsolute(binary)) throw new Error(`Install ${name} or set its ADNBN_*_BIN path`); + + if (!binary || !path.isAbsolute(binary)) { + throw new Error(`Install ${name} or set its ADNBN_*_BIN path`); + } + const port = await getFreePort(); - const profile = await mkdtemp(path.join(os.tmpdir(), "adnbn-isolation-")); + const profile = await mkdtemp(path.join(os.tmpdir(), "adnbn-browser-")); + const args = name === "chrome" ? [ @@ -22,6 +39,8 @@ export const startBrowserSession = async (name: "chrome" | "firefox", rootDir: s "--no-sandbox", "--no-first-run", "--no-default-browser-check", + "--enable-logging=stderr", + "--v=0", `--remote-debugging-port=${port}`, `--user-data-dir=${profile}`, "about:blank", @@ -35,68 +54,189 @@ export const startBrowserSession = async (name: "chrome" | "firefox", rootDir: s String(port), "about:blank", ]; + + const startedAt = performance.now(); const process = spawn(binary, args, {stdio: ["ignore", "ignore", "pipe"]}); let output = ""; + let spawnError: Error | undefined; + + process.once("error", error => { + spawnError = error; + }); + process.stderr?.on("data", chunk => { output += chunk; }); + let chrome: CdpClient | undefined; let firefox: BidiClient | undefined; + const close = async () => { try { await chrome?.close(); + if (firefox) { await firefox.send("session.end", {}, 2000).catch(() => undefined); await firefox.close(); } } finally { - await stop(process); - await rm(profile, {recursive: true, force: true, maxRetries: 5, retryDelay: 200}); + try { + if (process.pid) { + await stop(process); + } + } finally { + await rm(profile, {recursive: true, force: true, maxRetries: 5, retryDelay: 200}); + } } }; + + let startupReported = false; + + const reportStartup = (status: "ready" | "failed", error?: unknown) => { + startupReported = true; + + console.info( + "[browser-startup] " + + JSON.stringify({ + browser: name, + testName: expect.getState().currentTestName, + durationMs: Math.round(performance.now() - startedAt), + timeoutMs: startupTimeout, + status, + ...(error ? {error: String(error)} : {}), + }) + ); + }; + try { - let version: string; - let navigate: (url: string) => Promise; - let evaluate: (expression: string) => Promise; - if (name === "chrome") { - const {webSocketDebuggerUrl} = await waitFor(() => browserVersion(port)); - chrome = await CdpClient.connect(webSocketDebuggerUrl); - version = (await chrome.send("Browser.getVersion")).product; - await chrome.send("Extensions.loadUnpacked", {path: extensionDir}); - const {targetId} = await chrome.send("Target.createTarget", {url: "about:blank"}); - const {sessionId} = await chrome.send("Target.attachToTarget", {targetId, flatten: true}); - await chrome.send("Runtime.enable", {}, sessionId); - await chrome.send("Page.enable", {}, sessionId); - navigate = url => chrome!.send("Page.navigate", {url}, sessionId); - evaluate = async expression => { - const result = await chrome!.send( - "Runtime.evaluate", - {expression, awaitPromise: true, returnByValue: true}, - sessionId - ); - if (result.exceptionDetails) throw new Error(JSON.stringify(result.exceptionDetails)); - return result.result.value; - }; + const version = await waitForBrowserStartup( + async remaining => { + if (name === "chrome") { + const {webSocketDebuggerUrl} = await browserVersion(port, Math.min(5_000, remaining())); + chrome = await CdpClient.connect(webSocketDebuggerUrl, remaining()); + + try { + return (await chrome.send("Browser.getVersion", {}, undefined, remaining())).product as string; + } catch (error) { + await chrome.close(); + chrome = undefined; + + throw error; + } + } + + firefox = await BidiClient.connect(`ws://127.0.0.1:${port}/session`, Math.min(1_000, remaining())); + + try { + const session = await firefox.send("session.new", {capabilities: {alwaysMatch: {}}}, remaining()); + + return "Firefox/" + session.capabilities.browserVersion; + } catch (error) { + await firefox.close(); + firefox = undefined; + + throw error; + } + }, + { + timeout: startupTimeout, + startedAt, + description: `${name} startup`, + checkProcess: () => { + if (spawnError) { + throw spawnError; + } + + if (process.exitCode !== null || process.signalCode !== null) { + throw new Error(`${name} exited before startup: ${process.exitCode ?? process.signalCode}`); + } + }, + } + ); + + reportStartup("ready"); + + let extensionId: string; + + let navigate: (url: string) => Promise = async () => { + throw new Error("This session delegates page creation to the test"); + }; + + let evaluate: (expression: string) => Promise = navigate; + + if (chrome) { + extensionId = (await chrome.send("Extensions.loadUnpacked", {path: extensionDir})).id; + + if (createPage) { + const {targetId} = await chrome.send("Target.createTarget", {url: "about:blank"}); + const {sessionId} = await chrome.send("Target.attachToTarget", {targetId, flatten: true}); + + await chrome.send("Runtime.enable", {}, sessionId); + await chrome.send("Page.enable", {}, sessionId); + navigate = url => chrome!.send("Page.navigate", {url}, sessionId); + + evaluate = async expression => { + const result = await chrome!.send( + "Runtime.evaluate", + {expression, awaitPromise: true, returnByValue: true}, + sessionId + ); + + if (result.exceptionDetails) { + throw new Error(JSON.stringify(result.exceptionDetails)); + } + + return result.result.value; + }; + } } else { - firefox = await waitFor(() => BidiClient.connect(`ws://127.0.0.1:${port}/session`)); - const session = await firefox.send("session.new", {capabilities: {alwaysMatch: {}}}); - version = "Firefox/" + session.capabilities.browserVersion; - await firefox.send("webExtension.install", {extensionData: {path: extensionDir, type: "path"}}); - const {context} = await firefox.send("browsingContext.create", {type: "tab"}); - navigate = url => firefox!.send("browsingContext.navigate", {context, url, wait: "complete"}); - evaluate = expression => firefox!.evaluate(context, expression); + if (firefoxEvents.length) { + await firefox!.send("session.subscribe", {events: firefoxEvents}); + } + + extensionId = ( + await firefox!.send("webExtension.install", { + extensionData: {path: extensionDir, type: "path"}, + }) + ).extension; + + if (createPage) { + const {context} = await firefox!.send("browsingContext.create", {type: "tab"}); + + navigate = url => firefox!.send("browsingContext.navigate", {context, url, wait: "complete"}); + evaluate = expression => firefox!.evaluate(context, expression); + } } + return { version, + extensionId, + port, + chrome, + firefox, navigate, evaluate, close, + get output() { + return output; + }, get errors() { return chrome?.runtimeErrors ?? firefox?.runtimeErrors ?? []; }, }; } catch (error) { - await close(); + if (!startupReported) { + reportStartup("failed", error); + } + + try { + await close(); + } catch (cleanupError) { + throw new AggregateError([error, cleanupError], `${name} failed; browser output: ${output}`); + } + throw new Error(`${String(error)}; browser output: ${output}`, {cause: error}); } }; + +export type BrowserSession = Awaited>; diff --git a/tests/integration/browser/utils/startup.test.ts b/tests/integration/browser/utils/startup.test.ts new file mode 100644 index 00000000..3382a81e --- /dev/null +++ b/tests/integration/browser/utils/startup.test.ts @@ -0,0 +1,89 @@ +import {waitForBrowserStartup} from "./startup"; + +afterEach(() => { + jest.useRealTimers(); +}); + +test("startup retries share one deadline, including time spent connecting", async () => { + jest.useFakeTimers(); + const startedAt = performance.now(); + const budgets: number[] = []; + const failure = new Error("debugging endpoint unavailable"); + const result = waitForBrowserStartup( + async remaining => { + budgets.push(remaining()); + await new Promise(resolve => setTimeout(resolve, 400)); + + throw failure; + }, + {timeout: 900, startedAt, description: "chrome startup", checkProcess: () => {}} + ); + const assertion = expect(result).rejects.toMatchObject({ + message: "Timed out waiting for chrome startup after 900 ms", + cause: failure, + }); + + await jest.advanceTimersByTimeAsync(900); + await assertion; + expect(budgets).toEqual([900, 400]); +}); + +test("a later successful connection receives only the remaining startup budget", async () => { + jest.useFakeTimers(); + let attempts = 0; + const result = waitForBrowserStartup( + async remaining => { + attempts++; + + if (attempts === 1) { + throw new Error("not listening yet"); + } + + return remaining(); + }, + {timeout: 1_000, startedAt: performance.now(), description: "firefox startup", checkProcess: () => {}} + ); + + await jest.advanceTimersByTimeAsync(100); + await expect(result).resolves.toBe(900); +}); + +test("browser process failure aborts startup without retrying until the deadline", async () => { + const failure = new Error("browser exited with code 1"); + let attempts = 0; + + await expect( + waitForBrowserStartup( + async () => { + attempts++; + + return true; + }, + { + timeout: 30_000, + startedAt: performance.now(), + description: "firefox startup", + checkProcess: () => { + throw failure; + }, + } + ) + ).rejects.toBe(failure); + expect(attempts).toBe(0); +}); + +test("a readiness response delivered after the deadline is not accepted", async () => { + jest.useFakeTimers(); + const result = waitForBrowserStartup( + async () => { + await new Promise(resolve => setTimeout(resolve, 200)); + + return "ready"; + }, + {timeout: 100, startedAt: performance.now(), description: "chrome startup", checkProcess: () => {}} + ); + const assertion = expect(result).rejects.toThrow("Timed out waiting for chrome startup after 100 ms"); + + await jest.advanceTimersByTimeAsync(200); + await assertion; +}); diff --git a/tests/integration/browser/utils/startup.ts b/tests/integration/browser/utils/startup.ts new file mode 100644 index 00000000..203029eb --- /dev/null +++ b/tests/integration/browser/utils/startup.ts @@ -0,0 +1,46 @@ +interface BrowserStartupOptions { + timeout: number; + startedAt: number; + description: string; + checkProcess(): void; +} + +/** Each protocol operation must use remaining() as its own cancellable timeout. */ +export const waitForBrowserStartup = async ( + connect: (remaining: () => number) => Promise, + {timeout, startedAt, description, checkProcess}: BrowserStartupOptions +): Promise => { + const remaining = () => Math.max(0, Math.ceil(timeout - (performance.now() - startedAt))); + let lastError: unknown; + + while (remaining() > 0) { + checkProcess(); + + try { + const value = await connect(() => { + const budget = remaining(); + + if (!budget) { + throw new Error(`${description} deadline reached`); + } + + return budget; + }); + + if (remaining() > 0) { + return value; + } + } catch (error) { + lastError = error; + } + + checkProcess(); + const delay = Math.min(100, remaining()); + + if (delay > 0) { + await new Promise(resolve => setTimeout(resolve, delay)); + } + } + + throw new Error(`Timed out waiting for ${description} after ${timeout} ms`, {cause: lastError}); +}; diff --git a/tests/integration/browser/view/render.integration.test.ts b/tests/integration/browser/view/render.integration.test.ts index d59889ca..d126f3d0 100644 --- a/tests/integration/browser/view/render.integration.test.ts +++ b/tests/integration/browser/view/render.integration.test.ts @@ -1,33 +1,21 @@ -import {mkdtemp, readFile, rm} from "fs/promises"; -import os from "os"; +import {readFile} from "fs/promises"; import path from "path"; -import {spawn, type ChildProcess} from "child_process"; -import {browserVersion, findChromeBinary} from "../utils/chrome"; +import {startBrowserSession, type BrowserSession} from "../utils/session"; import CdpClient from "../utils/CdpClient"; -import {getFreePort, stop, waitFor} from "../utils/browser"; +import {waitFor} from "../utils/browser"; import {startIntegrationSite, type IntegrationSite} from "../utils/site"; import {createIntegrationFixture, type IntegrationFixture} from "../../utils/fixture"; const rootDir = path.resolve(__dirname, "..", "..", "..", ".."); -const chromeBinary = findChromeBinary(rootDir); jest.setTimeout(90_000); test("Chrome MV3 renders offscreen, sandbox, page and content views through the injected builders", async () => { - if (!chromeBinary || !path.isAbsolute(chromeBinary)) { - throw new Error( - "Chrome is not installed or could not be found. Install Chrome or set ADNBN_CHROME_BIN to its absolute executable path." - ); - } - - const userDataDir = await mkdtemp(path.join(os.tmpdir(), "adnbn-view-render-")); - const debuggingPort = await getFreePort(); - let chrome: ChildProcess | undefined; + let session: BrowserSession | undefined; let browser: CdpClient | undefined; let fixture: IntegrationFixture | undefined; let site: IntegrationSite | undefined; - let chromeOutput = ""; try { fixture = await createIntegrationFixture(rootDir, path.join(__dirname, "render")); @@ -39,26 +27,9 @@ test("Chrome MV3 renders offscreen, sandbox, page and content views through the expect(manifest.manifest_version).toBe(3); expect(manifest.permissions).toEqual(["offscreen"]); - chrome = spawn( - chromeBinary, - [ - "--headless=new", - "--no-sandbox", - "--no-first-run", - "--no-default-browser-check", - `--remote-debugging-port=${debuggingPort}`, - `--user-data-dir=${userDataDir}`, - "about:blank", - ], - {stdio: ["ignore", "ignore", "pipe"]} - ); - chrome.stderr?.on("data", chunk => (chromeOutput += chunk)); - - const {webSocketDebuggerUrl} = await waitFor(() => browserVersion(debuggingPort)); - browser = await CdpClient.connect(webSocketDebuggerUrl); - - const extension = await browser.send("Extensions.loadUnpacked", {path: extensionDir}); - const extensionId = extension.id as string | undefined; + session = await startBrowserSession("chrome", rootDir, extensionDir, {createPage: false}); + browser = session.chrome!; + const extensionId = session.extensionId; if (!extensionId) { throw new Error("Chrome did not return an extension ID after loading the view fixture"); @@ -97,6 +68,7 @@ test("Chrome MV3 renders offscreen, sandbox, page and content views through the }; const popup = await open(`chrome-extension://${extensionId}/popup.html`); + const result = JSON.parse( await waitFor(() => popup("document.getElementById('result')?.value || undefined"), 30_000, "view calls") ); @@ -120,6 +92,7 @@ test("Chrome MV3 renders offscreen, sandbox, page and content views through the }); const page = await open(`${site.origin}/top.html`); + const note = await waitFor(() => page(`(() => { const note = [...document.body.children].find(element => element.textContent.includes("React content text")); @@ -131,16 +104,11 @@ test("Chrome MV3 renders offscreen, sandbox, page and content views through the expect(note).toEqual({text: "React content text", markup: false}); expect(browser.runtimeErrors).toEqual([]); } catch (error) { - throw new Error(`${String(error)}; Chrome output: ${chromeOutput}`, {cause: error}); + throw new Error(`${String(error)}; Chrome output: ${session?.output ?? ""}`, {cause: error}); } finally { - await browser?.close(); - - if (chrome) { - await stop(chrome); - } + await session?.close(); await site?.close(); await fixture?.dispose(); - await rm(userDataDir, {recursive: true, force: true, maxRetries: 5, retryDelay: 200}); } }); diff --git a/tests/integration/build/action/action.integration.test.ts b/tests/integration/build/action/action.integration.test.ts new file mode 100644 index 00000000..15ab1d55 --- /dev/null +++ b/tests/integration/build/action/action.integration.test.ts @@ -0,0 +1,92 @@ +import {copyFile, readFile, stat} from "fs/promises"; +import path from "path"; + +import {createIntegrationFixture} from "../../utils/fixture"; + +const projectRoot = path.resolve(__dirname, "..", "..", "..", ".."); +const fixtureDir = path.join(__dirname, "fixture"); + +jest.setTimeout(90_000); + +describe.each([ + {browser: "chrome", manifestVersion: 3, actionField: "action"}, + {browser: "chrome", manifestVersion: 2, actionField: "browser_action"}, + {browser: "firefox", manifestVersion: 3, actionField: "action"}, + {browser: "firefox", manifestVersion: 2, actionField: "browser_action"}, +] as const)("$browser MV$manifestVersion action", ({browser, manifestVersion, actionField}) => { + test.each([ + {scenario: "config only", entry: undefined, popup: false, command: false, icon: "active", title: "action"}, + { + scenario: "execute action", + entry: "action.command.ts", + popup: false, + command: true, + icon: "active", + title: "action", + }, + { + scenario: "popup overrides", + entry: "full.popup.ts", + popup: true, + command: false, + icon: "popup", + title: "popup", + }, + { + scenario: "popup title only", + entry: "title.popup.ts", + popup: true, + command: false, + icon: "active", + title: "popup", + }, + { + scenario: "unapplied popup", + entry: "unapplied.popup.ts", + popup: false, + command: false, + icon: "active", + title: "action", + }, + ])("$scenario", async ({entry, popup, command, icon, title}) => { + const fixture = await createIntegrationFixture(projectRoot, fixtureDir); + + try { + if (entry) { + const filename = command ? "action.command.ts" : "popup.ts"; + + await copyFile( + path.join(fixture.directory, "variants", entry), + path.join(fixture.directory, "src", filename) + ); + } + + const extensionDir = await fixture.build({browser, manifestVersion}); + const manifest = JSON.parse(await readFile(path.join(extensionDir, "manifest.json"), "utf8")); + const action = manifest[actionField]; + const messages = JSON.parse( + await readFile(path.join(extensionDir, "_locales", "en", "messages.json"), "utf8") + ); + + expect(action).toEqual({ + default_icon: {16: `/icons/${icon}-16.png`}, + default_title: `__MSG_${title}_title__`, + ...(popup ? {default_popup: "popup.html"} : {}), + }); + expect(manifest.icons).toEqual({16: "/icons/16.png"}); + expect(messages.action_title.message).toBe("Run the action"); + expect(messages.popup_title.message).toBe("Open the popup"); + expect((await stat(path.join(extensionDir, "icons", `${icon}-16.png`))).isFile()).toBe(true); + + if (command) { + expect(manifest.commands).toBeDefined(); + expect(manifest.background).toBeDefined(); + } else { + expect(manifest.commands).toBeUndefined(); + expect(manifest.background).toBeUndefined(); + } + } finally { + await fixture.dispose(); + } + }); +}); diff --git a/tests/integration/build/action/fixture/adnbn.config.ts b/tests/integration/build/action/fixture/adnbn.config.ts new file mode 100644 index 00000000..be601078 --- /dev/null +++ b/tests/integration/build/action/fixture/adnbn.config.ts @@ -0,0 +1,13 @@ +import {defineConfig, type ActionOptions} from "adnbn"; + +const action: ActionOptions = { + icon: "active", + title: "@action.title", +}; + +export default defineConfig({ + name: "Action Integration", + version: "1.0.0", + icon: "default", + action, +}); diff --git a/tests/integration/build/action/fixture/package.json b/tests/integration/build/action/fixture/package.json new file mode 100644 index 00000000..bd4063df --- /dev/null +++ b/tests/integration/build/action/fixture/package.json @@ -0,0 +1,9 @@ +{ + "name": "adnbn-test-action", + "private": true, + "type": "module", + "version": "1.0.0", + "dependencies": { + "adnbn": "file:../../../../../.." + } +} diff --git a/tests/integration/build/action/fixture/src/icons/16.png b/tests/integration/build/action/fixture/src/icons/16.png new file mode 100644 index 00000000..0a3e580f Binary files /dev/null and b/tests/integration/build/action/fixture/src/icons/16.png differ diff --git a/tests/integration/build/action/fixture/src/icons/active16.png b/tests/integration/build/action/fixture/src/icons/active16.png new file mode 100644 index 00000000..0a3e580f Binary files /dev/null and b/tests/integration/build/action/fixture/src/icons/active16.png differ diff --git a/tests/integration/build/action/fixture/src/icons/popup16.png b/tests/integration/build/action/fixture/src/icons/popup16.png new file mode 100644 index 00000000..0a3e580f Binary files /dev/null and b/tests/integration/build/action/fixture/src/icons/popup16.png differ diff --git a/tests/integration/build/action/fixture/src/locales/en.json b/tests/integration/build/action/fixture/src/locales/en.json new file mode 100644 index 00000000..cb6e3b46 --- /dev/null +++ b/tests/integration/build/action/fixture/src/locales/en.json @@ -0,0 +1,8 @@ +{ + "action": { + "title": "Run the action" + }, + "popup": { + "title": "Open the popup" + } +} diff --git a/tests/integration/build/action/fixture/tsconfig.json b/tests/integration/build/action/fixture/tsconfig.json new file mode 100644 index 00000000..eadf1be8 --- /dev/null +++ b/tests/integration/build/action/fixture/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./.adnbn/tsconfig.json" +} diff --git a/tests/integration/build/action/fixture/variants/action.command.ts b/tests/integration/build/action/fixture/variants/action.command.ts new file mode 100644 index 00000000..75758875 --- /dev/null +++ b/tests/integration/build/action/fixture/variants/action.command.ts @@ -0,0 +1,8 @@ +import {defineExecuteActionCommand} from "adnbn"; + +export default defineExecuteActionCommand({ + defaultKey: "Ctrl+Shift+Y", + execute() { + console.info("Action clicked"); + }, +}); diff --git a/tests/integration/build/action/fixture/variants/full.popup.ts b/tests/integration/build/action/fixture/variants/full.popup.ts new file mode 100644 index 00000000..c00c4870 --- /dev/null +++ b/tests/integration/build/action/fixture/variants/full.popup.ts @@ -0,0 +1,7 @@ +import {definePopup} from "adnbn"; + +export default definePopup({ + icon: "popup", + title: "@popup.title", + render: "Popup", +}); diff --git a/tests/integration/build/action/fixture/variants/title.popup.ts b/tests/integration/build/action/fixture/variants/title.popup.ts new file mode 100644 index 00000000..9cd28351 --- /dev/null +++ b/tests/integration/build/action/fixture/variants/title.popup.ts @@ -0,0 +1,6 @@ +import {definePopup} from "adnbn"; + +export default definePopup({ + title: "@popup.title", + render: "Popup", +}); diff --git a/tests/integration/build/action/fixture/variants/unapplied.popup.ts b/tests/integration/build/action/fixture/variants/unapplied.popup.ts new file mode 100644 index 00000000..f2148b90 --- /dev/null +++ b/tests/integration/build/action/fixture/variants/unapplied.popup.ts @@ -0,0 +1,8 @@ +import {definePopup} from "adnbn"; + +export default definePopup({ + apply: false, + icon: "popup", + title: "@popup.title", + render: "Popup", +}); diff --git a/tests/integration/build/cli/BuildSession.integration.test.ts b/tests/integration/build/cli/BuildSession.integration.test.ts new file mode 100644 index 00000000..bb9e343e --- /dev/null +++ b/tests/integration/build/cli/BuildSession.integration.test.ts @@ -0,0 +1,159 @@ +import {fork} from "node:child_process"; +import {cp, mkdir, mkdtemp, readFile, rm, symlink, access} from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; + +import BuildSession, {BuildSessionError} from "../../utils/BuildSession"; + +const projectRoot = path.resolve(__dirname, "../../../.."); +const fixtures = path.join(__dirname, "fixtures"); +let directory: string; +let root: string; +let session: BuildSession; + +jest.setTimeout(60_000); + +beforeEach(async () => { + directory = await mkdtemp(path.join(os.tmpdir(), "adnbn-session-test-")); + root = path.join(directory, "project a"); + await cp(path.join(fixtures, "root-dir"), root, {recursive: true}); + await cp(path.join(fixtures, "build-session/adnbn.config.ts"), path.join(root, "adnbn.config.ts")); + await mkdir(path.join(root, "node_modules")); + await symlink( + projectRoot, + path.join(root, "node_modules/adnbn"), + process.platform === "win32" ? "junction" : "dir" + ); + + session = await BuildSession.create(projectRoot); +}); + +afterEach(async () => { + delete process.env.ADNBN_SESSION_CASE; + await session?.dispose(); + await rm(directory, {recursive: true, force: true, maxRetries: 5, retryDelay: 200}); +}); + +test("reuses one child with fixed cwd across A-B-A, rejects overlap and restores the parent", async () => { + const cwd = process.cwd(); + const environment = {...process.env}; + const rootB = path.join(directory, "project b"); + await cp(root, rootB, {recursive: true, filter: source => path.basename(source) !== "node_modules"}); + await mkdir(path.join(rootB, "node_modules")); + await symlink( + projectRoot, + path.join(rootB, "node_modules/adnbn"), + process.platform === "win32" ? "junction" : "dir" + ); + + await cp(path.join(fixtures, "root-dir-b"), path.join(rootB, "src"), {recursive: true}); + const first = session.build(root); + await expect(session.build(rootB)).rejects.toThrow("pending request"); + const samples = [await first, await session.build(rootB, "firefox"), await session.build(root)]; + + expect(samples.map(sample => sample.pid)).toEqual([session.pid, session.pid, session.pid]); + expect(samples.map(sample => sample.build)).toEqual([1, 2, 3]); + expect(samples.map(sample => sample.cwd)).toEqual(Array(3).fill(session.directory)); + expect(samples.every(sample => sample.rss > 0 && sample.heapUsed > 0)).toBe(true); + expect(await readFile(path.join(root, "dist/myapp-chrome-mv3/newtab.html"), "utf8")).toContain("Absolute root A"); + expect(await readFile(path.join(rootB, "dist/myapp-firefox-mv3/newtab.html"), "utf8")).toContain("Absolute root B"); + expect(process.cwd()).toBe(cwd); + expect({...process.env}).toEqual(environment); + await session.dispose(); + await expect(access(session.directory)).rejects.toMatchObject({code: "ENOENT"}); + await expect(session.build(root)).rejects.toThrow("closed"); +}); + +test("preserves the original error cause and can build after a config failure", async () => { + process.env.ADNBN_SESSION_CASE = "error"; + await expect(session.build(root)).rejects.toMatchObject({ + message: expect.stringContaining("Session config failed"), + cause: {message: "Original config cause"}, + }); + + delete process.env.ADNBN_SESSION_CASE; + await expect(session.build(root)).resolves.toMatchObject({pid: session.pid, build: 2}); +}); + +test("serializes compiler diagnostics and recovers after a failed compilation", async () => { + await cp(path.join(fixtures, "build-session/invalid-newtab.ts"), path.join(root, "src/newtab.ts")); + const error = await session.build(root).catch(error => error); + expect(error).toBeInstanceOf(BuildSessionError); + expect(error.stats).toContain("missing-runtime"); + await cp(path.join(fixtures, "root-dir/src/newtab.ts"), path.join(root, "src/newtab.ts")); + await expect(session.build(root)).resolves.toMatchObject({build: 2}); +}); + +test("kills a timed-out child, waits for close and never retries", async () => { + process.env.ADNBN_SESSION_CASE = "hang"; + await expect(session.build(root, "chrome", 3, 3_000)).rejects.toThrow("timed out"); + expect(() => process.kill(session.pid!, 0)).toThrow(); + await expect(session.build(root)).rejects.toThrow("closed"); +}); + +test("rejects an unexpected child exit without leaving a pending request", async () => { + process.env.ADNBN_SESSION_CASE = "exit"; + await expect(session.build(root)).rejects.toThrow("exited with 9"); + await expect(session.build(root)).rejects.toThrow("closed"); +}); + +test("disposing a pending build rejects it and removes the session after close", async () => { + process.env.ADNBN_SESSION_CASE = "hang"; + const pending = session.build(root); + const rejected = expect(pending).rejects.toThrow("disposed during a pending request"); + await session.dispose(); + await rejected; + expect(() => process.kill(session.pid!, 0)).toThrow(); + await expect(access(session.directory)).rejects.toMatchObject({code: "ENOENT"}); +}); + +test.each([false, true])("child exits on IPC disconnect (active build: %s)", async active => { + const child = fork(path.join(projectRoot, "tests/integration/utils/build-session-child.mjs"), [projectRoot], { + cwd: session.directory, + execArgv: [], + stdio: ["ignore", "ignore", "pipe", "ipc"], + }); + + let stderr = ""; + child.stderr!.on("data", chunk => { + stderr += chunk; + }); + + const exited = new Promise(resolve => child.once("exit", (code, signal) => resolve({code, signal}))); + const ready = new Promise((resolve, reject) => { + const timer = setTimeout(() => reject(new Error(`Child did not become ready: ${stderr}`)), 15_000); + child.on("message", message => { + if ((message as {ready?: boolean; started?: boolean})[active ? "started" : "ready"]) { + clearTimeout(timer); + resolve(); + } + }); + + child.once("error", error => { + clearTimeout(timer); + reject(error); + }); + }); + + const deadline = setTimeout(() => child.kill("SIGKILL"), 20_000); + + try { + if (active) { + child.send({ + id: 1, + rootDir: root, + browser: "chrome", + manifestVersion: 3, + environment: {...process.env, ADNBN_SESSION_CASE: "hang"}, + }); + } + + await ready; + child.disconnect(); + await expect(exited).resolves.toEqual({code: 0, signal: null}); + } finally { + clearTimeout(deadline); + child.kill("SIGKILL"); + await exited; + } +}); diff --git a/tests/integration/build/cli/build-api.integration.test.ts b/tests/integration/build/cli/build-api.integration.test.ts new file mode 100644 index 00000000..e22c5a3d --- /dev/null +++ b/tests/integration/build/cli/build-api.integration.test.ts @@ -0,0 +1,70 @@ +import {spawnSync} from "node:child_process"; +import {cpSync, mkdtempSync, rmSync} from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import {stripVTControlCharacters} from "node:util"; + +const scripts = path.join(__dirname, "scripts"); +const appModule = path.resolve(__dirname, "../../../../dist/cli/builders/app/index.js"); + +test.each([ + "sequence", + "sequence-mts", + "sequence-cts", + "unsupported-js", + "unsupported-mjs", + "unsupported-cjs", + "overlap", + "overlap-close", + "config", + "startup", + "bundler", + "run", + "compilation", + "close", +])("buildApp restores environment and permits the next build after %s", scenario => { + const root = mkdtempSync(path.join(os.tmpdir(), "adnbn-app-environment-")); + const env = Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.startsWith("ADNBN_BUILD_"))); + + try { + const result = spawnSync( + process.execPath, + [path.join(scripts, "verify-environment.mjs"), appModule, root, scenario], + { + env, + encoding: "utf8", + timeout: 30_000, + maxBuffer: 4 * 1024 * 1024, + } + ); + + expect(result.error).toBeUndefined(); + expect({status: result.status, stderr: result.stderr}).toEqual({status: 0, stderr: ""}); + expect(result.stdout).toContain("Environment contract verified"); + } finally { + rmSync(root, {recursive: true, force: true}); + } +}); + +test("the internal app build returns Stats after shutdown without printing them", () => { + const root = mkdtempSync(path.join(os.tmpdir(), "adnbn-app-lifecycle-")); + const artifact = path.join(root, "dist/exit-code-chrome-mv3"); + + try { + cpSync(path.join(__dirname, "fixtures/exit-code/lifecycle"), root, {recursive: true}); + const result = spawnSync(process.execPath, [path.join(scripts, "build-api.mjs"), appModule, root, artifact], { + cwd: root, + encoding: "utf8", + timeout: 30_000, + env: {...process.env, ADNBN_TEST_BUILD_FAILURE: ""}, + }); + + expect(result.error).toBeUndefined(); + expect(result.signal).toBeNull(); + expect(result.status).toBe(0); + expect(result.stdout).toContain("Build API resolved after shutdown"); + expect(stripVTControlCharacters(result.stdout)).not.toContain("compiled successfully"); + } finally { + rmSync(root, {recursive: true, force: true}); + } +}); diff --git a/tests/integration/build/cli/cli.integration.test.ts b/tests/integration/build/cli/cli.integration.test.ts new file mode 100644 index 00000000..018f3723 --- /dev/null +++ b/tests/integration/build/cli/cli.integration.test.ts @@ -0,0 +1,116 @@ +import {spawnSync} from "child_process"; +import fs from "fs"; +import os from "os"; +import path from "path"; +import {stripVTControlCharacters} from "util"; + +const cli = path.resolve(__dirname, "../../../../bin/adnbn.js"); +const fixtures = path.join(__dirname, "fixtures/exit-code"); + +describe("CLI exit codes", () => { + let root: string; + let artifact: string; + + beforeEach(() => { + root = fs.mkdtempSync(path.join(os.tmpdir(), "adnbn-cli-exit-code-")); + artifact = path.join(root, "dist/exit-code-chrome-mv3"); + }); + + afterEach(() => { + fs.rmSync(root, {recursive: true, force: true}); + }); + + const run = (command: "build" | "watch", fixture: string, failure?: string, configFile = "adnbn.config.ts") => { + fs.cpSync(path.join(fixtures, fixture), root, {recursive: true}); + + const result = spawnSync( + process.execPath, + [cli, command, root, "-a", "exit-code", "-b", "chrome", "--config", configFile], + { + cwd: root, + encoding: "utf8", + timeout: 30_000, + maxBuffer: 4 * 1024 * 1024, + env: {...process.env, ADNBN_TEST_BUILD_FAILURE: failure ?? ""}, + } + ); + + if (result.error) { + throw result.error; + } + + expect(result.signal).toBeNull(); + + return {status: result.status, output: stripVTControlCharacters(`${result.stdout}\n${result.stderr}`)}; + }; + + test("build exits with 0 and emits an extension on success", () => { + const result = run("build", "success"); + + expect(result.status).toBe(0); + expect(JSON.parse(fs.readFileSync(path.join(artifact, "manifest.json"), "utf8"))).toMatchObject({ + manifest_version: 3, + version: "1.0.0", + default_locale: "en", + }); + expect(JSON.parse(fs.readFileSync(path.join(artifact, "_locales/fr/messages.json"), "utf8"))).toMatchObject({ + cart_items: {message: "article|articles"}, + locale: {message: "fr"}, + }); + }); + + test("keeps native JavaScript configs supported by the ordinary CLI", () => { + const result = run("build", "javascript", undefined, "adnbn.config.mjs"); + + expect(result.status).toBe(0); + expect(JSON.parse(fs.readFileSync(path.join(artifact, "manifest.json"), "utf8"))).toMatchObject({ + version: "1.0.0", + }); + }); + + test.each(["build", "watch"] as const)("%s exits with 1 when a plural key is missing at startup", command => { + const result = run(command, "missing-plural"); + + expect(result.output).toContain( + 'Locale "fr" is missing plural key "cart.items" required by default locale "en"' + ); + expect(result.status).toBe(1); + expect(fs.existsSync(path.join(artifact, "manifest.json"))).toBe(false); + }); + + test.each(["build", "watch"] as const)("%s exits with 1 when configuration is invalid", command => { + const result = run(command, "invalid-config"); + + expect(result.output).toContain('Invalid language "unsupported" provided by config'); + expect(result.status).toBe(1); + expect(fs.existsSync(path.join(artifact, "manifest.json"))).toBe(false); + }); + + test("prints successful build statistics only after compiler shutdown", () => { + const result = run("build", "lifecycle"); + + expect(result.status).toBe(0); + expect(fs.readFileSync(path.join(artifact, "compiler-closed.txt"), "utf8")).toBe("closed"); + expect(result.output).toContain("Lifecycle fixture shutdown finished"); + expect(result.output).toContain("compiled successfully"); + expect(result.output.indexOf("Lifecycle fixture shutdown finished")).toBeLessThan( + result.output.indexOf("compiled successfully") + ); + }); + + test.each([ + {failure: "compilation", messages: ["Lifecycle fixture compilation failure"]}, + {failure: "run", messages: ["Lifecycle fixture run failure"]}, + {failure: "close", messages: ["Lifecycle fixture close failure"]}, + {failure: "both", messages: ["Lifecycle fixture run failure", "Lifecycle fixture close failure"]}, + ])("build exits with 1 after shutdown for $failure failure", ({failure, messages}) => { + const result = run("build", "lifecycle", failure); + + expect(result.status).toBe(1); + expect(fs.readFileSync(path.join(artifact, "compiler-closed.txt"), "utf8")).toBe("closed"); + + for (const message of messages) { + expect(result.output).toContain(message); + } + }); +}); diff --git a/tests/integration/build/cli/dotenv.integration.test.ts b/tests/integration/build/cli/dotenv.integration.test.ts new file mode 100644 index 00000000..18a0373b --- /dev/null +++ b/tests/integration/build/cli/dotenv.integration.test.ts @@ -0,0 +1,142 @@ +import {spawnSync} from "node:child_process"; +import {cpSync, mkdirSync, mkdtempSync, rmSync, writeFileSync} from "node:fs"; +import os from "node:os"; +import path from "node:path"; + +const fixtures = path.join(__dirname, "fixtures/dotenv"); +const resolver = path.resolve(__dirname, "../../../../dist/cli/resolvers/config.js"); +const filenames = [ + ".env.development.chrome.local", + ".env.development.chrome", + ".env.chrome.local", + ".env.chrome", + ".env.development.local", + ".env.development", + ".env.local", + ".env", +]; + +describe("dotenv resolution through the built config loader", () => { + let temporary: string; + let root: string; + let cwd: string; + + beforeEach(() => { + temporary = mkdtempSync(path.join(os.tmpdir(), "adnbn-dotenv-")); + root = path.join(temporary, "project"); + cwd = path.join(temporary, "caller"); + mkdirSync(root); + mkdirSync(cwd); + }); + + afterEach(() => { + rmSync(temporary, {recursive: true, force: true}); + }); + + const inspect = (env: NodeJS.ProcessEnv = {}, configFile = "missing.config.mjs") => { + const inherited = Object.fromEntries( + Object.entries(process.env).filter(([key]) => !key.startsWith("ADNBN_ENV_")) + ); + const result = spawnSync( + process.execPath, + [path.join(__dirname, "scripts/inspect-dotenv.mjs"), resolver, root, configFile], + { + cwd, + env: {...inherited, ...env}, + encoding: "utf8", + timeout: 30_000, + } + ); + + expect(result.error).toBeUndefined(); + expect({status: result.status, stderr: result.stderr}).toEqual({status: 0, stderr: ""}); + + return JSON.parse(result.stdout); + }; + + test("preserves all 24 file priorities, process precedence and reserved keys", () => { + const directories = [path.join(root, "src/apps/sample/source"), path.join(root, "src/apps/sample"), root]; + const expected: Record = {}; + let rank = 0; + + for (const directory of directories) { + mkdirSync(directory, {recursive: true}); + + for (const filename of filenames) { + const values: Record = { + ADNBN_ENV_CALLER: "file", + APP: "file", + BROWSER: "file", + MODE: "file", + MANIFEST_VERSION: "file", + }; + + for (let key = 0; key <= rank; key++) { + values[`ADNBN_ENV_PRIORITY_${key}`] = String(rank); + } + + expected[`ADNBN_ENV_PRIORITY_${rank}`] = String(rank); + writeFileSync( + path.join(directory, filename), + Object.entries(values) + .map(([key, value]) => `${key}=${value}`) + .join("\n") + ); + rank++; + } + } + + const result = inspect({ + ADNBN_ENV_CALLER: "caller", + APP: "caller", + BROWSER: "caller", + MODE: "caller", + MANIFEST_VERSION: "caller", + }); + const reserved = {APP: "sample", BROWSER: "chrome", MODE: "development", MANIFEST_VERSION: "3"}; + + expect(result.host).toMatchObject({...expected, ...reserved, ADNBN_ENV_CALLER: "caller"}); + expect(result.bundled).toMatchObject({...expected, ...reserved, ADNBN_ENV_CALLER: "file"}); + }); + + test("loads framework dotenv before config, repeats after target changes and leaves references literal", () => { + cpSync(path.join(fixtures, "adnbn.config.mjs"), path.join(root, "adnbn.config.mjs")); + writeFileSync( + path.join(root, ".env.development.chrome"), + "ADNBN_ENV_VALUE=chrome\nADNBN_ENV_REFERENCE=${ADNBN_ENV_VALUE}" + ); + writeFileSync(path.join(root, ".env.production.firefox"), "ADNBN_ENV_VALUE=firefox\nADNBN_ENV_SECOND=second"); + const result = inspect({ADNBN_ENV_SWITCH: "1"}, "adnbn.config.mjs"); + + expect(result.config).toMatchObject({ + ADNBN_ENV_VALUE: "chrome", + ADNBN_ENV_REFERENCE: "${ADNBN_ENV_VALUE}", + BROWSER: "chrome", + MODE: "development", + MANIFEST_VERSION: "3", + }); + expect(result.host).toMatchObject({ + ADNBN_ENV_VALUE: "chrome", + ADNBN_ENV_SECOND: "second", + BROWSER: "firefox", + MODE: "production", + MANIFEST_VERSION: "2", + }); + expect(result.bundled).toMatchObject({ + ADNBN_ENV_VALUE: "firefox", + ADNBN_ENV_SECOND: "second", + ADNBN_ENV_REFERENCE: "${ADNBN_ENV_VALUE}", + }); + }); + + test("ignores unrelated cwd dotenv when loading user config", () => { + cpSync(path.join(fixtures, "adnbn.config.mjs"), path.join(root, "adnbn.config.mjs")); + writeFileSync(path.join(cwd, ".env"), "ADNBN_ENV_CWD=cwd\nADNBN_ENV_CWD_REFERENCE=${ADNBN_ENV_CWD}"); + const result = inspect({}, "adnbn.config.mjs"); + + expect(result.config).not.toHaveProperty("ADNBN_ENV_CWD"); + expect(result.config).not.toHaveProperty("ADNBN_ENV_CWD_REFERENCE"); + expect(result.host).not.toHaveProperty("ADNBN_ENV_CWD"); + expect(result.bundled).not.toHaveProperty("ADNBN_ENV_CWD"); + }); +}); diff --git a/tests/integration/build/cli/fixtures/build-session/adnbn.config.ts b/tests/integration/build/cli/fixtures/build-session/adnbn.config.ts new file mode 100644 index 00000000..50fc5a11 --- /dev/null +++ b/tests/integration/build/cli/fixtures/build-session/adnbn.config.ts @@ -0,0 +1,25 @@ +import {defineConfig} from "adnbn"; + +export default defineConfig({ + name: "Build session lifecycle", + version: "1.0.0", + plugins: [ + { + name: "session-lifecycle", + async startup() { + if (process.env.ADNBN_SESSION_CASE === "error") { + throw new Error("Session config failed", {cause: new Error("Original config cause")}); + } + + if (process.env.ADNBN_SESSION_CASE === "hang") { + process.send?.({started: true}); + await new Promise(() => {}); + } + + if (process.env.ADNBN_SESSION_CASE === "exit") { + process.exit(9); + } + }, + }, + ], +}); diff --git a/tests/integration/build/cli/fixtures/build-session/invalid-newtab.ts b/tests/integration/build/cli/fixtures/build-session/invalid-newtab.ts new file mode 100644 index 00000000..93bcfb8c --- /dev/null +++ b/tests/integration/build/cli/fixtures/build-session/invalid-newtab.ts @@ -0,0 +1,4 @@ +import "./missing-runtime"; +import {defineNewtab} from "adnbn"; + +export default defineNewtab({render: "Invalid runtime import"}); diff --git a/tests/integration/build/cli/fixtures/dotenv/adnbn.config.mjs b/tests/integration/build/cli/fixtures/dotenv/adnbn.config.mjs new file mode 100644 index 00000000..c8ebc166 --- /dev/null +++ b/tests/integration/build/cli/fixtures/dotenv/adnbn.config.mjs @@ -0,0 +1,13 @@ +process.env.ADNBN_ENV_CONFIG_SNAPSHOT = JSON.stringify( + Object.fromEntries( + Object.entries(process.env).filter( + ([key]) => key.startsWith("ADNBN_ENV_") || ["APP", "BROWSER", "MODE", "MANIFEST_VERSION"].includes(key) + ) + ) +); + +export default { + browser: process.env.ADNBN_ENV_SWITCH ? "firefox" : "chrome", + mode: process.env.ADNBN_ENV_SWITCH ? "production" : "development", + manifestVersion: process.env.ADNBN_ENV_SWITCH ? 2 : 3, +}; diff --git a/tests/integration/build/cli/fixtures/dotenv/environment.cjs b/tests/integration/build/cli/fixtures/dotenv/environment.cjs new file mode 100644 index 00000000..25f2f3cc --- /dev/null +++ b/tests/integration/build/cli/fixtures/dotenv/environment.cjs @@ -0,0 +1 @@ +module.exports = process.env; diff --git a/tests/integration/build/cli/fixtures/environment/adnbn.config.ts b/tests/integration/build/cli/fixtures/environment/adnbn.config.ts new file mode 100644 index 00000000..e5c5bd25 --- /dev/null +++ b/tests/integration/build/cli/fixtures/environment/adnbn.config.ts @@ -0,0 +1,82 @@ +import {appendFileSync} from "node:fs"; +import path from "node:path"; + +const failure = process.env.ADNBN_BUILD_FAILURE; +const record = stage => { + appendFileSync( + path.join(import.meta.dirname, "observations.jsonl"), + JSON.stringify({ + stage, + value: process.env.ADNBN_BUILD_VALUE, + onlyA: process.env.ADNBN_BUILD_ONLY_A, + browser: process.env.BROWSER, + mode: process.env.MODE, + manifestVersion: process.env.MANIFEST_VERSION, + }) + "\n" + ); +}; + +record("config"); +process.env.ADNBN_BUILD_ADDED = "from-config"; +process.env.ADNBN_BUILD_CHANGED = "from-config"; +delete process.env.ADNBN_BUILD_DELETED; + +if (failure === "config") { + throw new Error("Environment fixture config failure"); +} + +export default { + name: process.env.ADNBN_BUILD_VALUE, + version: "1.0.0", + plugins: [ + { + name: "environment-fixture", + startup() { + record("startup"); + + if (failure === "startup") { + throw new Error("Environment fixture startup failure"); + } + }, + bundler() { + record("bundler"); + + if (failure === "bundler") { + throw new Error("Environment fixture bundler failure"); + } + + return { + plugins: [ + { + apply(compiler) { + compiler.hooks.beforeRun.tap("EnvironmentFixture", () => { + record("run"); + + if (failure === "run") { + throw new Error("Environment fixture run failure"); + } + }); + + compiler.hooks.thisCompilation.tap("EnvironmentFixture", compilation => { + if (failure === "compilation") { + compilation.errors.push(new Error("Environment fixture compilation failure")); + } + }); + + let closes = 0; + + compiler.hooks.shutdown.tap("EnvironmentFixture", () => { + record("close"); + + if (failure === "close" && ++closes === 1) { + throw new Error("Environment fixture close failure"); + } + }); + }, + }, + ], + }; + }, + }, + ], +}; diff --git a/src/cli/tests/fixtures/exit-code/invalid-config/adnbn.config.ts b/tests/integration/build/cli/fixtures/exit-code/invalid-config/adnbn.config.ts similarity index 100% rename from src/cli/tests/fixtures/exit-code/invalid-config/adnbn.config.ts rename to tests/integration/build/cli/fixtures/exit-code/invalid-config/adnbn.config.ts diff --git a/src/cli/tests/fixtures/exit-code/missing-plural/adnbn.config.ts b/tests/integration/build/cli/fixtures/exit-code/javascript/adnbn.config.mjs similarity index 100% rename from src/cli/tests/fixtures/exit-code/missing-plural/adnbn.config.ts rename to tests/integration/build/cli/fixtures/exit-code/javascript/adnbn.config.mjs diff --git a/tests/integration/build/cli/fixtures/exit-code/lifecycle/adnbn.config.ts b/tests/integration/build/cli/fixtures/exit-code/lifecycle/adnbn.config.ts new file mode 100644 index 00000000..ae3c2c35 --- /dev/null +++ b/tests/integration/build/cli/fixtures/exit-code/lifecycle/adnbn.config.ts @@ -0,0 +1,41 @@ +import {mkdir, writeFile} from "fs/promises"; +import path from "path"; +import type {Compiler} from "@rspack/core"; + +const failure = process.env.ADNBN_TEST_BUILD_FAILURE; + +export default { + version: "1.0.0", + plugins: [ + { + name: "build-lifecycle-fixture", + bundler: { + plugins: [ + { + apply(compiler: Compiler) { + compiler.hooks.beforeRun.tap("FailRun", () => { + if (failure === "run" || failure === "both") { + throw new Error("Lifecycle fixture run failure"); + } + }); + compiler.hooks.thisCompilation.tap("FailCompilation", compilation => { + if (failure === "compilation") { + compilation.errors.push(new Error("Lifecycle fixture compilation failure")); + } + }); + compiler.hooks.shutdown.tapPromise("ObserveClose", async () => { + await mkdir(compiler.outputPath, {recursive: true}); + await writeFile(path.join(compiler.outputPath, "compiler-closed.txt"), "closed"); + console.log("Lifecycle fixture shutdown finished"); + + if (failure === "close" || failure === "both") { + throw new Error("Lifecycle fixture close failure"); + } + }); + }, + }, + ], + }, + }, + ], +}; diff --git a/src/cli/tests/fixtures/exit-code/success/adnbn.config.ts b/tests/integration/build/cli/fixtures/exit-code/missing-plural/adnbn.config.ts similarity index 100% rename from src/cli/tests/fixtures/exit-code/success/adnbn.config.ts rename to tests/integration/build/cli/fixtures/exit-code/missing-plural/adnbn.config.ts diff --git a/src/cli/tests/fixtures/exit-code/missing-plural/src/locales/en.yaml b/tests/integration/build/cli/fixtures/exit-code/missing-plural/src/locales/en.yaml similarity index 100% rename from src/cli/tests/fixtures/exit-code/missing-plural/src/locales/en.yaml rename to tests/integration/build/cli/fixtures/exit-code/missing-plural/src/locales/en.yaml diff --git a/src/cli/tests/fixtures/exit-code/missing-plural/src/locales/fr.yaml b/tests/integration/build/cli/fixtures/exit-code/missing-plural/src/locales/fr.yaml similarity index 100% rename from src/cli/tests/fixtures/exit-code/missing-plural/src/locales/fr.yaml rename to tests/integration/build/cli/fixtures/exit-code/missing-plural/src/locales/fr.yaml diff --git a/tests/integration/build/cli/fixtures/exit-code/success/adnbn.config.ts b/tests/integration/build/cli/fixtures/exit-code/success/adnbn.config.ts new file mode 100644 index 00000000..626d6c0d --- /dev/null +++ b/tests/integration/build/cli/fixtures/exit-code/success/adnbn.config.ts @@ -0,0 +1,3 @@ +export default { + version: "1.0.0", +}; diff --git a/src/cli/tests/fixtures/exit-code/success/src/locales/en.yaml b/tests/integration/build/cli/fixtures/exit-code/success/src/locales/en.yaml similarity index 100% rename from src/cli/tests/fixtures/exit-code/success/src/locales/en.yaml rename to tests/integration/build/cli/fixtures/exit-code/success/src/locales/en.yaml diff --git a/src/cli/tests/fixtures/exit-code/success/src/locales/fr.yaml b/tests/integration/build/cli/fixtures/exit-code/success/src/locales/fr.yaml similarity index 100% rename from src/cli/tests/fixtures/exit-code/success/src/locales/fr.yaml rename to tests/integration/build/cli/fixtures/exit-code/success/src/locales/fr.yaml diff --git a/tests/integration/build/cli/fixtures/root-dir-b/title.ts b/tests/integration/build/cli/fixtures/root-dir-b/title.ts new file mode 100644 index 00000000..d9719d09 --- /dev/null +++ b/tests/integration/build/cli/fixtures/root-dir-b/title.ts @@ -0,0 +1 @@ +export const title = "Absolute root B"; diff --git a/tests/integration/build/cli/fixtures/root-dir-b/values.ts b/tests/integration/build/cli/fixtures/root-dir-b/values.ts new file mode 100644 index 00000000..520378e2 --- /dev/null +++ b/tests/integration/build/cli/fixtures/root-dir-b/values.ts @@ -0,0 +1,2 @@ +export const source = "https://root-b.example.com"; +export const markup = "
Root B runtime
"; diff --git a/tests/integration/build/cli/fixtures/root-dir/adnbn.config.ts b/tests/integration/build/cli/fixtures/root-dir/adnbn.config.ts new file mode 100644 index 00000000..7e53b47d --- /dev/null +++ b/tests/integration/build/cli/fixtures/root-dir/adnbn.config.ts @@ -0,0 +1,6 @@ +import {defineConfig} from "adnbn"; + +export default defineConfig({ + name: "Root Directory Integration", + version: "1.0.0", +}); diff --git a/tests/integration/build/cli/fixtures/root-dir/src/background.ts b/tests/integration/build/cli/fixtures/root-dir/src/background.ts new file mode 100644 index 00000000..b99a8e26 --- /dev/null +++ b/tests/integration/build/cli/fixtures/root-dir/src/background.ts @@ -0,0 +1,7 @@ +import {defineBackground} from "adnbn"; + +export default defineBackground({ + main() { + console.info("Root background runtime"); + }, +}); diff --git a/tests/integration/build/cli/fixtures/root-dir/src/newtab.ts b/tests/integration/build/cli/fixtures/root-dir/src/newtab.ts new file mode 100644 index 00000000..d6c30b92 --- /dev/null +++ b/tests/integration/build/cli/fixtures/root-dir/src/newtab.ts @@ -0,0 +1,10 @@ +import {defineNewtab} from "adnbn"; + +import {title} from "./title"; +import {source, markup} from "@/values"; + +export default defineNewtab({ + title, + csp: {sources: {connect: [source]}}, + render: markup, +}); diff --git a/tests/integration/build/cli/fixtures/root-dir/src/offscreen.ts b/tests/integration/build/cli/fixtures/root-dir/src/offscreen.ts new file mode 100644 index 00000000..7212f8ca --- /dev/null +++ b/tests/integration/build/cli/fixtures/root-dir/src/offscreen.ts @@ -0,0 +1,12 @@ +import {defineOffscreen, OffscreenReason} from "adnbn"; + +export default defineOffscreen({ + name: "rootProbe", + reasons: [OffscreenReason.DOMParser], + justification: "Check offscreen entry resolution from an absolute project root.", + init: () => ({ + read() { + return "Offscreen root runtime"; + }, + }), +}); diff --git a/tests/integration/build/cli/fixtures/root-dir/src/title.ts b/tests/integration/build/cli/fixtures/root-dir/src/title.ts new file mode 100644 index 00000000..494a9f50 --- /dev/null +++ b/tests/integration/build/cli/fixtures/root-dir/src/title.ts @@ -0,0 +1 @@ +export const title = "Absolute root A"; diff --git a/tests/integration/build/cli/fixtures/root-dir/src/values.ts b/tests/integration/build/cli/fixtures/root-dir/src/values.ts new file mode 100644 index 00000000..4d22fb1f --- /dev/null +++ b/tests/integration/build/cli/fixtures/root-dir/src/values.ts @@ -0,0 +1,2 @@ +export const source = "https://root-a.example.com"; +export const markup = "
Root A runtime
"; diff --git a/tests/integration/build/cli/fixtures/root-dir/tsconfig.json b/tests/integration/build/cli/fixtures/root-dir/tsconfig.json new file mode 100644 index 00000000..eadf1be8 --- /dev/null +++ b/tests/integration/build/cli/fixtures/root-dir/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./.adnbn/tsconfig.json" +} diff --git a/tests/integration/build/cli/root-dir.integration.test.ts b/tests/integration/build/cli/root-dir.integration.test.ts new file mode 100644 index 00000000..67d6f981 --- /dev/null +++ b/tests/integration/build/cli/root-dir.integration.test.ts @@ -0,0 +1,25 @@ +import {spawnSync} from "node:child_process"; +import {mkdtempSync, rmSync} from "node:fs"; +import os from "node:os"; +import path from "node:path"; + +jest.setTimeout(90_000); + +test.each(["api", "cli"])("%s builds absolute roots from an unrelated working directory", mode => { + const directory = mkdtempSync(path.join(os.tmpdir(), "adnbn-root-dir-")); + const projectRoot = path.resolve(__dirname, "../../../.."); + + try { + const result = spawnSync( + process.execPath, + [path.join(__dirname, "scripts/verify-root-dir.mjs"), projectRoot, directory, mode], + {encoding: "utf8", timeout: 60_000, maxBuffer: 4 * 1024 * 1024} + ); + + expect(result.error).toBeUndefined(); + expect({status: result.status, stderr: result.stderr}).toEqual({status: 0, stderr: ""}); + expect(result.stdout).toContain("Absolute root contract verified"); + } finally { + rmSync(directory, {recursive: true, force: true, maxRetries: 5, retryDelay: 200}); + } +}); diff --git a/tests/integration/build/cli/scripts/build-api.mjs b/tests/integration/build/cli/scripts/build-api.mjs new file mode 100644 index 00000000..6f257b66 --- /dev/null +++ b/tests/integration/build/cli/scripts/build-api.mjs @@ -0,0 +1,19 @@ +import {readFile} from "node:fs/promises"; +import path from "node:path"; +import {pathToFileURL} from "node:url"; + +const [modulePath, rootDir, outputPath] = process.argv.slice(2); +const {default: app} = await import(pathToFileURL(modulePath).href); +const stats = await app({command: "build", rootDir, app: "exit-code", browser: "chrome"}); + +if (!stats || stats.hasErrors()) { + throw new Error("Expected successful build statistics"); +} + +const marker = await readFile(path.join(outputPath, "compiler-closed.txt"), "utf8"); + +if (marker !== "closed") { + throw new Error("Build resolved before compiler shutdown"); +} + +console.log("Build API resolved after shutdown"); diff --git a/tests/integration/build/cli/scripts/inspect-dotenv.mjs b/tests/integration/build/cli/scripts/inspect-dotenv.mjs new file mode 100644 index 00000000..f0307e59 --- /dev/null +++ b/tests/integration/build/cli/scripts/inspect-dotenv.mjs @@ -0,0 +1,33 @@ +import path from "node:path"; +import {createRequire} from "node:module"; +import {rspack} from "@rspack/core"; +import {pathToFileURL} from "node:url"; + +const [modulePath, rootDir, configFile] = process.argv.slice(2); +const {default: resolveConfig} = await import(pathToFileURL(modulePath).href); +const config = await resolveConfig({rootDir, configFile, app: "sample", appSrcDir: "source"}); +const plugin = config.plugins.find(plugin => plugin.name === "adnbn:dotenv"); +const bundler = plugin.bundler({config}); +const {build} = await import(pathToFileURL(path.resolve(modulePath, "../../builders/app/build.js")).href); +const output = path.join(rootDir, "output"); +await build( + rspack({ + mode: "development", + target: "node", + entry: path.join(import.meta.dirname, "../fixtures/dotenv/environment.cjs"), + output: {path: output, filename: "environment.cjs", library: {type: "commonjs2"}}, + plugins: bundler.plugins, + }) +); +const bundled = createRequire(import.meta.url)(path.join(output, "environment.cjs")); +const snapshot = process.env.ADNBN_ENV_CONFIG_SNAPSHOT; +const select = env => + Object.fromEntries( + Object.entries(env).filter( + ([key]) => key.startsWith("ADNBN_ENV_") || ["APP", "BROWSER", "MODE", "MANIFEST_VERSION"].includes(key) + ) + ); + +console.log( + JSON.stringify({host: select(process.env), bundled, config: snapshot ? select(JSON.parse(snapshot)) : null}) +); diff --git a/tests/integration/build/cli/scripts/verify-environment.mjs b/tests/integration/build/cli/scripts/verify-environment.mjs new file mode 100644 index 00000000..de82160b --- /dev/null +++ b/tests/integration/build/cli/scripts/verify-environment.mjs @@ -0,0 +1,179 @@ +import assert from "node:assert/strict"; +import {cp, mkdir, readFile, writeFile} from "node:fs/promises"; +import path from "node:path"; +import {pathToFileURL} from "node:url"; + +const [modulePath, directory, scenario] = process.argv.slice(2); +const {buildApp} = await import(pathToFileURL(modulePath).href); +const configFile = + scenario === "sequence-mts" + ? "adnbn.config.mts" + : scenario === "sequence-cts" + ? "adnbn.config.cts" + : "adnbn.config.ts"; +const rootA = path.join(directory, "a"); +const rootB = path.join(directory, "b"); + +for (const root of [rootA, rootB]) { + await mkdir(root); + await cp(path.join(import.meta.dirname, "../fixtures/environment/adnbn.config.ts"), path.join(root, configFile)); +} + +await writeFile(path.join(rootA, ".env"), "ADNBN_BUILD_VALUE=A\nADNBN_BUILD_ONLY_A=only-a"); +await writeFile(path.join(rootB, ".env"), "ADNBN_BUILD_VALUE=B"); +process.env.ADNBN_BUILD_CHANGED = "caller"; +process.env.ADNBN_BUILD_DELETED = "caller"; +const environment = process.env; +const snapshot = {...environment}; +const options = rootDir => ({rootDir, configFile, app: "environment"}); +const restored = () => { + assert.equal(process.env, environment); + assert.deepEqual({...process.env}, snapshot); +}; +const verifyBuild = async (root, browser, manifestVersion, expected) => { + const stats = await buildApp({...options(root), browser, manifestVersion}); + assert.equal(stats.hasErrors(), false); + restored(); + const manifest = JSON.parse( + await readFile(path.join(root, `dist/environment-${browser}-mv${manifestVersion}/manifest.json`), "utf8") + ); + assert.equal(manifest.name, expected); + assert.equal(manifest.manifest_version, manifestVersion); +}; + +if (scenario.startsWith("sequence")) { + await verifyBuild(rootA, "chrome", 3, "A"); + await verifyBuild(rootB, "firefox", 2, "B"); + await writeFile(path.join(rootA, ".env"), "ADNBN_BUILD_VALUE=A-again\nADNBN_BUILD_ONLY_A=only-a"); + await verifyBuild(rootA, "chrome", 3, "A-again"); + + for (const [root, expected, browser, manifestVersion] of [ + [rootA, ["A", "A-again"], "chrome", "3"], + [rootB, ["B"], "firefox", "2"], + ]) { + const records = (await readFile(path.join(root, "observations.jsonl"), "utf8")) + .trim() + .split("\n") + .map(JSON.parse); + assert.deepEqual( + records.map(record => record.stage), + expected.flatMap(() => ["config", "startup", "bundler", "run", "close"]) + ); + + for (const [index, value] of expected.entries()) { + for (const record of records.slice(index * 5, (index + 1) * 5)) { + assert.equal(record.value, value); + assert.equal(record.browser, browser); + assert.equal(record.manifestVersion, manifestVersion); + assert.equal(record.onlyA, root === rootA ? "only-a" : undefined); + } + } + } +} else if (scenario.startsWith("unsupported-")) { + const filename = `adnbn.config.${scenario.slice("unsupported-".length)}`; + await cp(path.join(import.meta.dirname, "../fixtures/environment/adnbn.config.ts"), path.join(rootA, filename)); + await assert.rejects(buildApp({...options(rootA), configFile: filename}), /requires a TypeScript config/); + restored(); + await assert.rejects(readFile(path.join(rootA, "observations.jsonl")), {code: "ENOENT"}); + await verifyBuild(rootB, "firefox", 2, "B"); +} else if (scenario.startsWith("overlap")) { + const entered = Promise.withResolvers(); + const release = Promise.withResolvers(); + const running = buildApp({ + ...options(rootA), + plugins: [ + { + name: "hold-startup", + async startup() { + if (scenario === "overlap") { + entered.resolve(); + await release.promise; + } + }, + bundler: { + plugins: [ + { + apply(compiler) { + if (scenario === "overlap-close") { + compiler.hooks.shutdown.tapPromise("HoldShutdown", async () => { + entered.resolve(); + await release.promise; + }); + } + }, + }, + ], + }, + }, + ], + }); + + try { + await entered.promise; + const active = {...process.env}; + await assert.rejects(buildApp(options(rootB)), /already running/); + assert.deepEqual({...process.env}, active); + } finally { + release.resolve(); + await running; + } + + restored(); + await verifyBuild(rootB, "firefox", 2, "B"); +} else { + process.env.ADNBN_BUILD_FAILURE = scenario; + const failingSnapshot = {...process.env}; + let compiler; + + try { + await assert.rejects( + buildApp({ + ...options(rootA), + plugins: [ + { + name: "capture-compiler", + bundler: { + plugins: [ + { + apply(current) { + compiler = current; + }, + }, + ], + }, + }, + ], + }), + error => { + const diagnostic = [ + error.message, + error.cause?.message, + error.stats?.toString({all: false, errors: true}), + ].join("\n"); + assert.match(diagnostic, new RegExp(`Environment fixture ${scenario} failure`)); + + return true; + } + ); + assert.deepEqual({...process.env}, failingSnapshot); + } finally { + // A failing shutdown hook interrupts native cleanup; release the real compiler after checking restoration. + if (scenario === "close" && compiler) { + await new Promise((resolve, reject) => + compiler.close(error => { + if (error) { + reject(error); + } else { + resolve(); + } + }) + ); + } + } + + delete process.env.ADNBN_BUILD_FAILURE; + restored(); + await verifyBuild(rootB, "firefox", 2, "B"); +} + +console.log("Environment contract verified"); diff --git a/tests/integration/build/cli/scripts/verify-root-dir.mjs b/tests/integration/build/cli/scripts/verify-root-dir.mjs new file mode 100644 index 00000000..f6b0ce86 --- /dev/null +++ b/tests/integration/build/cli/scripts/verify-root-dir.mjs @@ -0,0 +1,115 @@ +import assert from "node:assert/strict"; +import {spawnSync} from "node:child_process"; +import {cp, mkdir, readFile, readdir, symlink} from "node:fs/promises"; +import path from "node:path"; +import {pathToFileURL} from "node:url"; + +const [projectRoot, directory, mode, sharedCwd] = process.argv.slice(2); +const {buildApp} = await import(pathToFileURL(path.join(projectRoot, "dist/cli/builders/app/index.js")).href); +const rootA = path.join(directory, "project a"); +const rootB = path.join(directory, "project b"); +const cwd = sharedCwd ?? path.join(directory, "unrelated"); + +await mkdir(cwd, {recursive: true}); + +for (const root of [rootA, rootB]) { + await cp(path.join(import.meta.dirname, "../fixtures/root-dir"), root, {recursive: true}); + await mkdir(path.join(root, "node_modules")); + await symlink( + projectRoot, + path.join(root, "node_modules/adnbn"), + process.platform === "win32" ? "junction" : "dir" + ); +} + +await cp(path.join(import.meta.dirname, "../fixtures/root-dir-b"), path.join(rootB, "src"), {recursive: true}); +process.chdir(cwd); +const workingDirectory = process.cwd(); +const environment = process.env; +const snapshot = {...environment}; + +const sequence = [ + [rootA, "A", "chrome"], + [rootB, "B", "firefox"], + [rootA, "A", "chrome"], +]; + +for (const [root, label, browser] of mode === "parallel" ? sequence.slice(1, 2) : sequence) { + if (mode !== "cli") { + const stats = await buildApp({ + rootDir: root, + browser, + mode: "production", + plugins: + mode === "parallel" + ? [ + { + name: "hold-before-run", + bundler: { + plugins: [ + { + apply(compiler) { + compiler.hooks.beforeRun.tapPromise( + "HoldBeforeRun", + () => + new Promise(resolve => { + process.once("message", message => { + assert.equal(message, "build"); + resolve(); + }); + process.send("ready"); + }) + ); + }, + }, + ], + }, + }, + ] + : [], + }).catch(error => { + if (error.stats) { + console.error(error.stats.toString({all: false, errors: true})); + } + + throw error; + }); + assert.equal(stats.hasErrors(), false); + } else { + const result = spawnSync( + process.execPath, + [path.join(projectRoot, "bin/adnbn.js"), "build", root, "-b", browser], + { + encoding: "utf8", + timeout: 30_000, + } + ); + assert.equal(result.error, undefined); + assert.equal(result.status, 0, result.stderr + result.stdout); + } + + assert.equal(process.cwd(), workingDirectory); + assert.equal(process.env, environment); + assert.deepEqual({...process.env}, snapshot); + const output = path.join(root, `dist/myapp-${browser}-mv3`); + const html = await readFile(path.join(output, "newtab.html"), "utf8"); + const manifest = JSON.parse(await readFile(path.join(output, "manifest.json"), "utf8")); + assert.ok(html.includes(`Absolute root ${label}`)); + assert.ok( + JSON.stringify(manifest.content_security_policy).includes(`https://root-${label.toLowerCase()}.example.com`) + ); + const scripts = (await readdir(output, {recursive: true})).filter(file => file.endsWith(".js")); + const code = (await Promise.all(scripts.map(file => readFile(path.join(output, file), "utf8")))).join("\n"); + assert.ok(code.includes(`Root ${label} runtime`)); + assert.ok(code.includes("Offscreen root runtime")); + + if (browser === "firefox") { + assert.ok(manifest.background.scripts.length > 0); + } +} + +console.log("Absolute root contract verified"); + +if (process.connected) { + process.disconnect(); +} diff --git a/tests/integration/build/override/bookmarks.integration.test.ts b/tests/integration/build/override/bookmarks.integration.test.ts new file mode 100644 index 00000000..091c0ebd --- /dev/null +++ b/tests/integration/build/override/bookmarks.integration.test.ts @@ -0,0 +1,10 @@ +import {testOverridePage} from "./override-utils"; + +jest.setTimeout(90_000); + +testOverridePage({ + page: "bookmarks", + permission: "bookmarks", + supported: ["chrome", "edge"], + unsupported: ["firefox", "safari", "opera"], +}); diff --git a/tests/integration/build/override/competing.integration.test.ts b/tests/integration/build/override/competing.integration.test.ts new file mode 100644 index 00000000..0dfe0333 --- /dev/null +++ b/tests/integration/build/override/competing.integration.test.ts @@ -0,0 +1,60 @@ +import {readdir, readFile, writeFile} from "fs/promises"; +import path from "path"; + +import {createIntegrationFixture, type IntegrationFixture} from "../../utils/fixture"; +import {readOverrideManifest} from "./override-utils"; + +const rootDir = path.resolve(__dirname, "..", "..", "..", ".."); + +jest.setTimeout(90_000); + +/** Adds the History entrypoint to a copy of the New Tab application, so two overrides compete. */ +const createCompetingFixture = async (): Promise => { + const fixture = await createIntegrationFixture(rootDir, path.join(__dirname, "newtab")); + + try { + await writeFile( + path.join(fixture.directory, "src", "history.ts"), + await readFile(path.join(__dirname, "history", "src", "history.ts")) + ); + } catch (error) { + await fixture.dispose(); + + throw error; + } + + return fixture; +}; + +describe("competing overrides", () => { + test.each(["chrome", "edge"])("%s build fails and names both entrypoints", async browser => { + const fixture = await createCompetingFixture(); + + try { + const build = fixture.build({browser}); + + await expect(build).rejects.toThrow( + `An extension can override only one browser page, but app "myapp" enables 2 override entrypoints for ${browser}:` + ); + await expect(build).rejects.toThrow(" - newtab: src/newtab.ts"); + await expect(build).rejects.toThrow(" - history: src/history.ts"); + } finally { + await fixture.dispose(); + } + }); + + test("firefox build keeps the new tab and its permission without the unsupported history page", async () => { + const fixture = await createCompetingFixture(); + + try { + const extensionDir = await fixture.build({browser: "firefox"}); + const manifest = await readOverrideManifest(extensionDir); + + expect(manifest.chrome_url_overrides).toEqual({newtab: "newtab.html"}); + expect(manifest.permissions).toEqual(["search"]); + expect(await readdir(extensionDir)).not.toContain("history.html"); + } finally { + await fixture.dispose(); + } + }); +}); diff --git a/tests/integration/build/override/history.integration.test.ts b/tests/integration/build/override/history.integration.test.ts new file mode 100644 index 00000000..5497f82f --- /dev/null +++ b/tests/integration/build/override/history.integration.test.ts @@ -0,0 +1,10 @@ +import {testOverridePage} from "./override-utils"; + +jest.setTimeout(90_000); + +testOverridePage({ + page: "history", + permission: "history", + supported: ["chrome", "edge"], + unsupported: ["firefox", "safari", "opera"], +}); diff --git a/tests/integration/build/override/newtab.integration.test.ts b/tests/integration/build/override/newtab.integration.test.ts new file mode 100644 index 00000000..386c9e14 --- /dev/null +++ b/tests/integration/build/override/newtab.integration.test.ts @@ -0,0 +1,10 @@ +import {testOverridePage} from "./override-utils"; + +jest.setTimeout(90_000); + +testOverridePage({ + page: "newtab", + permission: "search", + supported: ["chrome", "edge", "firefox", "safari"], + unsupported: ["opera"], +}); diff --git a/tests/integration/build/override/override-utils.ts b/tests/integration/build/override/override-utils.ts new file mode 100644 index 00000000..a604bc7b --- /dev/null +++ b/tests/integration/build/override/override-utils.ts @@ -0,0 +1,83 @@ +import {readdir, readFile} from "fs/promises"; +import path from "path"; + +import BuildSession from "../../utils/BuildSession"; + +import {createIntegrationFixture} from "../../utils/fixture"; + +interface OverridePageScenario { + page: string; + permission: string; + supported: readonly string[]; + unsupported: readonly string[]; +} + +const buildMode = process.env.ADNBN_OVERRIDE_BUILD_MODE ?? "cli"; + +if (buildMode !== "cli" && buildMode !== "session") { + throw new Error(`Unknown override build mode: ${buildMode}`); +} + +const rootDir = path.resolve(__dirname, "..", "..", "..", ".."); + +export const readOverrideManifest = async (extensionDir: string) => { + return JSON.parse(await readFile(path.join(extensionDir, "manifest.json"), "utf8")); +}; + +export const testOverridePage = ({page, permission, supported, unsupported}: OverridePageScenario): void => { + describe(`${page} override`, () => { + const fixtureDir = path.join(__dirname, page); + const source = `https://${page}.example.com`; + let session: BuildSession | undefined; + + beforeAll(async () => { + if (buildMode === "session") { + session = await BuildSession.create(rootDir); + } + }); + + afterAll(async () => { + await session?.dispose(); + }); + + describe.each(supported)("%s", browser => { + test.each([2, 3] as const)("MV%s build replaces the browser page", async manifestVersion => { + const fixture = await createIntegrationFixture(rootDir, fixtureDir, session); + + try { + const extensionDir = await fixture.build({browser, manifestVersion}); + const manifest = await readOverrideManifest(extensionDir); + const html = await readFile(path.join(extensionDir, `${page}.html`), "utf8"); + + expect(manifest.manifest_version).toBe(manifestVersion); + expect(manifest.chrome_url_overrides).toEqual({[page]: `${page}.html`}); + expect(JSON.stringify(manifest.content_security_policy)).toContain(source); + expect(manifest.permissions).toContain(permission); + expect(html).toContain("Custom"); + expect(html).toContain("<script"); + } finally { + await fixture.dispose(); + } + }); + }); + + describe.each(unsupported)("%s", browser => { + test.each([2, 3] as const)("MV%s build skips the unsupported page", async manifestVersion => { + const fixture = await createIntegrationFixture(rootDir, fixtureDir, session); + + try { + const extensionDir = await fixture.build({browser, manifestVersion}); + const manifest = await readOverrideManifest(extensionDir); + + expect(manifest.manifest_version).toBe(manifestVersion); + expect(manifest).not.toHaveProperty("chrome_url_overrides"); + expect(JSON.stringify(manifest)).not.toContain(source); + expect(manifest.permissions ?? []).not.toContain(permission); + expect(await readdir(extensionDir)).not.toContain(`${page}.html`); + } finally { + await fixture.dispose(); + } + }); + }); + }); +}; diff --git a/tests/integration/build/override/override.integration.test.ts b/tests/integration/build/override/override.integration.test.ts deleted file mode 100644 index 200e8171..00000000 --- a/tests/integration/build/override/override.integration.test.ts +++ /dev/null @@ -1,122 +0,0 @@ -import {readdir, readFile, writeFile} from "fs/promises"; -import path from "path"; - -import {createIntegrationFixture, type IntegrationFixture} from "../../utils/fixture"; - -const rootDir = path.resolve(__dirname, "..", "..", "..", ".."); - -jest.setTimeout(90_000); - -const readManifest = async (extensionDir: string) => { - return JSON.parse(await readFile(path.join(extensionDir, "manifest.json"), "utf8")); -}; - -/** Adds the History entrypoint to a copy of the New Tab application, so two overrides compete. */ -const createCompetingFixture = async (): Promise<IntegrationFixture> => { - const fixture = await createIntegrationFixture(rootDir, path.join(__dirname, "newtab")); - - try { - await writeFile( - path.join(fixture.directory, "src", "history.ts"), - await readFile(path.join(__dirname, "history", "src", "history.ts")) - ); - } catch (error) { - await fixture.dispose(); - - throw error; - } - - return fixture; -}; - -describe.each([ - {page: "newtab", permission: "search", supported: ["chrome", "edge", "firefox", "safari"], unsupported: ["opera"]}, - { - page: "bookmarks", - permission: "bookmarks", - supported: ["chrome", "edge"], - unsupported: ["firefox", "safari", "opera"], - }, - { - page: "history", - permission: "history", - supported: ["chrome", "edge"], - unsupported: ["firefox", "safari", "opera"], - }, -])("$page override", ({page, permission, supported, unsupported}) => { - const fixtureDir = path.join(__dirname, page); - const source = `https://${page}.example.com`; - - describe.each(supported)("%s", browser => { - test.each([2, 3] as const)("MV%s build replaces the browser page", async manifestVersion => { - const fixture = await createIntegrationFixture(rootDir, fixtureDir); - - try { - const extensionDir = await fixture.build({browser, manifestVersion}); - const manifest = await readManifest(extensionDir); - const html = await readFile(path.join(extensionDir, `${page}.html`), "utf8"); - - expect(manifest.manifest_version).toBe(manifestVersion); - expect(manifest.chrome_url_overrides).toEqual({[page]: `${page}.html`}); - expect(JSON.stringify(manifest.content_security_policy)).toContain(source); - expect(manifest.permissions).toContain(permission); - expect(html).toContain("<title>Custom"); - expect(html).toContain("<script"); - } finally { - await fixture.dispose(); - } - }); - }); - - describe.each(unsupported)("%s", browser => { - test.each([2, 3] as const)("MV%s build skips the unsupported page", async manifestVersion => { - const fixture = await createIntegrationFixture(rootDir, fixtureDir); - - try { - const extensionDir = await fixture.build({browser, manifestVersion}); - const manifest = await readManifest(extensionDir); - - expect(manifest.manifest_version).toBe(manifestVersion); - expect(manifest).not.toHaveProperty("chrome_url_overrides"); - expect(JSON.stringify(manifest)).not.toContain(source); - expect(manifest.permissions ?? []).not.toContain(permission); - expect(await readdir(extensionDir)).not.toContain(`${page}.html`); - } finally { - await fixture.dispose(); - } - }); - }); -}); - -describe("competing overrides", () => { - test.each(["chrome", "edge"])("%s build fails and names both entrypoints", async browser => { - const fixture = await createCompetingFixture(); - - try { - const build = fixture.build({browser}); - - await expect(build).rejects.toThrow( - `An extension can override only one browser page, but app "myapp" enables 2 override entrypoints for ${browser}:` - ); - await expect(build).rejects.toThrow(" - newtab: src/newtab.ts"); - await expect(build).rejects.toThrow(" - history: src/history.ts"); - } finally { - await fixture.dispose(); - } - }); - - test("firefox build keeps the new tab and its permission without the unsupported history page", async () => { - const fixture = await createCompetingFixture(); - - try { - const extensionDir = await fixture.build({browser: "firefox"}); - const manifest = await readManifest(extensionDir); - - expect(manifest.chrome_url_overrides).toEqual({newtab: "newtab.html"}); - expect(manifest.permissions).toEqual(["search"]); - expect(await readdir(extensionDir)).not.toContain("history.html"); - } finally { - await fixture.dispose(); - } - }); -}); diff --git a/tests/integration/build/style/compiler.ts b/tests/integration/build/style/compiler.ts new file mode 100644 index 00000000..b4f1e3f2 --- /dev/null +++ b/tests/integration/build/style/compiler.ts @@ -0,0 +1,109 @@ +import {cp, mkdtemp, realpath, rm} from "fs/promises"; +import os from "os"; +import path from "path"; +import {rspack, type Compilation, type Compiler, type Configuration, type Stats} from "@rspack/core"; + +import stylePlugin from "@cli/plugins/style"; +import assetPlugin from "@cli/plugins/asset"; +import {Browser} from "@typing/browser"; +import type {ReadonlyConfig} from "@typing/config"; + +const project = path.resolve(__dirname, "../../../.."); +export const fixtures = path.join(__dirname, "fixtures"); +export const entries = ["normal", "document", "asis"]; + +export const createFixture = async (): Promise<string> => { + // Rspack resolves symlinks, including the macOS temporary-directory alias. + const root = await realpath(await mkdtemp(path.join(os.tmpdir(), "adnbn-style-merge-"))); + + try { + await cp(path.join(fixtures, "multi-app"), root, {recursive: true}); + } catch (error) { + await rm(root, {recursive: true, force: true}); + + throw error; + } + + return root; +}; + +export const createCompiler = async (root: string, app: string, mergeStyles = true): Promise<Compiler> => { + const config = { + rootDir: root, + srcDir: "src", + sharedDir: "shared", + appsDir: "apps", + appSrcDir: "", + app, + browser: Browser.Chrome, + mergeStyles, + cssDir: "css", + cssFilename: "[name].css", + cssIdentName: "[app]-[local]__[hash:base64:5]", + assetsDir: "assets", + assetsFilename: "[name].[contenthash:8][ext]", + } as ReadonlyConfig; + + const settings: Configuration[] = []; + + for (const plugin of [stylePlugin(), assetPlugin()]) { + const handler = plugin.bundler!; + + settings.push((typeof handler === "function" ? await handler({config, rspack: {}}) : handler) as Configuration); + } + + const [styles, assets] = settings; + + return rspack({ + context: root, + mode: "production", + devtool: false, + entry: Object.fromEntries(entries.map(entry => [entry, `./${entry}.js`])), + output: { + ...assets.output, + path: path.join(root, "dist", app), + filename: "[name].js", + publicPath: "", + library: {type: "commonjs2"}, + }, + resolve: { + alias: {"@tokens": path.join(root, "src/shared/theme/_alias.scss")}, + modules: [path.join(root, "vendor"), path.join(project, "node_modules")], + }, + resolveLoader: {modules: [path.join(project, "node_modules")]}, + module: {rules: [...styles.module!.rules!, ...assets.module!.rules!]}, + plugins: [...styles.plugins!, ...assets.plugins!], + optimization: {minimize: false}, + }); +}; + +export const runCompiler = (compiler: Compiler): Promise<Stats> => + new Promise((resolve, reject) => { + compiler.run((error, stats) => { + if (error || !stats) { + reject(error ?? new Error("Style compilation did not return stats")); + } else { + resolve(stats); + } + }); + }); + +export const closeCompiler = (compiler: Compiler): Promise<void> => + new Promise((resolve, reject) => { + compiler.close(error => { + if (error) { + reject(error); + } else { + resolve(); + } + }); + }); + +export const getCss = (compilation: Compilation, entry: string): string => { + return compilation.entrypoints + .get(entry)! + .getFiles() + .filter(file => file.endsWith(".css")) + .map(file => compilation.getAsset(file)!.source.source().toString()) + .join("\n"); +}; diff --git a/tests/integration/build/style/fixtures/multi-app-updates/Inter-Latin.woff2 b/tests/integration/build/style/fixtures/multi-app-updates/Inter-Latin.woff2 new file mode 100644 index 00000000..be70fdab --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app-updates/Inter-Latin.woff2 @@ -0,0 +1 @@ +updated-font-fixture diff --git a/tests/integration/build/style/fixtures/multi-app-updates/conflict.scss b/tests/integration/build/style/fixtures/multi-app-updates/conflict.scss new file mode 100644 index 00000000..8427cbc2 --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app-updates/conflict.scss @@ -0,0 +1,5 @@ +@use "../theme/tokens" as sharedTokens; + +.badge { + color: sharedTokens.$accent; +} diff --git a/tests/integration/build/style/fixtures/multi-app-updates/content.scss b/tests/integration/build/style/fixtures/multi-app-updates/content.scss new file mode 100644 index 00000000..b8f35c9d --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app-updates/content.scss @@ -0,0 +1,10 @@ +@use "sass:math"; +@use "../theme/tokens" as appTokens; +@use "../theme/fonts" with ( + $inter-latin: url("../theme/fonts/Inter-Latin.woff2?browser&v=1#face") +); + +.badge { + color: appTokens.$accent; + --order: app-updated; +} diff --git a/tests/integration/build/style/fixtures/multi-app-updates/fonts.scss b/tests/integration/build/style/fixtures/multi-app-updates/fonts.scss new file mode 100644 index 00000000..4ae076d6 --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app-updates/fonts.scss @@ -0,0 +1,9 @@ +@use "../metrics"; + +$inter-latin: url("./Inter-Latin.woff2?browser") !default; + +@font-face { + font-family: ExampleUpdated; + font-weight: metrics.$weight; + src: $inter-latin format("woff2"); +} diff --git a/tests/integration/build/style/fixtures/multi-app-updates/invalid.scss b/tests/integration/build/style/fixtures/multi-app-updates/invalid.scss new file mode 100644 index 00000000..9f908838 --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app-updates/invalid.scss @@ -0,0 +1 @@ +.badge { diff --git a/tests/integration/build/style/fixtures/multi-app-updates/metrics.scss b/tests/integration/build/style/fixtures/multi-app-updates/metrics.scss new file mode 100644 index 00000000..05f1dc4b --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app-updates/metrics.scss @@ -0,0 +1 @@ +$weight: 700; diff --git a/tests/integration/build/style/fixtures/multi-app-updates/missing.scss b/tests/integration/build/style/fixtures/multi-app-updates/missing.scss new file mode 100644 index 00000000..ebbdf928 --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app-updates/missing.scss @@ -0,0 +1 @@ +@use "../missing/theme"; diff --git a/tests/integration/build/style/fixtures/multi-app/asis.js b/tests/integration/build/style/fixtures/multi-app/asis.js new file mode 100644 index 00000000..c4c4a14f --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app/asis.js @@ -0,0 +1 @@ +import "./src/shared/content/content.scss?asis"; diff --git a/tests/integration/build/style/fixtures/multi-app/document.js b/tests/integration/build/style/fixtures/multi-app/document.js new file mode 100644 index 00000000..b6beb654 --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app/document.js @@ -0,0 +1,3 @@ +import styles from "./src/shared/content/content.scss?unisolated"; + +export default styles; diff --git a/tests/integration/build/style/fixtures/multi-app/normal.js b/tests/integration/build/style/fixtures/multi-app/normal.js new file mode 100644 index 00000000..54953851 --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app/normal.js @@ -0,0 +1,3 @@ +import styles from "./src/shared/content/content.scss"; + +export default styles; diff --git a/tests/integration/build/style/fixtures/multi-app/src/apps/alpha/content/content.scss b/tests/integration/build/style/fixtures/multi-app/src/apps/alpha/content/content.scss new file mode 100644 index 00000000..de9cc26e --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app/src/apps/alpha/content/content.scss @@ -0,0 +1,10 @@ +@use "sass:math"; +@use "../theme/tokens" as appTokens; +@use "../theme/fonts" with ( + $inter-latin: url("../theme/fonts/Inter-Latin.woff2?browser&v=1#face") +); + +.badge { + color: appTokens.$accent; + --order: app; +} diff --git a/tests/integration/build/style/fixtures/multi-app/src/apps/alpha/theme/_metrics.scss b/tests/integration/build/style/fixtures/multi-app/src/apps/alpha/theme/_metrics.scss new file mode 100644 index 00000000..abc0c236 --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app/src/apps/alpha/theme/_metrics.scss @@ -0,0 +1 @@ +$weight: 400; diff --git a/tests/integration/build/style/fixtures/multi-app/src/apps/alpha/theme/_tokens.scss b/tests/integration/build/style/fixtures/multi-app/src/apps/alpha/theme/_tokens.scss new file mode 100644 index 00000000..2e174f9b --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app/src/apps/alpha/theme/_tokens.scss @@ -0,0 +1 @@ +$accent: blue; diff --git a/tests/integration/build/style/fixtures/multi-app/src/apps/alpha/theme/fonts/Inter-Latin.woff2 b/tests/integration/build/style/fixtures/multi-app/src/apps/alpha/theme/fonts/Inter-Latin.woff2 new file mode 100644 index 00000000..626ce765 --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app/src/apps/alpha/theme/fonts/Inter-Latin.woff2 @@ -0,0 +1 @@ +alpha-font-fixture diff --git a/tests/integration/build/style/fixtures/multi-app/src/apps/alpha/theme/fonts/_index.scss b/tests/integration/build/style/fixtures/multi-app/src/apps/alpha/theme/fonts/_index.scss new file mode 100644 index 00000000..07fdd6df --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app/src/apps/alpha/theme/fonts/_index.scss @@ -0,0 +1,9 @@ +@use "../metrics"; + +$inter-latin: url("./Inter-Latin.woff2?browser") !default; + +@font-face { + font-family: Example; + font-weight: metrics.$weight; + src: $inter-latin format("woff2"); +} diff --git a/tests/integration/build/style/fixtures/multi-app/src/apps/beta/content/content.scss b/tests/integration/build/style/fixtures/multi-app/src/apps/beta/content/content.scss new file mode 100644 index 00000000..de9cc26e --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app/src/apps/beta/content/content.scss @@ -0,0 +1,10 @@ +@use "sass:math"; +@use "../theme/tokens" as appTokens; +@use "../theme/fonts" with ( + $inter-latin: url("../theme/fonts/Inter-Latin.woff2?browser&v=1#face") +); + +.badge { + color: appTokens.$accent; + --order: app; +} diff --git a/tests/integration/build/style/fixtures/multi-app/src/apps/beta/theme/_metrics.scss b/tests/integration/build/style/fixtures/multi-app/src/apps/beta/theme/_metrics.scss new file mode 100644 index 00000000..abc0c236 --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app/src/apps/beta/theme/_metrics.scss @@ -0,0 +1 @@ +$weight: 400; diff --git a/tests/integration/build/style/fixtures/multi-app/src/apps/beta/theme/_tokens.scss b/tests/integration/build/style/fixtures/multi-app/src/apps/beta/theme/_tokens.scss new file mode 100644 index 00000000..0a9308d8 --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app/src/apps/beta/theme/_tokens.scss @@ -0,0 +1 @@ +$accent: green; diff --git a/tests/integration/build/style/fixtures/multi-app/src/apps/beta/theme/fonts/Inter-Latin.woff2 b/tests/integration/build/style/fixtures/multi-app/src/apps/beta/theme/fonts/Inter-Latin.woff2 new file mode 100644 index 00000000..966d9c69 --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app/src/apps/beta/theme/fonts/Inter-Latin.woff2 @@ -0,0 +1 @@ +beta-font-fixture diff --git a/tests/integration/build/style/fixtures/multi-app/src/apps/beta/theme/fonts/_index.scss b/tests/integration/build/style/fixtures/multi-app/src/apps/beta/theme/fonts/_index.scss new file mode 100644 index 00000000..07fdd6df --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app/src/apps/beta/theme/fonts/_index.scss @@ -0,0 +1,9 @@ +@use "../metrics"; + +$inter-latin: url("./Inter-Latin.woff2?browser") !default; + +@font-face { + font-family: Example; + font-weight: metrics.$weight; + src: $inter-latin format("woff2"); +} diff --git a/tests/integration/build/style/fixtures/multi-app/src/shared/content/content.scss b/tests/integration/build/style/fixtures/multi-app/src/shared/content/content.scss new file mode 100644 index 00000000..ad588f9f --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app/src/shared/content/content.scss @@ -0,0 +1,12 @@ +@use "sass:math"; +@use "@tokens" as aliasTokens; +@use "fixture-theme" as packageTokens; +@use "../theme/tokens" as sharedTokens; + +.badge { + color: sharedTokens.$accent; + width: math.div(4px, 2); + --alias: #{aliasTokens.$value}; + --package: #{packageTokens.$value}; + --order: shared; +} diff --git a/tests/integration/build/style/fixtures/multi-app/src/shared/theme/_alias.scss b/tests/integration/build/style/fixtures/multi-app/src/shared/theme/_alias.scss new file mode 100644 index 00000000..f8f07da0 --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app/src/shared/theme/_alias.scss @@ -0,0 +1 @@ +$value: alias; diff --git a/tests/integration/build/style/fixtures/multi-app/src/shared/theme/_tokens.scss b/tests/integration/build/style/fixtures/multi-app/src/shared/theme/_tokens.scss new file mode 100644 index 00000000..a96ed09a --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app/src/shared/theme/_tokens.scss @@ -0,0 +1 @@ +$accent: red; diff --git a/tests/integration/build/style/fixtures/multi-app/vendor/fixture-theme/_index.scss b/tests/integration/build/style/fixtures/multi-app/vendor/fixture-theme/_index.scss new file mode 100644 index 00000000..623ff60a --- /dev/null +++ b/tests/integration/build/style/fixtures/multi-app/vendor/fixture-theme/_index.scss @@ -0,0 +1 @@ +$value: package; diff --git a/tests/integration/build/style/style-merge.integration.test.ts b/tests/integration/build/style/style-merge.integration.test.ts new file mode 100644 index 00000000..d83eaf4b --- /dev/null +++ b/tests/integration/build/style/style-merge.integration.test.ts @@ -0,0 +1,259 @@ +import {readFile, rm, unlink, writeFile} from "fs/promises"; +import path from "path"; +import vm from "vm"; +import postcss from "postcss"; +import type {Compilation, Compiler, NormalModule, Stats} from "@rspack/core"; + +import {DefaultStylesLayer, DocumentStylesLayer} from "@cli/bundler/layers"; + +import {closeCompiler, createCompiler, createFixture, entries, fixtures, getCss, runCompiler} from "./compiler"; + +let root: string; +let compiler: Compiler | undefined; + +beforeEach(async () => { + root = await createFixture(); +}); + +afterEach(async () => { + try { + if (compiler) { + await closeCompiler(compiler); + } + } finally { + compiler = undefined; + await rm(root, {recursive: true, force: true}); + } +}); + +const getBadgeClass = (compilation: Compilation, entry: string): string => { + const script = compilation.getAsset(`${entry}.js`)!.source.source().toString(); + + return vm.runInNewContext(`${script}\nmodule.exports.default.badge`, {module: {exports: {}}}); +}; + +const getFont = (compilation: Compilation): string => { + return compilation + .getAssets() + .filter(asset => asset.name.endsWith(".woff2")) + .map(asset => asset.source.source().toString()) + .join(""); +}; + +const assertSuccess = (stats: Stats): void => { + expect(stats.toJson({all: false, errors: true}).errors).toEqual([]); +}; + +const hasDeclaration = (css: string, prop: string, value: string): boolean => { + let found = false; + + postcss.parse(css).walkDecls(prop, declaration => { + found ||= declaration.value === value; + }); + + return found; +}; + +test.each([ + ["alpha", "blue"], + ["beta", "green"], +])("merges %s styles into one CSS module per destination with its own resources", async (app, color) => { + compiler = await createCompiler(root, app); + const stats = await runCompiler(compiler); + assertSuccess(stats); + const {compilation} = stats; + const badge = getBadgeClass(compilation, "normal"); + + expect(badge).toMatch(new RegExp(`^${app}-badge__`)); + expect(getBadgeClass(compilation, "document")).toBe(badge); + + for (const entry of entries) { + const css = getCss(compilation, entry); + const selector = entry === "asis" ? ".badge" : `.${badge}`; + const rules = postcss.parse(css).nodes.filter(node => node.type === "rule"); + + expect(rules.map(rule => rule.selector)).toEqual([selector, selector]); + expect(hasDeclaration(rules[0].toString(), "color", "red")).toBe(true); + expect(hasDeclaration(rules[1].toString(), "color", color)).toBe(true); + expect(hasDeclaration(css, "width", "2px")).toBe(true); + expect(hasDeclaration(css, "--alias", "alias")).toBe(true); + expect(hasDeclaration(css, "--package", "package")).toBe(true); + expect(hasDeclaration(css, "font-weight", "400")).toBe(true); + expect(css).toMatch( + /url\(chrome-extension:\/\/__MSG_@@extension_id__\/assets\/Inter-Latin\.[a-f0-9]+\.woff2#face\)/ + ); + } + + const font = path.join(root, "src/apps", app, "theme/fonts/Inter-Latin.woff2"); + expect(Buffer.from(getFont(compilation))).toEqual(await readFile(font)); + + const sharedPath = path.join(root, "src/shared/content/content.scss"); + const resources = [...compilation.modules] + .filter((module): module is NormalModule => "resource" in module) + .map(module => ({resource: module.resource, layer: module.layer})); + + expect(resources).toEqual( + expect.arrayContaining([ + {resource: sharedPath, layer: DefaultStylesLayer}, + {resource: `${sharedPath}?unisolated`, layer: DocumentStylesLayer}, + {resource: `${sharedPath}?asis`, layer: DefaultStylesLayer}, + ]) + ); + + for (const filename of ["content/content.scss", "theme/fonts/_index.scss", "theme/_metrics.scss"]) { + expect(compilation.fileDependencies.has(path.join(root, "src/apps", app, filename))).toBe(true); + } +}); + +test("mergeStyles=false compiles the shared source without loading the app override", async () => { + compiler = await createCompiler(root, "alpha", false); + const stats = await runCompiler(compiler); + assertSuccess(stats); + + for (const entry of entries) { + const css = getCss(stats.compilation, entry); + + expect(hasDeclaration(css, "--order", "shared")).toBe(true); + expect(hasDeclaration(css, "--order", "app")).toBe(false); + expect(css).not.toContain("@font-face"); + } + + expect(stats.compilation.fileDependencies.has(path.join(root, "src/apps/alpha/content/content.scss"))).toBe(false); +}); + +test.each([ + ["conflict", /already a module with namespace "sharedTokens"/], + ["missing", /Can't find stylesheet to import/], + ["invalid", /Unclosed block/], +])("fails the build for a %s app override", async (name, message) => { + const appPath = path.join(root, "src/apps/alpha/content/content.scss"); + await writeFile(appPath, await readFile(path.join(fixtures, "multi-app-updates", `${name}.scss`))); + compiler = await createCompiler(root, "alpha"); + const stats = await runCompiler(compiler); + + expect(stats.hasErrors()).toBe(true); + expect(stats.toString({all: false, errors: true})).toMatch(message); +}); + +test.each(["alpha", "beta"])( + "watch tracks %s override creation, edits, dependencies and removal", + async app => { + const appPath = path.join(root, "src/apps", app, "content/content.scss"); + const initialSource = path.join(fixtures, "multi-app/src/apps", app, "content/content.scss"); + await unlink(appPath); + compiler = await createCompiler(root, app); + let complete: ((error: Error | null, stats?: Stats) => void) | undefined; + + const next = ( + description: string, + matches: (compilation: Compilation) => boolean, + update?: () => Promise<void>, + expectErrors = false + ) => + new Promise<Stats>((resolve, reject) => { + const timer = setTimeout(() => { + complete = undefined; + reject(new Error(`Style watch did not observe ${description} for ${app}`)); + }, 10_000); + + complete = (error, stats) => { + if (!error && stats && !stats.hasErrors() && (expectErrors || !matches(stats.compilation))) { + return; + } + + clearTimeout(timer); + complete = undefined; + + if (error || !stats || (!expectErrors && stats.hasErrors())) { + reject(error ?? new Error(stats?.toString({all: false, errors: true}))); + } else { + // Rspack reconnects the watcher on nextTick after delivering its compilation callback. + setImmediate(() => resolve(stats)); + } + }; + + void update?.().catch(error => complete?.(error)); + }); + + const contains = (prop: string, value: string) => (compilation: Compilation) => + entries.every(entry => hasDeclaration(getCss(compilation, entry), prop, value)); + const updateFrom = async (destination: string, filename: string): Promise<void> => { + await writeFile(destination, await readFile(path.join(fixtures, "multi-app-updates", filename))); + }; + + const initial = next("shared styles without an override", contains("--order", "shared")); + const watcher = compiler.watch({poll: 50}, (error, stats) => complete?.(error, stats)); + + try { + let stats = await initial; + const badge = getBadgeClass(stats.compilation, "normal"); + + expect(stats.compilation.missingDependencies.has(appPath)).toBe(true); + + stats = await next("override creation", contains("--order", "app"), async () => { + await writeFile(appPath, await readFile(initialSource)); + }); + + expect(stats.compilation.fileDependencies.has(appPath)).toBe(true); + expect(getBadgeClass(stats.compilation, "normal")).toBe(badge); + + stats = await next("override edit", contains("--order", "app-updated"), () => + updateFrom(appPath, "content.scss") + ); + expect(getBadgeClass(stats.compilation, "normal")).toBe(badge); + + stats = await next( + "invalid override error", + () => true, + () => updateFrom(appPath, "invalid.scss"), + true + ); + + expect(stats.toString({all: false, errors: true})).toContain("Unclosed block"); + expect(stats.compilation.fileDependencies.has(appPath)).toBe(true); + + stats = await next("override correction", contains("--order", "app-updated"), () => + updateFrom(appPath, "content.scss") + ); + + expect(getBadgeClass(stats.compilation, "normal")).toBe(badge); + + await next("imported index edit", contains("font-family", "ExampleUpdated"), () => + updateFrom(path.join(root, "src/apps", app, "theme/fonts/_index.scss"), "fonts.scss") + ); + + await next("nested partial edit", contains("font-weight", "700"), () => + updateFrom(path.join(root, "src/apps", app, "theme/_metrics.scss"), "metrics.scss") + ); + + const expectedFont = await readFile(path.join(fixtures, "multi-app-updates/Inter-Latin.woff2"), "utf8"); + + await next( + "font resource edit", + compilation => getFont(compilation) === expectedFont, + () => updateFrom(path.join(root, "src/apps", app, "theme/fonts/Inter-Latin.woff2"), "Inter-Latin.woff2") + ); + + stats = await next( + "override removal", + compilation => + entries.every(entry => !hasDeclaration(getCss(compilation, entry), "--order", "app-updated")), + () => unlink(appPath) + ); + + expect(stats.compilation.missingDependencies.has(appPath)).toBe(true); + expect(getFont(stats.compilation)).toBe(""); + expect(getBadgeClass(stats.compilation, "normal")).toBe(badge); + + stats = await next("override recreation", contains("--order", "app-updated"), () => + updateFrom(appPath, "content.scss") + ); + + expect(getBadgeClass(stats.compilation, "normal")).toBe(badge); + expect(getFont(stats.compilation)).toBe(expectedFont); + } finally { + await new Promise<void>(resolve => watcher.close(resolve)); + } + }, + 30_000 +); diff --git a/tests/integration/types/fixtures/locale/substitutions.ts b/tests/integration/types/fixtures/locale/substitutions.ts index 98a5201e..b387aa0e 100644 --- a/tests/integration/types/fixtures/locale/substitutions.ts +++ b/tests/integration/types/fixtures/locale/substitutions.ts @@ -1,4 +1,11 @@ -import type {LocaleNonPluralKeys, LocalePluralKeys, LocaleSubstitutionArgs} from "adnbn/locale"; +import { + ObservableLocale, + Language, + type LocaleDynamicProvider, + type LocaleNonPluralKeys, + type LocalePluralKeys, + type LocaleSubstitutionArgs, +} from "adnbn/locale"; interface Structure { "app.name": {plural: false; substitutions: []}; @@ -6,6 +13,14 @@ interface Structure { "app.cars": {plural: true; substitutions: ["count"]}; } +const observable = new ObservableLocale<Structure>(false); +const provider: LocaleDynamicProvider<Structure> = observable; + +provider.trans("app.greeting", {name: "Alice"}); +provider.choice("app.cars", 2, {count: 2}); +void provider.change(Language.French); +observable.snapshot().get("app.name"); + declare function trans<K extends LocaleNonPluralKeys<Structure>>( key: K, ...args: LocaleSubstitutionArgs<Structure, K> diff --git a/tests/integration/types/fixtures/registries/fallback.ts b/tests/integration/types/fixtures/registries/fallback.ts index 98e7ebfb..bea4557c 100644 --- a/tests/integration/types/fixtures/registries/fallback.ts +++ b/tests/integration/types/fixtures/registries/fallback.ts @@ -16,6 +16,10 @@ api.changeSidebar("custom"); api.changeActionIcon("custom"); api.changeSidebarIcon(); +api.defineConfig({action: {icon: "custom"}}); +api.definePopup({icon: "custom", render: "Popup"}); +api.defineSidebar({icon: "custom", render: "Sidebar"}); + // @ts-expect-error: Empty transport registries remain strict. api.getService("unknown"); // @ts-expect-error: Empty transport registries remain strict. diff --git a/tests/integration/types/fixtures/registries/generated.ts b/tests/integration/types/fixtures/registries/generated.ts index a3122cff..7e0e40a4 100644 --- a/tests/integration/types/fixtures/registries/generated.ts +++ b/tests/integration/types/fixtures/registries/generated.ts @@ -27,6 +27,17 @@ api.getPopups().get("popup"); api.getSidebars().get("sidebar"); api.getIcons().get("brand"); +api.defineConfig({action: {icon: "brand"}}); +api.definePopup({icon: "brand", render: "Popup"}); +api.defineSidebar({icon: "brand", render: "Sidebar"}); + +// @ts-expect-error: Action defaults use the generated icon group names. +api.defineConfig({action: {icon: "unknown"}}); +// @ts-expect-error: Popup icons use the generated icon group names. +api.definePopup({icon: "unknown", render: "Popup"}); +// @ts-expect-error: Sidebar icons use the generated icon group names. +api.defineSidebar({icon: "unknown", render: "Sidebar"}); + const worker = api.getService("worker"); const document = api.getOffscreen("document"); const frame = api.getSandbox("frame"); diff --git a/tests/integration/types/fixtures/relay/definition.ts b/tests/integration/types/fixtures/relay/definition.ts new file mode 100644 index 00000000..94b1645a --- /dev/null +++ b/tests/integration/types/fixtures/relay/definition.ts @@ -0,0 +1,99 @@ +import * as api from "adnbn"; +import * as entry from "adnbn/entry/relay"; +import {Builder as ContentBuilder} from "adnbn/entry/content/vanilla"; +import {Builder as ReactContentBuilder} from "adnbn/entry/content/react"; + +const definition = api.defineRelay({ + name: "scanner", + allFrames: api.RelayAllFrames.All, + method: api.RelayMethod.Scripting, + init: () => ({scan: (text: string) => text.length}), +}); + +new entry.Builder(definition, ContentBuilder); + +const preparedDefinition = api.defineRelay({ + name: "scanner", + init: () => ({scan: (text: string) => text.length}), + prepare: async () => ({title: "Prepared"}), + + render: ({data}) => { + const title: string = data.title; + // @ts-expect-error: Runtime constructors preserve the inferred prepare data. + data.missing; + + return title; + }, +}); + +const preparedBuilder = new entry.Builder(preparedDefinition, ContentBuilder); +entry.default(preparedDefinition, ContentBuilder); + +const unresolved: entry.RelayUnresolvedDefinition<ReturnType<typeof definition.init>> = {}; +new entry.Builder(unresolved, ContentBuilder); +entry.default(entry.resolveDefinition({default: definition}, "scanner"), ContentBuilder); + +type PreparedData = {title: string}; +type Scanner = ReturnType<typeof definition.init>; +type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? true : false; +type Expect<T extends true> = T; + +type PreparedBuilder = Expect<Equal<typeof preparedBuilder, entry.Builder<Scanner, PreparedData, "none">>>; + +new entry.Builder(preparedDefinition, ReactContentBuilder); +entry.default(preparedDefinition, ReactContentBuilder); + +class ShadowBuilder extends ContentBuilder<PreparedData, "shadow"> {} + +const shadowDefinition = api.defineRelay({ + name: "shadow", + init: definition.init, + isolation: "shadow", + prepare: () => ({title: "Shadow"}), + container: {tagName: "input", value: "initial"}, + render: ({data}) => data.title, +}); + +new entry.Builder(shadowDefinition, ShadowBuilder); +entry.default(shadowDefinition, ShadowBuilder); + +const partial: entry.RelayUnresolvedDefinition<Scanner, PreparedData, "shadow"> = { + isolation: "shadow", + prepare: shadowDefinition.prepare, +}; + +new entry.Builder(partial, ShadowBuilder); +entry.default(partial, ShadowBuilder); + +// Inference is checked above; explicit arguments keep rejection checks from exploring fallback inference candidates. +// @ts-expect-error: A specialized adapter cannot receive a different isolation mode. +new entry.Builder<Scanner, PreparedData, "none">(preparedDefinition, ShadowBuilder); +// @ts-expect-error: Bootstrap preserves the same adapter isolation constraint. +entry.default<Scanner, PreparedData, "none">(preparedDefinition, ShadowBuilder); + +class NumericBuilder extends ContentBuilder<number, "none"> {} + +// @ts-expect-error: The adapter must accept the data produced by prepare. +new entry.Builder<Scanner, PreparedData, "none">(preparedDefinition, NumericBuilder); +// @ts-expect-error: Bootstrap must not erase the prepared data contract. +entry.default<Scanner, PreparedData, "none">(preparedDefinition, NumericBuilder); + +new entry.Builder( + { + init: definition.init, + prepare: () => ({title: "Inline"}), + + render: ({data}) => { + // @ts-expect-error: Inline definitions still infer the prepare result. + data.missing; + + return data.title; + }, + }, + ContentBuilder +); + +// @ts-expect-error: Container properties remain specific to the selected HTML tag. +api.defineRelay({name: "invalid-tag", init: definition.init, container: {tagName: "div", href: "https://example.com"}}); +// @ts-expect-error: Container options do not accept DOM methods. +api.defineRelay({name: "invalid-method", init: definition.init, container: {tagName: "input", focus: () => {}}}); diff --git a/src/cli/plugins/content/tests/fixtures/relay-types/virtual.ts b/tests/integration/types/fixtures/relay/virtual.ts similarity index 100% rename from src/cli/plugins/content/tests/fixtures/relay-types/virtual.ts rename to tests/integration/types/fixtures/relay/virtual.ts diff --git a/tests/integration/types/relay.integration.test.ts b/tests/integration/types/relay.integration.test.ts new file mode 100644 index 00000000..4005ee28 --- /dev/null +++ b/tests/integration/types/relay.integration.test.ts @@ -0,0 +1,58 @@ +import path from "path"; +import ts from "typescript"; + +describe("Relay entrypoint contracts", () => { + const projectDir = path.resolve(__dirname, "../../.."); + const fixture = path.join(__dirname, "fixtures/relay/definition.ts"); + + test.each(["source", "package"])( + "checks Relay constructors and bootstrap through the %s API", + mode => { + const config = ts.readConfigFile(path.join(projectDir, "tsconfig.json"), ts.sys.readFile); + const parsed = ts.parseJsonConfigFileContent(config.config, ts.sys, projectDir); + expect(config.error).toBeUndefined(); + expect(parsed.errors).toEqual([]); + + const options: ts.CompilerOptions = { + ...(mode === "source" ? parsed.options : {}), + module: ts.ModuleKind.ESNext, + moduleResolution: ts.ModuleResolutionKind.Bundler, + target: ts.ScriptTarget.ESNext, + jsx: ts.JsxEmit.ReactJSX, + strict: true, + noEmit: true, + skipLibCheck: mode === "source", + types: ["node", "chrome"], + }; + + const rootNames = [fixture]; + + if (mode === "source") { + rootNames.push( + path.join(__dirname, "fixtures/relay/virtual.ts"), + path.join(projectDir, "src/cli/virtual/relay.ts"), + path.join(projectDir, "src/cli/virtual/virtual.d.ts") + ); + } + + const host = ts.createCompilerHost(options); + const program = ts.createProgram(rootNames, options, host); + + const diagnostics = ts.getPreEmitDiagnostics(program).map(diagnostic => { + const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"); + const position = diagnostic.file?.getLineAndCharacterOfPosition(diagnostic.start ?? 0); + + return `${diagnostic.file?.fileName ?? "compiler"}:${(position?.line ?? 0) + 1}: ${message}`; + }); + + expect(diagnostics).toEqual([]); + + const apiFile = ts.resolveModuleName("adnbn", fixture, options, host).resolvedModule?.resolvedFileName; + + expect(apiFile).toBe( + path.join(projectDir, mode === "source" ? "src/index.ts" : "dist/index.d.ts").replace(/\\/g, "/") + ); + }, + 30_000 + ); +}); diff --git a/tests/integration/utils/BuildSession.ts b/tests/integration/utils/BuildSession.ts new file mode 100644 index 00000000..846f8870 --- /dev/null +++ b/tests/integration/utils/BuildSession.ts @@ -0,0 +1,197 @@ +import {fork, type ChildProcess} from "node:child_process"; +import {appendFileSync} from "node:fs"; +import {mkdir, mkdtemp, rm} from "node:fs/promises"; +import path from "node:path"; + +import type { + BuildSessionFailure, + BuildSessionRequest, + BuildSessionResponse, + BuildSessionSample, +} from "./build-session-protocol"; + +interface BuildSessionPending { + id: number; + resolve(sample: BuildSessionSample): void; + reject(error: Error): void; + timer: NodeJS.Timeout; +} + +export class BuildSessionError extends Error { + readonly stats?: string; + + constructor(failure: BuildSessionFailure) { + super(failure.message, failure.cause ? {cause: new BuildSessionError(failure.cause)} : undefined); + this.name = failure.name; + this.stats = failure.stats; + } +} + +/** One file owns one process; builds are sequential and cwd never changes. */ +export default class BuildSession { + private readonly child: ChildProcess; + private readonly closed: Promise<void>; + private pending?: BuildSessionPending; + private sequence = 0; + private stopped = false; + private failure?: Error; + private output = ""; + private disposal?: Promise<void>; + + private constructor( + readonly directory: string, + projectRoot: string + ) { + this.child = fork(path.join(projectRoot, "tests/integration/utils/build-session-child.mjs"), [projectRoot], { + cwd: directory, + // Keep Node flags, including the macOS JIT workaround and optional benchmark GC. + execArgv: process.execArgv.filter(flag => flag === "--no-sparkplug" || flag === "--expose-gc"), + stdio: ["ignore", "pipe", "pipe", "ipc"], + }); + + const capture = (chunk: Buffer) => { + this.output = (this.output + chunk.toString()).slice(-64 * 1024); + }; + + this.child.stdout!.on("data", capture); + this.child.stderr!.on("data", capture); + this.child.on("message", message => this.receive(message as BuildSessionResponse)); + this.child.once("error", error => this.terminate(error)); + this.closed = new Promise(resolve => { + this.child.once("close", (code, signal) => { + this.stopped = true; + const pending = this.pending; + this.pending = undefined; + + if (pending) { + clearTimeout(pending.timer); + pending.reject( + this.failure ?? new Error(`Build session exited with ${signal ?? code}\n${this.output}`) + ); + } + + resolve(); + }); + }); + } + + static async create(projectRoot: string): Promise<BuildSession> { + const parent = path.join(projectRoot, ".cache/integration"); + await mkdir(parent, {recursive: true}); + const directory = await mkdtemp(path.join(parent, "session-")); + + try { + // The external plugin's parent-directory creation is not atomic. This directory is session-owned. + await mkdir(path.join(directory, "node_modules"), {recursive: true}); + + return new BuildSession(directory, projectRoot); + } catch (error) { + await rm(directory, {recursive: true, force: true}); + throw error; + } + } + + get pid(): number | undefined { + return this.child.pid; + } + + async build( + rootDir: string, + browser = "chrome", + manifestVersion: 2 | 3 = 3, + timeout = 30_000 + ): Promise<BuildSessionSample> { + if (this.stopped) { + throw new Error("Build session is closed"); + } + + if (this.pending) { + throw new Error("Build session already has a pending request; await it before starting another"); + } + + this.output = ""; + const id = ++this.sequence; + const request: BuildSessionRequest = { + id, + rootDir: path.resolve(rootDir), + browser, + manifestVersion, + environment: {...process.env}, + }; + + return new Promise((resolve, reject) => { + const timer = setTimeout( + () => this.terminate(new Error(`Build session timed out after ${timeout} ms\n${this.output}`)), + timeout + ); + + this.pending = {id, resolve, reject, timer}; + this.child.send(request, error => { + if (error) { + this.terminate(error); + } + }); + }); + } + + private receive(response: BuildSessionResponse): void { + const pending = this.pending; + + if (this.stopped || !pending || response.id !== pending.id) { + return; + } + + clearTimeout(pending.timer); + this.pending = undefined; + + try { + // Diagnostics are opt-in and report the child, never Jest worker memory. + if (process.env.ADNBN_SESSION_METRICS) { + appendFileSync(process.env.ADNBN_SESSION_METRICS, JSON.stringify(response.sample) + "\n"); + } + + if (response.error) { + pending.reject(new BuildSessionError(response.error)); + } else { + pending.resolve(response.sample); + } + } catch (error) { + pending.reject(error as Error); + } + } + + private terminate(error: Error): void { + this.failure ??= error; + this.stopped = true; + this.child.kill("SIGKILL"); + } + + dispose(): Promise<void> { + this.disposal ??= this.close(); + + return this.disposal; + } + + private async close(): Promise<void> { + this.stopped = true; + + if (this.pending) { + this.terminate(new Error("Build session disposed during a pending request")); + } else if (this.child.connected) { + this.child.send({close: true}, error => { + if (error) { + this.terminate(error); + } + }); + } + + const timer = setTimeout(() => this.child.kill("SIGKILL"), 5_000); + + try { + await this.closed; + } finally { + clearTimeout(timer); + await rm(this.directory, {recursive: true, force: true, maxRetries: 5, retryDelay: 200}); + } + } +} diff --git a/tests/integration/utils/build-session-child.mjs b/tests/integration/utils/build-session-child.mjs new file mode 100644 index 00000000..dbeab35c --- /dev/null +++ b/tests/integration/utils/build-session-child.mjs @@ -0,0 +1,78 @@ +import path from "node:path"; +import {pathToFileURL} from "node:url"; + +// Install before loading the framework: the parent may disappear during startup or a build. +process.on("disconnect", () => process.exit(0)); + +const [projectRoot] = process.argv.slice(2); +const app = import(pathToFileURL(path.join(projectRoot, "dist/cli/builders/app/index.js")).href); +// The request handler reports load failures through the same error protocol as build failures. +app.catch(() => {}); +let active = false; +let builds = 0; + +const serializeError = (error, depth = 0) => ({ + name: error?.name ?? "Error", + message: error?.message ?? String(error), + ...(error?.cause !== undefined && depth < 5 ? {cause: serializeError(error.cause, depth + 1)} : {}), + ...(error?.stats + ? {stats: error.stats.toString({all: false, errors: true, errorDetails: true, colors: false})} + : {}), +}); + +process.on("message", async request => { + if (request.close) { + process.exit(0); + } + + if (active) { + // A parent protocol violation cannot leave overlapping builds running. + process.exit(1); + } + + active = true; + const environment = process.env; + let failure; + + try { + process.env = {...request.environment}; + const {buildApp} = await app; + await buildApp({ + rootDir: request.rootDir, + browser: request.browser, + manifestVersion: request.manifestVersion, + mode: "production", + }); + } catch (error) { + failure = serializeError(error); + } finally { + process.env = environment; + active = false; + } + + builds++; + + if (global.gc) { + global.gc(); + } + + const {rss, heapUsed} = process.memoryUsage(); + + if (process.connected) { + process.send({ + id: request.id, + ...(failure ? {error: failure} : {}), + sample: { + pid: process.pid, + build: builds, + rootDir: request.rootDir, + cwd: process.cwd(), + rss, + heapUsed, + maxRssKiB: process.resourceUsage().maxRSS, + }, + }); + } +}); + +process.send?.({ready: true}); diff --git a/tests/integration/utils/build-session-protocol.ts b/tests/integration/utils/build-session-protocol.ts new file mode 100644 index 00000000..8d606e69 --- /dev/null +++ b/tests/integration/utils/build-session-protocol.ts @@ -0,0 +1,30 @@ +export interface BuildSessionRequest { + id: number; + rootDir: string; + browser: string; + manifestVersion: 2 | 3; + environment: NodeJS.ProcessEnv; +} + +export interface BuildSessionFailure { + name: string; + message: string; + cause?: BuildSessionFailure; + stats?: string; +} + +export interface BuildSessionSample { + pid: number; + build: number; + rootDir: string; + cwd: string; + rss: number; + heapUsed: number; + maxRssKiB: number; +} + +export interface BuildSessionResponse { + id: number; + error?: BuildSessionFailure; + sample: BuildSessionSample; +} diff --git a/tests/integration/utils/fixture.test.ts b/tests/integration/utils/fixture.test.ts new file mode 100644 index 00000000..4b199e2a --- /dev/null +++ b/tests/integration/utils/fixture.test.ts @@ -0,0 +1,19 @@ +import path from "node:path"; + +import {findIntegrationFixtures} from "./fixture"; + +const integration = path.resolve(__dirname, ".."); + +test("does not prepare internal CLI configs as standalone consumer applications", async () => { + expect(await findIntegrationFixtures(path.join(integration, "build/cli"))).toEqual([]); +}); + +test("discovers the three complete override applications", async () => { + const override = path.join(integration, "build/override"); + + expect(await findIntegrationFixtures(override)).toEqual([ + path.join(override, "bookmarks"), + path.join(override, "history"), + path.join(override, "newtab"), + ]); +}); diff --git a/tests/integration/utils/fixture.ts b/tests/integration/utils/fixture.ts index 1b28fa15..276f9612 100644 --- a/tests/integration/utils/fixture.ts +++ b/tests/integration/utils/fixture.ts @@ -1,9 +1,11 @@ import {cp, lstat, mkdir, mkdtemp, readFile, readdir, realpath, rm, symlink} from "fs/promises"; import path from "path"; +import type BuildSession from "./BuildSession"; import {run} from "./process"; export interface IntegrationFixtureBuildOptions { + session?: BuildSession; browser?: string; manifestVersion?: 2 | 3; } @@ -41,7 +43,7 @@ const linkDependency = async (source: string, destination: string): Promise<void export const prepareIntegrationFixture = async ( projectRoot: string, directory: string, - {browser = "chrome", manifestVersion = 3}: IntegrationFixtureBuildOptions = {}, + {browser = "chrome", manifestVersion = 3, session}: IntegrationFixtureBuildOptions = {}, timeout?: number ): Promise<string> => { const manifest = JSON.parse(await readFile(path.join(directory, "package.json"), "utf8")) as { @@ -55,26 +57,31 @@ export const prepareIntegrationFixture = async ( ); } - await run( - process.execPath, - [ - path.join(projectRoot, "bin", "adnbn.js"), - "build", - ".", - "-b", - browser, - ...(manifestVersion === 2 ? ["--mv2"] : []), - ], - directory, - timeout - ); + if (session) { + await session.build(directory, browser, manifestVersion, timeout); + } else { + await run( + process.execPath, + [ + path.join(projectRoot, "bin", "adnbn.js"), + "build", + ".", + "-b", + browser, + ...(manifestVersion === 2 ? ["--mv2"] : []), + ], + directory, + timeout + ); + } return path.join(directory, "dist", `myapp-${browser}-mv${manifestVersion}`); }; export const createIntegrationFixture = async ( projectRoot: string, - sourceDirectory: string + sourceDirectory: string, + session?: BuildSession ): Promise<IntegrationFixture> => { const cacheDirectory = path.join(projectRoot, ".cache", "integration"); @@ -95,7 +102,7 @@ export const createIntegrationFixture = async ( return { directory, - build: options => prepareIntegrationFixture(projectRoot, directory, options), + build: options => prepareIntegrationFixture(projectRoot, directory, {session, ...options}), dispose, }; }; @@ -103,7 +110,10 @@ export const createIntegrationFixture = async ( export const findIntegrationFixtures = async (directory: string): Promise<string[]> => { const entries = await readdir(directory, {withFileTypes: true}); - if (entries.some(entry => entry.isFile() && entry.name === "adnbn.config.ts")) { + const hasConfig = entries.some(entry => entry.isFile() && entry.name === "adnbn.config.ts"); + const hasPackage = entries.some(entry => entry.isFile() && entry.name === "package.json"); + + if (hasConfig && hasPackage) { return [directory]; } diff --git a/tests/jest-message.setup.ts b/tests/jest-message.setup.ts deleted file mode 100644 index e18826b0..00000000 --- a/tests/jest-message.setup.ts +++ /dev/null @@ -1,37 +0,0 @@ -type MessageSender = chrome.runtime.MessageSender; - -type MessageListener = (...args: any[]) => boolean | void; - -const listeners = new Set<MessageListener>(); - -chrome.runtime.onMessage.addListener = jest.fn((callback: MessageListener) => listeners.add(callback)); -chrome.runtime.onMessage.removeListener = jest.fn((callback: MessageListener) => listeners.delete(callback)); -chrome.runtime.onMessage.hasListeners = jest.fn(() => listeners.size > 0); - -const dispatch = (message: unknown, sender: MessageSender, callback?: (response: any) => void): void => { - let called = false; - let asynchronous = false; - - for (const listener of listeners) { - const result = listener(message, sender, (response: any) => { - if (called) return; - - called = true; - callback?.(response); - }); - - asynchronous ||= result === true; - } - - if (!asynchronous && !called) { - callback?.(undefined); - } -}; - -chrome.runtime.sendMessage = jest.fn().mockImplementation((message, callback) => { - dispatch(message, {} as MessageSender, callback); -}); - -chrome.tabs.sendMessage = jest.fn().mockImplementation((tabId, message, options, callback) => { - dispatch(message, {} as MessageSender, callback); -}); diff --git a/tests/jest-modules.setup.ts b/tests/jest-modules.setup.ts deleted file mode 100644 index 91d320f5..00000000 --- a/tests/jest-modules.setup.ts +++ /dev/null @@ -1,106 +0,0 @@ -jest.mock("@addon-core/browser", () => ({ - __esModule: true, - - throwRuntimeError: jest.fn(), - getManifest: jest.fn(() => ({manifest_version: 3, name: "Test extension", version: "1.0.0", permissions: []})), - getManifestVersion: jest.fn(), - isAvailableScripting: jest.fn(), - - browser: jest.fn(() => chrome), - isBackground: jest.fn(() => false), - isManifestVersion3: jest.fn(() => true), - - hasOffscreen: jest.fn(), - getOffscreenContext: jest.fn(), - getOffscreenUrl: jest.fn(), - getOffscreenPath: jest.fn(), - hasOffscreenUrl: jest.fn(), - hasOffscreenPath: jest.fn(), - closeOffscreen: jest.fn(), - createOffscreen: jest.fn(), - - containsPermissions: jest.fn(() => true), - requestPermissions: jest.fn(() => true), - onPermissionsAdded: jest.fn(() => jest.fn()), - onPermissionsRemoved: jest.fn(() => jest.fn()), - - getContexts: jest.fn(), - getAllFrames: jest.fn(), - getBrowserInfo: jest.fn(() => Promise.resolve({name: "Firefox", version: "153.0"})), - - executeScript: (...args: Parameters<typeof chrome.scripting.executeScript>) => { - return chrome.scripting.executeScript(...args); - }, - - sendMessage: (msg: any) => { - return new Promise(resolve => { - chrome.runtime.sendMessage(msg, (response: any) => { - resolve(response); - }); - }); - }, - - sendTabMessage: (tabId: number, msg: any, options: any) => { - return new Promise(resolve => { - chrome.tabs.sendMessage(tabId, msg, options, (response: any) => { - resolve(response); - }); - }); - }, - - onMessage: (callback: any) => { - chrome.runtime.onMessage.addListener(callback); - return () => chrome.runtime.onMessage.removeListener(callback); - }, -})); - -jest.mock("nanoid", () => ({ - nanoid: jest.fn(() => "mocked-id"), -})); - -jest.mock("nanoid/non-secure", () => ({ - nanoid: jest.fn(() => "mocked-id"), -})); - -jest.mock( - "@addon-core/storage", - () => { - type Unsubscribe = () => void; - type WatchMap = Record<string, (value: any) => void>; - - const createMockStorage = () => { - const store = new Map<string, any>(); - let watchers: WatchMap = {}; - - const get = jest.fn(async (key: string) => store.get(key)); - const set = jest.fn(async (key: string, value: any) => { - store.set(key, value); - const cb = watchers[key]; - if (cb) cb(value); - }); - const watch = jest.fn((map: WatchMap): Unsubscribe => { - watchers = map; - return () => { - watchers = {}; - }; - }); - - return {get, set, watch}; - }; - - return { - __esModule: true, - Storage: { - Local: () => createMockStorage(), - Sync: () => createMockStorage(), - Session: () => createMockStorage(), - }, - }; - }, - {virtual: true} -); - -jest.mock("@main/env", () => ({ - ...jest.requireActual("@main/env"), - isBrowser: jest.fn().mockReturnValue(false), -})); diff --git a/tests/jest-relay.setup.ts b/tests/jest-relay.setup.ts deleted file mode 100644 index d56547e3..00000000 --- a/tests/jest-relay.setup.ts +++ /dev/null @@ -1,43 +0,0 @@ -const resolveScriptingResult = async ( - injection: Parameters<typeof chrome.scripting.executeScript>[0] -): Promise<chrome.scripting.InjectionResult<unknown>[]> => { - const {func, target} = injection; - - if (!func) { - return []; - } - - const args = "args" in injection ? injection.args : []; - const frameIds = target.allFrames ? [0, 2] : target.frameIds || [0]; - const documentIds = target.documentIds || []; - const targets = documentIds.length - ? documentIds.map(documentId => ({ - documentId, - frameId: Number.parseInt(documentId.match(/\d+$/)?.[0] ?? "0", 10), - })) - : frameIds.map(frameId => ({frameId, documentId: `document-${frameId}`})); - - return Promise.all( - targets.map(async target => ({ - ...target, - result: await func(...args), - })) - ); -}; - -chrome.scripting = { - ...chrome.scripting, - executeScript: jest.fn().mockImplementation((injection, callback) => { - const result = resolveScriptingResult(injection); - - if (callback) { - result.then(callback); - } - - return result; - }), -}; - -jest.mock("../src/relay/utils", () => ({ - isRelayContext: jest.fn(), -})); diff --git a/tests/jest.setup.ts b/tests/jest.setup.ts index c2ac7fd3..c4d5308a 100644 --- a/tests/jest.setup.ts +++ b/tests/jest.setup.ts @@ -1,5 +1,13 @@ -import "jest-webextension-mock"; +import {startBrowserTest, stopBrowserTest} from "./browser-harness/session"; -import "./jest-message.setup"; -import "./jest-modules.setup"; -import "./jest-relay.setup"; +beforeEach(startBrowserTest); + +afterEach(async () => { + try { + await stopBrowserTest(); + } finally { + jest.restoreAllMocks(); + jest.clearAllMocks(); + jest.useRealTimers(); + } +}); diff --git a/tests/offscreen/MockLockManager.ts b/tests/offscreen/MockLockManager.ts new file mode 100644 index 00000000..d673aee7 --- /dev/null +++ b/tests/offscreen/MockLockManager.ts @@ -0,0 +1,90 @@ +type MockLockMode = "exclusive" | "shared"; +type LockState = {shared: number; exclusive: boolean}; +type MockLockOptions = {mode: MockLockMode}; +type MockLockCallback = () => unknown | Promise<unknown>; + +type LockTask = { + mode: MockLockMode; + callback: MockLockCallback; + resolve: (value: unknown) => void; + reject: (error: unknown) => void; +}; + +/** + * Models only ProxyOffscreen's Web Locks usage: queued shared/exclusive requests, + * with release after callback settlement, including rejection. Web Locks are outside the browser test-kit. + * Does not implement ifAvailable, steal or signal; extend this double if production starts using them. + */ +export default class MockLockManager { + private readonly queues = new Map<string, LockTask[]>(); + + private readonly states = new Map<string, LockState>(); + + public request(name: string, options: MockLockOptions, callback: MockLockCallback): Promise<unknown> { + return new Promise((resolve, reject) => { + const queue = this.queues.get(name) ?? []; + queue.push({mode: options.mode, callback, resolve, reject}); + this.queues.set(name, queue); + this.drain(name); + }); + } + + public pending(name: string): number { + return this.queues.get(name)?.length ?? 0; + } + + private state(name: string): LockState { + const state = this.states.get(name) ?? {shared: 0, exclusive: false}; + + this.states.set(name, state); + + return state; + } + + private drain(name: string): void { + const queue = this.queues.get(name); + const state = this.state(name); + + if (!queue?.length || state.exclusive) { + return; + } + + const task = queue[0]; + + if (task.mode === "exclusive") { + if (state.shared > 0) { + return; + } + + queue.shift(); + state.exclusive = true; + this.run(name, task); + + return; + } + + while (queue[0]?.mode === "shared" && !state.exclusive) { + const sharedTask = queue.shift()!; + + state.shared++; + this.run(name, sharedTask); + } + } + + private run(name: string, task: LockTask): void { + Promise.resolve() + .then(task.callback) + .then(task.resolve, task.reject) + .finally(() => { + const state = this.state(name); + + if (task.mode === "exclusive") { + state.exclusive = false; + } else { + state.shared--; + } + + this.drain(name); + }); + } +} diff --git a/tests/raw-module-resolver.cjs b/tests/raw-module-resolver.cjs index a28ef334..2d86da7f 100644 --- a/tests/raw-module-resolver.cjs +++ b/tests/raw-module-resolver.cjs @@ -2,5 +2,10 @@ module.exports = (request, options) => { const suffix = "?raw"; const target = request.endsWith(suffix) ? request.slice(0, -suffix.length) : request; + // Storage publishes ESM only. SWC transforms that entry for Jest's CJS runtime. + if (target === "@addon-core/storage" || target.startsWith("@addon-core/storage/")) { + return options.defaultResolver(target, {...options, conditions: ["import", "default"]}); + } + return options.defaultResolver(target, options); }; diff --git a/tsconfig.json b/tsconfig.json index 9511d008..ae6b805a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -73,7 +73,8 @@ "@shared/*": ["shared/*"], "@storage/*": ["storage/*"], "@transport/*": ["transport/*"], - "@typing/*": ["types/*"] + "@typing/*": ["types/*"], + "@tests/*": ["../tests/*"] }, "isolatedModules": true, "allowJs": true, @@ -87,7 +88,7 @@ "./src/cli/virtual/virtual.d.ts", "node_modules/@types/node/*.d.ts", "tests/**/*.test.ts", - "tests/pre-push.ts", + "tests/check.ts", "tests/integration/*.ts", "tests/jest*.ts" ],