mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
fix(electron): separate script frame admission from declared source labels
This commit is contained in:
1 parent
aad3d88cc0
commit
df00588325
8 files changed
+272
-41
No files matched your search
@@ -76,8 +76,23 @@ completeness diagnostics, so `total_nodes` can exceed the retained node count
|
||||
and a parent index can refer to an omitted source node.
|
||||
|
||||
Script metadata retains its execution-context frame ID when CDP supplies one and
|
||||
the observed frame generation is authorized. Frames describe the current inventory;
|
||||
script observations can retain earlier authorized frames that have since detached.
|
||||
records admission separately in `frame_scope`. An `authorized` scope refers to
|
||||
the observed document generation; `unknown` retains the known frame ID without
|
||||
claiming authorization and makes script coverage attach-limited. Historical
|
||||
captures may omit this field, which leaves that admission fact unknown.
|
||||
Scripts from document generations whose frames were explicitly excluded by
|
||||
local-file policy are omitted before script-source reads. Frame identity can
|
||||
come from the script event's own auxiliary data or its observed execution
|
||||
context; contradictory identities are not aligned.
|
||||
Frames describe the current inventory; script observations can retain earlier
|
||||
authorized frames that have since detached.
|
||||
|
||||
`has_source_url` preserves CDP's sourceURL declaration flag as true, false, or
|
||||
unknown (`null`, or omitted in historical captures). A script's canonical
|
||||
`file_path` is the admitted local-file URL or supplied sourceURL label. It does
|
||||
not verify that the runtime bytes came from that disk file; captured source
|
||||
bytes have their own digest. `reported_url` retains the original URL or label
|
||||
before canonical path admission. Labels remain distinct from filesystem provenance.
|
||||
The capture also
|
||||
inventories worker, service-worker, and shared-worker targets with
|
||||
validated opener-target and parent-frame IDs. Worker discovery uses passive
|
||||
|
||||
@@ -3,9 +3,56 @@ import { describe, expect, it } from "vitest";
|
||||
import { inspectWebPageInputSchema } from "../domain/browserObservation.js";
|
||||
import { CdpCaptureCompleteness } from "./CdpCaptureCompleteness.js";
|
||||
import { CdpCaptureEvents } from "./CdpCaptureEvents.js";
|
||||
import { ingestElectronScriptEvent } from "./CdpElectronScriptEvents.js";
|
||||
import {
|
||||
ingestElectronScriptEvent,
|
||||
type ElectronScriptDraft,
|
||||
} from "./CdpElectronScriptEvents.js";
|
||||
|
||||
describe("CDP execution-context retention", () => {
|
||||
it.each([
|
||||
{
|
||||
frameId: "frame-other",
|
||||
hasSourceURL: true,
|
||||
expectedFrame: null,
|
||||
expectedLabel: true,
|
||||
},
|
||||
{
|
||||
frameId: "frame-main",
|
||||
hasSourceURL: "invalid",
|
||||
expectedFrame: "frame-main",
|
||||
expectedLabel: null,
|
||||
},
|
||||
])(
|
||||
"keeps contradictory Electron ownership and invalid source labels uncertain: $frameId/$hasSourceURL",
|
||||
({ frameId, hasSourceURL, expectedFrame, expectedLabel }) => {
|
||||
const scripts: ElectronScriptDraft[] = [];
|
||||
const completeness = new CdpCaptureCompleteness();
|
||||
ingestElectronScriptEvent({
|
||||
event: {
|
||||
method: "Debugger.scriptParsed",
|
||||
params: {
|
||||
scriptId: "script",
|
||||
url: "file:///fixture/app.js",
|
||||
executionContextId: 7,
|
||||
executionContextAuxData: { frameId },
|
||||
hasSourceURL,
|
||||
},
|
||||
},
|
||||
scripts,
|
||||
executionContextFrames: new Map([["7", "frame-main"]]),
|
||||
completeness,
|
||||
});
|
||||
expect(scripts[0]).toMatchObject({
|
||||
frameId: expectedFrame,
|
||||
hasSourceUrl: expectedLabel,
|
||||
});
|
||||
expect(completeness.snapshot().excluded).toContainEqual({
|
||||
section: "scripts",
|
||||
reason: "invalid_protocol_value",
|
||||
count: 1,
|
||||
});
|
||||
},
|
||||
);
|
||||
it("removes stale frame attribution when execution contexts are destroyed", () => {
|
||||
const request = inspectWebPageInputSchema.parse({
|
||||
cdp_endpoint: "http://127.0.0.1:9222",
|
||||
|
||||
@@ -209,7 +209,11 @@ it.each([
|
||||
expect(fired).toBe(true);
|
||||
if (stage !== "final")
|
||||
expect(result.scripts.items).toContainEqual(
|
||||
expect.objectContaining({ frame_id: null, cdp_hash: "new-document" }),
|
||||
expect.objectContaining({
|
||||
frame_id: "child",
|
||||
frame_scope: "unknown",
|
||||
cdp_hash: "new-document",
|
||||
}),
|
||||
);
|
||||
expect(result.completeness.attach_limited_sections).toContain("frames");
|
||||
}
|
||||
@@ -369,7 +373,8 @@ it.each([
|
||||
expect(captured.scripts.items).toContainEqual(
|
||||
expect.objectContaining({
|
||||
cdp_hash: "observed",
|
||||
frame_id: kind === "duplicate-child" ? "child" : null,
|
||||
frame_id: "child",
|
||||
frame_scope: kind === "duplicate-child" ? "authorized" : "unknown",
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -518,11 +523,11 @@ describe("Electron frame capture", () => {
|
||||
include_script_sources: false,
|
||||
}),
|
||||
scripts,
|
||||
frameForScript: (script) =>
|
||||
frameScopeForScript: (script) =>
|
||||
script.frameId !== null &&
|
||||
new Set(["frame-old", "frame-new"]).has(script.frameId)
|
||||
? script.frameId
|
||||
: null,
|
||||
? { state: "authorized", frameId: script.frameId }
|
||||
: { state: "unknown", frameId: script.frameId },
|
||||
completeness,
|
||||
asarHeaderAdmission: DEFAULT_ASAR_HEADER_ADMISSION,
|
||||
});
|
||||
@@ -543,7 +548,7 @@ describe("Electron frame capture", () => {
|
||||
),
|
||||
).toEqual({
|
||||
unknown: null,
|
||||
outside: null,
|
||||
outside: "frame-outside",
|
||||
});
|
||||
expect(completeness.snapshot().excluded).toContainEqual({
|
||||
section: "scripts",
|
||||
@@ -579,6 +584,7 @@ it("admits a prefix and fitting suffix around an oversized Electron source", asy
|
||||
isModule: false,
|
||||
language: "JavaScript",
|
||||
frameId: null,
|
||||
hasSourceUrl: null,
|
||||
};
|
||||
});
|
||||
const completeness = new CdpCaptureCompleteness();
|
||||
@@ -602,7 +608,7 @@ it("admits a prefix and fitting suffix around an oversized Electron source", asy
|
||||
include_script_sources: true,
|
||||
}),
|
||||
scripts,
|
||||
frameForScript: () => null,
|
||||
frameScopeForScript: () => ({ state: "unknown", frameId: null }),
|
||||
completeness,
|
||||
asarHeaderAdmission: DEFAULT_ASAR_HEADER_ADMISSION,
|
||||
});
|
||||
@@ -788,7 +794,8 @@ it.each([
|
||||
expect(captured.scripts.items).toContainEqual(
|
||||
expect.objectContaining({
|
||||
cdp_hash: `after-${id}`,
|
||||
frame_id: changed ? null : id,
|
||||
frame_id: id,
|
||||
frame_scope: changed ? "unknown" : "authorized",
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ interface ElectronInspectionState {
|
||||
readonly frameParents: Map<string, string | null>;
|
||||
readonly frameDocuments: Map<string, ElectronFrameDocument>;
|
||||
readonly authorizedFrameGenerations: Set<string>;
|
||||
readonly excludedFrameGenerations: Set<string>;
|
||||
readonly scriptGenerations: WeakMap<ElectronScriptDraft, number>;
|
||||
readonly limitations: string[];
|
||||
mainFrameId: string | undefined;
|
||||
@@ -68,6 +69,7 @@ interface ElectronInspectionState {
|
||||
interface ElectronFrameInventory {
|
||||
readonly frames: ElectronPageInspection["frames"];
|
||||
readonly parents: ReadonlyMap<string, string | null>;
|
||||
readonly excludedFrameIds: ReadonlySet<string>;
|
||||
}
|
||||
|
||||
interface ElectronFrameRead extends ElectronFrameInventory {
|
||||
@@ -92,12 +94,15 @@ export const inspectCdpElectronPage = async (
|
||||
frameParents: new Map(),
|
||||
frameDocuments: new Map(),
|
||||
authorizedFrameGenerations: new Set(),
|
||||
excludedFrameGenerations: new Set(),
|
||||
scriptGenerations: new WeakMap(),
|
||||
limitations: [
|
||||
...(context.discovery.limitations ?? []),
|
||||
"Local file paths exposed by the selected CDP target are retained in the observation.",
|
||||
"REA does not evaluate renderer JavaScript, invoke Electron APIs, navigate, click, or close the page.",
|
||||
"Select include_script_sources to retain script contents.",
|
||||
"Script file_path is the admitted local-file URL or sourceURL label, not a verification that disk bytes supplied the runtime code. has_source_url retains CDP's sourceURL declaration flag; null or an omitted historical field is unknown.",
|
||||
"Known script frame identity is retained independently from frame_scope authorization. Unknown frame scope is attach-limited; scripts from known excluded document generations are omitted before source reads.",
|
||||
...(context.target.attached === null
|
||||
? [
|
||||
"CDP discovery omitted the target attachment state; attached is reported as unknown.",
|
||||
@@ -234,6 +239,11 @@ const authorizeFrameGenerations = (
|
||||
compositeKey([frame.frame_id, generation]),
|
||||
);
|
||||
}
|
||||
for (const id of read.excludedFrameIds)
|
||||
if (!unproven.has(id))
|
||||
state.excludedFrameGenerations.add(
|
||||
compositeKey([id, read.generations.get(id) ?? 0]),
|
||||
);
|
||||
};
|
||||
|
||||
/** A changed ancestor also invalidates descendants first discovered in this tree. */
|
||||
@@ -512,17 +522,16 @@ const captureElectronContent = async (
|
||||
...(context.signal === undefined ? {} : { signal: context.signal }),
|
||||
request: context.input,
|
||||
scripts: state.scripts,
|
||||
frameForScript: (script) => {
|
||||
frameScopeForScript: (script) => {
|
||||
const generation = state.scriptGenerations.get(script);
|
||||
const authorized =
|
||||
script.frameId !== null &&
|
||||
generation !== undefined &&
|
||||
state.authorizedFrameGenerations.has(
|
||||
compositeKey([script.frameId, generation]),
|
||||
);
|
||||
if (script.frameId !== null && !authorized)
|
||||
state.completeness.attachLimited("scripts");
|
||||
return authorized ? script.frameId : null;
|
||||
if (script.frameId !== null && generation !== undefined) {
|
||||
const key = compositeKey([script.frameId, generation]);
|
||||
if (state.excludedFrameGenerations.has(key))
|
||||
return { state: "excluded" };
|
||||
if (state.authorizedFrameGenerations.has(key))
|
||||
return { state: "authorized", frameId: script.frameId };
|
||||
}
|
||||
return { state: "unknown", frameId: script.frameId };
|
||||
},
|
||||
completeness: state.completeness,
|
||||
asarHeaderAdmission: context.asarHeaderAdmission,
|
||||
@@ -606,6 +615,7 @@ export const captureFrames = async (
|
||||
const frames: ElectronPageInspection["frames"] = [];
|
||||
const enclosingParents = new Map<UnknownRecord, string>();
|
||||
const parents = new Map<string, string | null>();
|
||||
const excludedFrameIds = new Set<string>();
|
||||
for (const tree of walkFrameTrees(frameTree, (count) =>
|
||||
completeness.exclude("frames", "invalid_protocol_value", count),
|
||||
)) {
|
||||
@@ -643,6 +653,7 @@ export const captureFrames = async (
|
||||
);
|
||||
if (path === undefined) {
|
||||
completeness.exclude("frames", "out_of_target_scope");
|
||||
excludedFrameIds.add(frameId);
|
||||
continue;
|
||||
}
|
||||
frames.push({
|
||||
@@ -651,7 +662,7 @@ export const captureFrames = async (
|
||||
file_path: path,
|
||||
});
|
||||
}
|
||||
return { frames, parents };
|
||||
return { frames, parents, excludedFrameIds };
|
||||
};
|
||||
|
||||
const captureResources = async (
|
||||
|
||||
@@ -15,8 +15,15 @@ export interface ElectronScriptDraft {
|
||||
readonly isModule: boolean | null;
|
||||
readonly language: string | null;
|
||||
readonly frameId: string | null;
|
||||
readonly hasSourceUrl: boolean | null;
|
||||
}
|
||||
|
||||
/** Frame identity is retained separately from the document generation's admission. */
|
||||
export type ElectronScriptFrameScope =
|
||||
| { readonly state: "authorized"; readonly frameId: string }
|
||||
| { readonly state: "excluded" }
|
||||
| { readonly state: "unknown"; readonly frameId: string | null };
|
||||
|
||||
/** Retain script/frame metadata from passive Electron events. */
|
||||
export const ingestElectronScriptEvent = (input: {
|
||||
readonly event: CdpEvent;
|
||||
@@ -51,14 +58,34 @@ export const ingestElectronScriptEvent = (input: {
|
||||
return;
|
||||
}
|
||||
const contextKey = executionContextKey(value?.executionContextId);
|
||||
const contextFrame =
|
||||
contextKey === null
|
||||
? undefined
|
||||
: input.executionContextFrames.get(contextKey);
|
||||
const auxiliary = recordValue(value?.executionContextAuxData);
|
||||
const reportedFrame = cdpStringValue(auxiliary?.frameId);
|
||||
const declaredFrame = reportedFrame === "" ? undefined : reportedFrame;
|
||||
const conflictingFrame =
|
||||
declaredFrame !== undefined &&
|
||||
contextFrame !== undefined &&
|
||||
declaredFrame !== contextFrame;
|
||||
if (
|
||||
conflictingFrame ||
|
||||
(value?.executionContextAuxData !== undefined && auxiliary === undefined) ||
|
||||
(auxiliary !== undefined &&
|
||||
Object.hasOwn(auxiliary, "frameId") &&
|
||||
declaredFrame === undefined) ||
|
||||
(value?.hasSourceURL !== undefined &&
|
||||
typeof value.hasSourceURL !== "boolean")
|
||||
)
|
||||
input.completeness.exclude("scripts", "invalid_protocol_value");
|
||||
input.scripts.push({
|
||||
scriptId,
|
||||
rawUrl,
|
||||
...scriptMetadataValues(value),
|
||||
frameId:
|
||||
contextKey === null
|
||||
? null
|
||||
: (input.executionContextFrames.get(contextKey) ?? null),
|
||||
frameId: conflictingFrame ? null : (declaredFrame ?? contextFrame ?? null),
|
||||
hasSourceUrl:
|
||||
typeof value?.hasSourceURL === "boolean" ? value.hasSourceURL : null,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -70,7 +97,7 @@ const retainExecutionContext = (
|
||||
const runtimeContext = recordValue(parameters?.context);
|
||||
const contextKey = executionContextKey(runtimeContext?.id);
|
||||
const frameId = cdpStringValue(recordValue(runtimeContext?.auxData)?.frameId);
|
||||
if (contextKey === null || frameId === undefined) return;
|
||||
if (contextKey === null || frameId === undefined || frameId === "") return;
|
||||
frames.set(contextKey, frameId);
|
||||
};
|
||||
|
||||
|
||||
@@ -8,7 +8,10 @@ import { createWebTextArtifact } from "../domain/webContentArtifact.js";
|
||||
import type { CdpConnection } from "./CdpConnection.js";
|
||||
import { CdpCaptureCompleteness } from "./CdpCaptureCompleteness.js";
|
||||
import { requiredScriptSource } from "./CdpCaptureValues.js";
|
||||
import type { ElectronScriptDraft } from "./CdpElectronScriptEvents.js";
|
||||
import type {
|
||||
ElectronScriptDraft,
|
||||
ElectronScriptFrameScope,
|
||||
} from "./CdpElectronScriptEvents.js";
|
||||
import { authorizedElectronFile } from "./ElectronFileScope.js";
|
||||
import { compareUnicodeCodePoints } from "../domain/unicodeCodePointOrder.js";
|
||||
import { AnalysisCancelledError } from "../domain/analysisErrorCore.js";
|
||||
@@ -25,7 +28,9 @@ interface ElectronScriptCaptureInput {
|
||||
readonly signal?: AbortSignal;
|
||||
readonly request: InspectElectronPageInput;
|
||||
readonly scripts: readonly ElectronScriptDraft[];
|
||||
readonly frameForScript: (script: ElectronScriptDraft) => string | null;
|
||||
readonly frameScopeForScript: (
|
||||
script: ElectronScriptDraft,
|
||||
) => ElectronScriptFrameScope;
|
||||
readonly completeness: CdpCaptureCompleteness;
|
||||
readonly asarHeaderAdmission: AsarHeaderAdmission;
|
||||
}
|
||||
@@ -49,6 +54,11 @@ export const captureElectronScripts = async (
|
||||
try {
|
||||
for (const script of input.scripts) {
|
||||
throwIfCallerCancelled(input.signal);
|
||||
const frame = input.frameScopeForScript(script);
|
||||
if (frame.state === "excluded") {
|
||||
input.completeness.exclude("scripts", "out_of_target_scope");
|
||||
continue;
|
||||
}
|
||||
const path = await authorizedElectronFile(
|
||||
script.rawUrl,
|
||||
input.signal,
|
||||
@@ -59,9 +69,14 @@ export const captureElectronScripts = async (
|
||||
input.completeness.exclude("scripts", "out_of_target_scope");
|
||||
continue;
|
||||
}
|
||||
if (frame.state === "unknown")
|
||||
input.completeness.attachLimited("scripts");
|
||||
const identity = {
|
||||
frame_id: input.frameForScript(script),
|
||||
frame_id: frame.frameId,
|
||||
frame_scope: frame.state,
|
||||
file_path: path,
|
||||
reported_url: script.rawUrl,
|
||||
has_source_url: script.hasSourceUrl,
|
||||
cdp_hash: script.hash,
|
||||
length: script.length,
|
||||
is_module: script.isModule,
|
||||
|
||||
@@ -96,7 +96,10 @@ export const electronPageInspectionSchema = z.object({
|
||||
z.object({
|
||||
script_key: prefixedDigestSchema("electron_script"),
|
||||
frame_id: z.string().nullable().default(null),
|
||||
frame_scope: z.enum(["authorized", "unknown"]).optional(),
|
||||
file_path: z.string(),
|
||||
has_source_url: z.boolean().nullable().optional(),
|
||||
reported_url: z.string().optional(),
|
||||
...cdpScriptMetadataSchema.shape,
|
||||
source: cdpScriptSourceSchema,
|
||||
}),
|
||||
|
||||
@@ -126,14 +126,22 @@ it.each(["late", "initial", "destroyed", "final-malformed", "out-of-scope"])(
|
||||
expect(
|
||||
inspected.value.frames.some(({ frame_id }) => frame_id === "child"),
|
||||
).toBe(kind !== "out-of-scope");
|
||||
expect(inspected.value.scripts.items).toContainEqual(
|
||||
expect.objectContaining({
|
||||
file_path: join(root, "app.js"),
|
||||
// Main snapshots historical frame identity at script ingestion, so
|
||||
// later context teardown does not erase this observed association.
|
||||
frame_id: kind === "out-of-scope" ? null : "child",
|
||||
}),
|
||||
);
|
||||
if (kind === "out-of-scope") {
|
||||
expect(inspected.value.scripts.items).toEqual([]);
|
||||
expect(
|
||||
browser.commands.some(
|
||||
({ method }) => method === "Debugger.getScriptSource",
|
||||
),
|
||||
).toBe(false);
|
||||
} else
|
||||
expect(inspected.value.scripts.items).toContainEqual(
|
||||
expect.objectContaining({
|
||||
file_path: join(root, "app.js"),
|
||||
// Later context teardown does not erase the observed association.
|
||||
frame_id: "child",
|
||||
frame_scope: "authorized",
|
||||
}),
|
||||
);
|
||||
if (kind === "final-malformed") {
|
||||
expect(inspected.value.completeness.attach_limited_sections).toEqual(
|
||||
expect.arrayContaining(["frames", "scripts", "workers"]),
|
||||
@@ -339,9 +347,107 @@ it.each([
|
||||
).toEqual(["first", "second"]);
|
||||
if (kind !== "detached" && kind !== "same-document")
|
||||
expect(
|
||||
result.value.scripts.items.find(({ cdp_hash }) => cdp_hash === "foreign")
|
||||
?.frame_id,
|
||||
).toBeNull();
|
||||
result.value.scripts.items.find(({ cdp_hash }) => cdp_hash === "foreign"),
|
||||
).toMatchObject({
|
||||
frame_id: kind === "unvalidated-context" ? "not-authorized" : "first",
|
||||
frame_scope: "unknown",
|
||||
});
|
||||
});
|
||||
|
||||
it("excludes srcdoc scripts before source reads while retaining declared labels and unknown frame scope", async () => {
|
||||
const root = await electronFixture();
|
||||
const mainUrl = pathToFileURL(join(root, "index.html")).href;
|
||||
const label = pathToFileURL(join(root, "app.js")).href;
|
||||
const browser = await startFakeCdpBrowser({
|
||||
electronFileUrl: mainUrl,
|
||||
eventsBeforeReply: ["Debugger.enable"],
|
||||
commandResult: ({ method, params }) =>
|
||||
method === "Page.getFrameTree"
|
||||
? {
|
||||
frameTree: {
|
||||
frame: { id: "frame-main", url: mainUrl },
|
||||
childFrames: [
|
||||
{
|
||||
frame: {
|
||||
id: "srcdoc",
|
||||
parentId: "frame-main",
|
||||
url: "about:srcdoc",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
: method === "Debugger.getScriptSource"
|
||||
? { scriptSource: `void ${String(params.scriptId)};` }
|
||||
: undefined,
|
||||
commandEvents: ({ method, sessionId }) =>
|
||||
method === "Runtime.enable"
|
||||
? []
|
||||
: method === "Debugger.enable"
|
||||
? [
|
||||
["declared", "frame-main", true],
|
||||
["reported", "frame-main", false],
|
||||
["excluded", "srcdoc", true],
|
||||
["unknown", "unproven-frame", true],
|
||||
].map(([scriptId, frameId, hasSourceURL], index) => ({
|
||||
method: "Debugger.scriptParsed",
|
||||
sessionId,
|
||||
params: {
|
||||
scriptId,
|
||||
url: label,
|
||||
executionContextId: index + 1,
|
||||
executionContextAuxData: { frameId },
|
||||
hasSourceURL,
|
||||
hash: scriptId,
|
||||
},
|
||||
}))
|
||||
: undefined,
|
||||
});
|
||||
browsers.push(browser);
|
||||
const result = await new CdpElectronProvider().inspectPage(
|
||||
inspectElectronPageInputSchema.parse({
|
||||
cdp_endpoint: browser.endpoint,
|
||||
target_id: "electron-page",
|
||||
observation_ms: 0,
|
||||
include_script_sources: true,
|
||||
}),
|
||||
);
|
||||
if (!result.ok) throw result.error;
|
||||
const byHash = new Map(
|
||||
result.value.scripts.items.map((script) => [script.cdp_hash, script]),
|
||||
);
|
||||
expect(byHash.has("excluded")).toBe(false);
|
||||
expect(byHash.get("declared")).toMatchObject({
|
||||
frame_id: "frame-main",
|
||||
frame_scope: "authorized",
|
||||
has_source_url: true,
|
||||
reported_url: label,
|
||||
file_path: join(root, "app.js"),
|
||||
source: { included: true },
|
||||
});
|
||||
expect(byHash.get("reported")).toMatchObject({
|
||||
has_source_url: false,
|
||||
frame_scope: "authorized",
|
||||
});
|
||||
expect(byHash.get("unknown")).toMatchObject({
|
||||
frame_id: "unproven-frame",
|
||||
frame_scope: "unknown",
|
||||
has_source_url: true,
|
||||
});
|
||||
expect(
|
||||
browser.commands
|
||||
.filter(({ method }) => method === "Debugger.getScriptSource")
|
||||
.map(({ params }) => params.scriptId)
|
||||
.sort(),
|
||||
).toEqual(["declared", "reported", "unknown"]);
|
||||
expect(result.value.completeness.excluded).toContainEqual({
|
||||
section: "scripts",
|
||||
reason: "out_of_target_scope",
|
||||
count: 1,
|
||||
});
|
||||
expect(result.value.completeness.attach_limited_sections).toContain(
|
||||
"scripts",
|
||||
);
|
||||
});
|
||||
|
||||
describe("CdpElectronProvider target access", () => {
|
||||
|
||||
Reference in new issue
Block a user