Skip to content

perf(electron-service): batch updateAllMocks into a single CDP round-trip #268

@goosewobbler

Description

@goosewobbler

Context

Raised by Greptile as a P1 on PR #264:

installCommandOverrides() is called at line 94 whenever hasElectronApi is true, which is the normal native-mode path. The installed override calls updateAllMocks() after every click, doubleClick, setValue, and clearValue. In native mode updateAllMocks() calls each mock's update(), which issues a browser.electron.execute() CDP call per registered mock. This silently adds latency proportional to mock count for all native-mode users.

The behaviour predates PR #264 (the override was already there) — the refactor surfaced it but did not introduce it. An attempt to scope the override to browser mode (commit 43b767f4) broke a large number of E2E suites that rely on element-command auto-sync, and was reverted in 8e84e8f7. The override stays; the underlying cost still needs to be addressed.

Hot path

packages/electron-service/src/service.ts:474-508updateAllMocks() calls Promise.all(mocks.map(m => m.update())). With N registered mocks, every element command (click/doubleClick/setValue/clearValue) costs N CDP round-trips.

What each update() does

Each one is a read-only call returning JSON.parse(JSON.stringify(inner.mock.calls)). Three flavours:

  • packages/electron-service/src/mock.ts:117 — api mock: (electron, apiName, funcName) => electron[apiName][funcName].mock.calls
  • packages/electron-service/src/classMock.ts:55 — class method on a prototype
  • packages/electron-service/src/classMock.ts:139, 194 — class constructor mock
  • packages/electron-service/src/mock.ts:221 — browser-mode mock reading window.__wdio_mocks__[channel].mock.calls

Proposed refactor

  1. Expose the existing MockAccessor (or an equivalent small descriptor) on each outer mock, e.g. mock.__accessor.
  2. Add an __applyCalls(calls: unknown[][]) method on each outer mock that runs the existing diff-and-apply logic against the outer mock without any CDP call.
  3. Rewrite updateAllMocks():
    • Collect [mockId, accessor] pairs from mockStore.getMocks().
    • Issue one browser.electron.execute() that takes the array, walks electron[...] for each accessor, and returns { [mockId]: calls }.
    • Distribute: mocks.forEach(([id, mock]) => mock.__applyCalls(result[id])).
  4. Apply the same shape to browser mode (single browser.execute() that walks window.__wdio_mocks__[...]).
  5. Leave per-mock await mock.update() untouched — when called explicitly by user code, the one-round-trip cost is an explicit choice and rare.

Sibling work

Tauri service (packages/tauri-service) has the same architecture and the same per-mock-update cost. File or fold a matching task once the Electron pattern lands.

Acceptance

  • updateAllMocks() performs at most one CDP / WDIO round-trip regardless of mock count.
  • Existing unit tests under packages/electron-service/test/ still pass, including the two native-mode override tests ("should install element command overrides with overwriteCommand" and "should update mocks after overridden element command executes").
  • E2E test "should trigger mock updates when DOM interactions occur" (e2e/test/electron/mocking.spec.ts) still passes.
  • New unit test verifying that updateAllMocks() issues exactly one browser.electron.execute call when ≥2 mocks are registered.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions