9 Commits
Author SHA1 Message Date
Samuel Attard 8287520b9b test: use vitest's API and expect directly, drop the mocha shim and chai (#54439)
* test: assert with vitest's matchers instead of chai

Every assertion moves from chai's chainable language to vitest's own
matchers, so an assertion is always a call and never a bare property read:

- `to.equal` → `toBe`, `to.deep.equal`/`eql` → `toEqual`, `to.be.true` →
  `toBe(true)`, `to.have.property` → `toHaveProperty`, `to.include` →
  `toContain`/`toMatchObject`/`toContainEqual`, `to.be.a('string')` →
  `toBeTypeOf('string')`, `instanceOf` → `toBeInstanceOf`, `lengthOf` →
  `toHaveLength`, `above`/`least`/`below`/`most` → `toBeGreaterThan` and
  friends, `match` → `toMatch`, `throw` → `toThrow`, `oneOf` → `toContain`
  with the operands swapped, `closeTo(e, d)` → `Math.abs(x - e) <= d`,
  `members`/`keys` → `arrayContaining`/`Object.keys`. A chain that kept
  going after `.property()` or `.a()` becomes one matcher where vitest has
  one (`toHaveProperty(key, expect.any(String))`) and separate assertions
  otherwise. chai-as-promised becomes `.resolves`/`.rejects`, a failed
  manual check is `assert.fail()` rather than `new chai.AssertionError`.
- vitest reports promise assertions that are not awaited; the app badge,
  webview zoom and `import('electron')` specs had some that never ran and
  now do (the service worker badge ones waited for a count the worker had
  already cleared, so those just check the worker's answer; the zoom fixture
  issued loadURL() from dom-ready, which aborts, and now waits for the load).
- Code that runs outside the spec module graph (itremote bodies in a
  renderer, the utility-process net harness, two crash-case apps) is
  CommonJS and vitest refuses `require('vitest')`, so it loads the same
  module through spec/lib/vitest-cjs.cjs.
- A lint rule (script/lint-plugins/no-chai-assertions.mjs) rejects chai
  chains on `expect()`, and no-unused-expressions is on for the specs, so
  neither form of property-style assertion can come back.
- chai, chai-as-promised, dirty-chai and their @types leave
  spec/package.json; the refresh-page fixture served chai.js only as "a
  large script" and serves the jquery fixture instead.

* test: return promises instead of taking mocha's `done` callback

vitest has no `done` parameter. The tests and hooks that used one now
return a promise instead; where the callback was only ever called as
`done()` it is the promise's `resolve`, and where it was also handed to an
error-first API or called as `done(error)` it keeps mocha's meaning
(reject on a truthy argument, resolve otherwise) through a local `done`.

* test: move the spec runner's own semantics out of the mocha shim

Three things the shim did are not mocha compatibility but how the Electron
specs need to run, whichever API they are written against: every hook and
test starts from a fresh macrotask so that Chromium's message loop turns
between them, `defer()`-ed cleanup runs before the enclosing suites' own
afterEach hooks, and spec/disabled-tests.json can switch a test off by its
title. Those now live in a small custom vitest runner
(spec/vitest/runner.ts) that applies to every test, and the shim only
translates mocha's API.

* test: write the specs against vitest's API instead of mocha's

Mechanical conversion of every spec file and helper:

- `describe`/`it`/`beforeEach`/... are imported from 'vitest' rather than
  taken from the runner's globals; `before`/`after` become
  `beforeAll`/`afterAll`.
- `this.timeout(n)` becomes the `{ timeout: n }` option of the test or
  suite (a hook's second argument; hooks inside a suite that had a timeout
  get it explicitly, since vitest's suite option only reaches the tests), `this.retries(n)` `{ retry: n }`, and
  `this.skip()` the test context's `ctx.skip()`. The two `before` hooks that
  skipped a whole suite now compute the condition up front
  (`describe.runIf`) or per test. `function () {}` callbacks that only
  existed for `this` are arrows again.
- `ifit`/`ifdescribe` map to `it.skip`/`describe.skip`, mocha's chained
  `it(...).timeout(n)` and `this.bail()` are gone, and the two helpers that
  read `this.test` use the context's task state.

* test: remove the mocha compatibility layer

Nothing imports mocha's API any more, so spec/vitest/mocha-compat.ts and the
setup file that installed its globals go, together with @types/mocha and the
Mocha type overloads in spec/ambient.d.ts. Timeouts are vitest's own again
(30 s per test and hook, ELECTRON_SPEC_TIMEOUT raises it for the sanitizer
jobs, replacing MOCHA_TIMEOUT), the junit path variable becomes
ELECTRON_SPEC_JUNIT_FILE, and `closeAllWindows` loses the parameter that a
hook's context argument would otherwise land in.
2026-10-14 10:58:22 -04:00
Samuel Attard 826ffcf375 chore: remove dead code, unused chromium_src sources and orphaned files (#54229)
* build: remove unused and duplicated sources from chromium_src

- global_accelerator_listener{,_linux}.cc were compiled here and again by
  the //ui/base/accelerators/global_accelerator_listener dep; keep the dep
  (as a public_dep, shell/ includes its header) and drop the copies.
- native_window_tracker_aura duplicates //ui/native_window_tracker, which
  is already linked; //ui/views/native_window_tracker.h no longer exists.
- chrome's platform_util{,_aura,_mac} were added for the colour chooser
  in 2018 and nothing includes chrome/browser/platform_util.h any more.
- browser_theme_pack, custom_theme_supplier and window_frame_util.cc only
  reference each other; theme_properties and window_frame_util.h stay.
- back_to_tab_label_button, skip_ad_label_button, resize_handle_button
  and overlay_window_live_caption_button are never constructed on desktop
  or sit entirely inside our #if 0 hunks.
- scoped_picture_in_picture_occlusion_observation, predictors_features
  (+ its proto dep), exclusive_access_bubble_type.cc and the spellcheck
  language policy handlers have no callers in anything Electron builds.
- delegated_source_list_capturer is only used by thumbnail_capturer_mac,
  printing_service.cc only by pdf_to_emf_converter (Windows), and the
  hang_crash_dump.cc Linux stub is never called.
- web_request_resource_type.{cc,h} moved upstream in June; the
  enable_electron_extensions=false branch could not gn gen with it listed.

* refactor: remove unregistered browserAction/pageAction extension classes

browser_action.json is not part of the function registration target, so
the twelve BrowserAction*Function and seven PageAction*Function classes
were never instantiated, and the ExtensionActionAPI keyed service was
never built because nothing referenced its factory. Drop them together
with a few other extension leftovers that have no callers:
ElectronExtensionSystem::OnExtensionRegisteredWithRequestContexts,
ElectronExtensionsBrowserClient::SetAPIClientForTest and
ElectronExtensionsRendererClient::AllowPopup (its upstream virtual is
gone), and use extensions::util::AllowFileAccess instead of two local
copies of it.

* refactor: remove unreferenced C++ from shell/

None of these have a caller on any platform:

- OffScreenRenderWidgetHostView::AddGuestHostView/RemoveGuestHostView/
  RemoveViewProxy and guest_host_views_ (never populated, so every loop
  over it was dead too)
- FileSelectHelper::IsAcceptTypeValid/GetSanitizedFileName/
  SetFileSelectListenerForTesting
- FileSystemAccessPermissionContext::OriginHasReadAccess/WriteAccess
- ElectronBrowserClient::GetSiteInstanceFromAffinity
- ElectronPermissionManager::HasPermissionRequestHandler
- api::BaseWindow::SetGTKDarkThemeEnabled and the NativeWindow virtual
  behind it (NativeWindowViews keeps its own private helper)
- crash_reporter::IsCrashReporterEnabled/GetGlobalCrashKeys
- InspectableWebContents::GetDelegate
- NativeWindowObserver::WillCreatePopupWindow/WillNavigate
- WebRequest::RequestFilter's three-argument constructor
- declarations with no definition: ServiceWorkerContext::
  StopWorkersForScope, WebContents::OnElectronBrowserConnectionError/
  OnGetZoomLevel, NetworkContextService::CreateNetworkContextParams,
  WindowsToastNotification::ShowInternal, ElectronApiServiceImpl::
  ProcessPendingMessages
- NativeWindow::is_closed(), WebContentsZoomController::
  persist_zoom_mode(), BrowserProcessImpl::CreateGlobalFeaturesForTesting()
- the Linux stubs for asar::Archive::HeaderIntegrity/RelativePath and the
  non-mac Clipboard::ReadFindText stub (their callers are already
  platform-guarded, so guard the declarations instead)
- the 'render-view-deleted' event, which existed for the removed remote
  module's object registry

* refactor: remove unused gin_helper and converter code

- gin_helper::ErrorThrower::ThrowReferenceError/ThrowSyntaxError, the
  zero-argument PromiseBase::Reject() and PromiseBase::RejectPromise had
  no callers.
- CreateDataPropertyCallback and Dispatcher::DispatchToCallbackForProperty
  were never instantiated.
- Converter<blink::mojom::Referrer> (Electron uses content::Referrer) and
  Converter<blink::mojom::MenuItem::Type>::ToV8 had no users; drop the
  full popup_menu.mojom.h include from content_converter.h and only pull
  the ReferrerPolicy enum into blink_converter.

* refactor: remove native bindings and methods that JS never calls

- The electron_common_environment binding has had no caller since the
  forwarded WebContents events moved native (#53975).
- Nothing requests the electron_browser_printing binding;
  webContents.getPrintersAsync() calls GetPrinterListAsync() directly.
- features.isExtensionsEnabled(), the top-level executeInWorld/
  exposeAPIInWorld exports on the context_bridge binding,
  webFrameMain._fromIdIfExists/_fromFtnIdIfExists,
  WebContents._getPreloadScript, webFrame._isEvalAllowed and
  Menu._getUserAcceleratorAt are not used by lib/ or spec/.
- Protocol::IsProtocolHandled was installed on the prototype template and
  then unconditionally replaced by the JS implementation in
  lib/browser/api/protocol.ts, so the C++ copy could never run.

* chore: remove dead code and stale typings from lib/

- WebFrameMain.prototype._sendInternal, WebViewImpl.setupEventProperty/
  on/onAttach, the 'setLayoutZoomLevelLimits' webview method (removed in
  Electron 9) and WEB_VIEW_ATTRIBUTES.NAME had no callers.
- The Electron 20 sandbox-default deprecation log could never fire:
  getLastWebPreferences() never contains 'preload' and always contains
  'sandbox'.
- lib/browser/api/crash-reporter.ts only ships in the browser bundle, so
  its process.type checks were constant.
- WebSQL was removed in Electron 31; drop enableWebSQL from the inherited
  guest preferences and the docs, and the no-op 'webSQL' clearData type.
- Drop typings for things that no longer exist (ProcessType, ReplyChannel/
  _replyChannel, the WebContents '-ipc-*' and '-window-visibility-change'
  overloads, ElectronInternal.LoadURLOptions, Chrome.Tabs, NodeJS.Global
  augmentations, Process.log/mainModule/_serviceStartupScript, the
  document-start/end overloads, NetBinding.Net/net, App: Function,
  Window.ELECTRON_*_SECURITY_WARNINGS, _awaitNextLoad) and the .oxlintrc
  globs for lib/preload_realm and lib/sandboxed_renderer.

* chore: remove orphaned fixtures, scripts and CI leftovers

- spec/fixtures/release-notes/ (its spec left with the release scripts in
  #52100), devtools-extensions/bad-manifest/, assets/osr.png,
  pages/storage/web_sql.html, pages/history.html, module/hello-child.js,
  esm/local.mjs and api/local-ai-handler/no-language-model.js are not
  referenced by any spec.
- script/verify-chromedriver.py and script/run-if-exists.js lost their
  last callers with the AppVeyor removal; script/lib/npm.py,
  script/lib/native_tests.py, build/args/native_tests.gn, the pyyaml DEPS
  entry and shell/browser/ui/{accelerator_util_unittests,run_all_unittests}.cc
  are leftovers of the native test target removed in #40190.
- script/lib/git.py is_repo_root/get_repo_root and script/lib/utils.js
  getCurrentBranch/handleGitCall have no callers.
- pipeline-electron-build-and-test-and-nan.yml is not referenced by any
  workflow since #49072.
- Three spec/disabled-tests.json titles, one node-disabled-tests entry, a
  wayland allowlist entry, a sysroots.json arch and two CODEOWNERS paths
  named things that no longer exist; four docs/images files are unlinked.
2026-09-24 00:12:45 +02:00
Samuel AttardandClaude 9baaed782e test: load specs as native ES modules (#54171)
* test: mechanically rewrite spec imports and __dirname for native ESM

This is the automated half of loading spec/ as native ES modules and is
not expected to pass on its own; the hand-written half follows in the
next commit. A script (included in the pull request description) made
three changes to spec/*.ts and spec/lib/*.ts, then oxfmt was re-run:

* relative import specifiers gain their real file extension
* __dirname / __filename become import.meta.dirname / .filename
* files that still call require() from module code get a
  createRequire(import.meta.url) prelude

Code inside functions that are stringified and evaluated in another
process (remotely(), itremote(), template-interpolated functions and
similar) is left as it was.

* test: load specs as native ES modules with Node's type stripping

spec/package.json declares "type": "module", so the spec runner and every
spec file now load through Node's own ESM loader and built-in TypeScript
type stripping; the require.extensions transpile hook is gone. A new
spec/fixtures/package.json keeps fixtures in a CommonJS scope.

The hand-written changes on top of the mechanical rewrite:

* spec/index.js becomes an ES module and import()s its test dependencies
  after the app is ready, then uses mocha.loadFilesAsync().
* Functions that are stringified and sent to another process now arrive
  as written rather than as CommonJS transpiler output, so the
  remote-control and utility-process fixtures define the plain names
  those bodies use (chai, expect, once, setTimeout, ...) instead of the
  old `electron_1` style shims, and a few bodies require() what they use.
* The two specs that exercise lib/ sources require() them, since lib/ is
  not in a "type": "module" scope.
* spec-helpers resolves BrowserWindow lazily so it stays loadable from a
  utility process.
* tsconfig.spec.json allows .ts extension imports; spec/tsconfig.json
  points editors at it.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-09-21 15:21:31 -07:00
Samuel AttardandClaude 2774d5761f test: keep spec sources to erasable syntax and explicit type imports (#54170)
Turn on erasableSyntaxOnly and isolatedModules for the spec typecheck so
the specs only use TypeScript syntax that a plain type stripper can
remove (no enums, parameter properties, import = require or angle
bracket casts), and lint spec/ for type-only imports so they are
written as `import type` / inline `type` specifiers.

Also hoist a handful of inline require() calls in spec bodies up to the
existing top-level imports and use named imports from ws.

Co-authored-by: Claude <noreply@anthropic.com>
2026-09-21 15:21:31 -07:00
David Sanders 101bd86383 build: cleanup eslint/oxlint disable comments (#53925) 2026-09-14 23:20:22 -07:00
David Sanders e82d859a90 test: enforce awaiting promise returning helpers (#53036)
Assisted-by: Claude Opus 5
2026-08-20 10:27:21 -04:00
David Sanders 74f9fcf907 test: don't import from ../lib in API tests (#51905) 2026-06-08 12:15:50 -04:00
Samuel Attard b034f494b0 build: generate Electron-specific PGO profiles in CI (#51812)
Adds a workflow_dispatch pipeline that builds PGO-instrumented Electron
for every published platform/arch combo, collects optimization profiles
by running benchmark and app-style workloads against each build, and
publishes merged profiles to https://dev-cdn.electronjs.org/pgo/.

Why: Electron release builds apply Chrome's published PGO profile, which
matches functions by symbol name and control-flow hash. Every function
that differs from Chrome - all of Node.js, patched Chromium code, V8
built with Node's flags, and the shell/ code - silently gets no
optimization guidance and is laid out as cold. The same applies to V8's
builtins profile: Chrome's published profiles reject Electron's
promise/async builtins because the Node integration changes their
codegen. Measured on Linux x64, an Electron-collected profile is worth
+9.5% on Speedometer 3.1 and +16% geomean across 22 app-operation
benchmarks (contextBridge calls +44-51%) versus Chrome's profile.

The pipeline:

- Instrumented builds (chrome_pgo_phase = 1) for linux x64/arm/arm64,
  win x64/x86/arm64, and macos x64/arm64, each collecting on hosts that
  can run its own binaries.
- Collection workloads: Speedometer 3 (x3), JetStream 2, and MotionMark
  (the same set Chromium's PGO recipe uses), plus Electron-specific
  phases browser benchmarks cannot cover: main-process Node.js
  (Buffer/crypto/fs/JSON), contextBridge and ipcRenderer.invoke
  marshaling, and renderer/Node networking over HTTPS + HTTP/2 with real
  certificate verification via an ephemeral CA installed in the
  collection host's trust store.
- A V8 builtins profile generated from an instrumented d8 built with
  Electron's exact V8 configuration; one x64 profile covers x64 and
  arm64 (builtin block graphs are arch-independent, mirroring upstream).
- Per-platform profraw sets merged with in-tree llvm-profdata tooling,
  then uploaded via OIDC through the pgo-upload GitHub environment
  (manual approval, no long-lived credentials).

Build-system changes:

- build/args/pgo-instrument.gn and pgo-builtins-instrument.gn
  instrumented build configurations (symbol_level = 0, no dSYMs -
  instrumented binaries are never shipped and do not fit on macOS
  runners with debug info).
- A Chromium patch fixing profile-runtime resolution for instrumented
  builds on Linux arm32 (clang package ships the armv7 triple; the
  driver resolves the arm triple) and Windows cross-compiles (objects
  embed the per-target runtime lib name; the Windows package ships the
  legacy layout). Chrome never builds these configurations; the patch is
  upstreamable.
- generate-chromedriver and generate-test-artifacts flags on the build
  segment and build-electron action (default true, no behavior change
  for existing callers) so instrumented builds skip artifacts only
  consumed by test jobs.

Release builds do not consume these profiles yet; that is a follow-up
change.
2026-06-01 12:34:04 -07:00
Samuel Attard 6ed3198ba8 build: migrate from eslint to oxlint (#50691)
Consolidates the root .eslintrc.json and five nested configs (build,
script, docs, default_app, spec) into a single .oxlintrc.json at the
repo root. script/lint.js now shells out to the oxlint binary from
node_modules/.bin instead of using the ESLint Node API, and emits
GitHub Actions annotations directly via --format=github in CI
(replacing the deleted eslint-stylish problem matcher).

Oxlint has no markdown processor, so the ESLint-based lint of JS code
blocks in docs/**/*.md is replaced with an inline regex check for bare
Node.js builtin imports. This preserves the rule docs/.eslintrc.json
was originally added for in #42113; the rest of the standard ruleset on
docs code blocks was already being enforced in parallel by
lint-roller-markdown-standard.
2026-04-06 09:05:13 -07:00