| // Copyright 2026 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| /* eslint @devtools/enforce-test-universe-return-types: "error" */ |
| |
| import * as Common from '../core/common/common.js'; |
| import type * as Host from '../core/host/host.js'; |
| import * as Root from '../core/root/root.js'; |
| import * as SDK from '../core/sdk/sdk.js'; |
| import type * as Foundation from '../foundation/foundation.js'; |
| import * as Bindings from '../models/bindings/bindings.js'; |
| import * as Workspace from '../models/workspace/workspace.js'; |
| |
| import {DEFAULT_SETTING_REGISTRATIONS_FOR_TEST} from './SettingsHelpers.js'; |
| import {createTarget} from './TargetHelpers.js'; |
| |
| export interface CreationOptions extends Partial<Foundation.Universe.CreationOptions> { |
| pageResourceLoaderOptions?: { |
| loadOverride: ((arg0: string) => Promise<{ |
| success: boolean, |
| content: string|Uint8Array<ArrayBuffer>, |
| errorDescription: Host.ResourceLoader.LoadErrorDescription, |
| }>)|null, |
| maxConcurrentLoads?: number, |
| }; |
| } |
| |
| /** |
| * Similar to a `Foundation.Universe` but creates instances lazily as required. |
| * |
| * IMPORTANT: Do not add any `.instance()` singleton access here. Only add classes |
| * that take all their dependencies via constructor (including Settings)! |
| * |
| * Registered settings need to be passed via constructor. By default `TestUniverse` |
| * uses DEFAULT_SETTING_REGISTRATIONS_FOR_TEST, but it will not read the global |
| * registered settings (on purpose). |
| */ |
| export class TestUniverse { |
| readonly #context = new Root.DevToolsContext.WritableDevToolsContext(); |
| readonly #creationOptions?: CreationOptions; |
| |
| constructor(options?: CreationOptions) { |
| this.#creationOptions = options; |
| } |
| |
| /** |
| * Convenience shortcut for `createTarget({targetManager: testUniverse.targetManager})` |
| */ |
| createTarget(options: Parameters<typeof createTarget>[0]): SDK.Target.Target { |
| return createTarget({...options, targetManager: this.targetManager}); |
| } |
| |
| get console(): Common.Console.Console { |
| if (!this.#context.has(Common.Console.Console)) { |
| this.#context.set(Common.Console.Console, new Common.Console.Console()); |
| } |
| return this.#context.get(Common.Console.Console); |
| } |
| |
| get cssWorkspaceBinding(): Bindings.CSSWorkspaceBinding.CSSWorkspaceBinding { |
| if (!this.#context.has(Bindings.CSSWorkspaceBinding.CSSWorkspaceBinding)) { |
| this.#context.set( |
| Bindings.CSSWorkspaceBinding.CSSWorkspaceBinding, |
| new Bindings.CSSWorkspaceBinding.CSSWorkspaceBinding(this.#resourceMapping, this.targetManager)); |
| } |
| return this.#context.get(Bindings.CSSWorkspaceBinding.CSSWorkspaceBinding); |
| } |
| |
| get debuggerWorkspaceBinding(): Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding { |
| if (!this.#context.has(Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding)) { |
| this.#context.set( |
| Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding, |
| new Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding( |
| this.#resourceMapping, this.targetManager, this.ignoreListManager, this.workspace)); |
| } |
| return this.#context.get(Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding); |
| } |
| |
| get frameManager(): SDK.FrameManager.FrameManager { |
| if (!this.#context.has(SDK.FrameManager.FrameManager)) { |
| this.#context.set(SDK.FrameManager.FrameManager, new SDK.FrameManager.FrameManager(this.targetManager)); |
| } |
| return this.#context.get(SDK.FrameManager.FrameManager); |
| } |
| |
| get ignoreListManager(): Workspace.IgnoreListManager.IgnoreListManager { |
| if (!this.#context.has(Workspace.IgnoreListManager.IgnoreListManager)) { |
| this.#context.set( |
| Workspace.IgnoreListManager.IgnoreListManager, |
| new Workspace.IgnoreListManager.IgnoreListManager(this.settings, this.targetManager)); |
| } |
| return this.#context.get(Workspace.IgnoreListManager.IgnoreListManager); |
| } |
| |
| get multitargetNetworkManager(): SDK.NetworkManager.MultitargetNetworkManager { |
| if (!this.#context.has(SDK.NetworkManager.MultitargetNetworkManager)) { |
| const multitargetNetworkManager = new SDK.NetworkManager.MultitargetNetworkManager(this.targetManager); |
| this.#context.set(SDK.NetworkManager.MultitargetNetworkManager, multitargetNetworkManager); |
| } |
| return this.#context.get(SDK.NetworkManager.MultitargetNetworkManager); |
| } |
| |
| get pageResourceLoader(): SDK.PageResourceLoader.PageResourceLoader { |
| if (!this.#context.has(SDK.PageResourceLoader.PageResourceLoader)) { |
| const options = this.#creationOptions?.pageResourceLoaderOptions ?? { |
| loadOverride: null, |
| }; |
| const pageResourceLoader = new SDK.PageResourceLoader.PageResourceLoader( |
| this.targetManager, this.settings, this.multitargetNetworkManager, options.loadOverride, |
| options.maxConcurrentLoads); |
| this.#context.set(SDK.PageResourceLoader.PageResourceLoader, pageResourceLoader); |
| } |
| return this.#context.get(SDK.PageResourceLoader.PageResourceLoader); |
| } |
| |
| get targetManager(): SDK.TargetManager.TargetManager { |
| if (!this.#context.has(SDK.TargetManager.TargetManager)) { |
| // `SDKModel` instances pull their dependencies from the context we pass here. |
| // Instead of eagerly creating them in `createTarget`, we pass a simple stub that |
| // re-directs to the TestUniverse for lazy initialization. This also makes it explicit |
| // what dependencies `SDKModel` instances are using and also safe-guards against |
| // `createTarget({targetManager: universe.targetManager}) instantiations. |
| const universe = this; |
| const context = new (class LazyContext implements Root.DevToolsContext.DevToolsContext { |
| // eslint-disable-next-line @devtools/enforce-test-universe-return-types |
| get<T>(ctor: Root.DevToolsContext.ConstructorT<T>): T { |
| if (ctor === Common.Settings.Settings.prototype.constructor) { |
| return universe.settings as T; |
| } |
| if (ctor === SDK.FrameManager.FrameManager.prototype.constructor) { |
| return universe.frameManager as T; |
| } |
| throw new Error(`Class ${ |
| ctor.name} not set-up as a dependency for SDKModels in TestUniverse.ts. Add it to LazyContext#get in TestUniverse.ts`); |
| } |
| })(); |
| const targetManager = |
| new SDK.TargetManager.TargetManager(context, this.#creationOptions?.overrideAutoStartModels ?? new Set()); |
| this.#context.set(SDK.TargetManager.TargetManager, targetManager); |
| } |
| return this.#context.get(SDK.TargetManager.TargetManager); |
| } |
| |
| get settings(): Common.Settings.Settings { |
| if (!this.#context.has(Common.Settings.Settings)) { |
| const storage = new Common.Settings.SettingsStorage({}, undefined, 'test'); |
| const options = this.#creationOptions?.settingsCreationOptions ?? { |
| syncedStorage: storage, |
| globalStorage: storage, |
| localStorage: storage, |
| settingRegistrations: DEFAULT_SETTING_REGISTRATIONS_FOR_TEST, |
| }; |
| const settings = new Common.Settings.Settings(options); |
| this.#context.set(Common.Settings.Settings, settings); |
| } |
| return this.#context.get(Common.Settings.Settings); |
| } |
| |
| get workspace(): Workspace.Workspace.WorkspaceImpl { |
| if (!this.#context.has(Workspace.Workspace.WorkspaceImpl)) { |
| this.#context.set(Workspace.Workspace.WorkspaceImpl, new Workspace.Workspace.WorkspaceImpl()); |
| } |
| return this.#context.get(Workspace.Workspace.WorkspaceImpl); |
| } |
| |
| get #resourceMapping(): Bindings.ResourceMapping.ResourceMapping { |
| if (!this.#context.has(Bindings.ResourceMapping.ResourceMapping)) { |
| this.#context.set( |
| Bindings.ResourceMapping.ResourceMapping, |
| new Bindings.ResourceMapping.ResourceMapping(this.targetManager, this.workspace)); |
| } |
| return this.#context.get(Bindings.ResourceMapping.ResourceMapping); |
| } |
| } |