fix(devtools): fix regression for node serialization.

This fixes a regression introduced by #69309 where the format of `serializedId` from a coma separated string, to a JSON representation of an array.

fixes #70104

(cherry picked from commit 03f1758e2a)
This commit is contained in:
Matthieu Riegler
2026-08-06 18:37:56 +02:00
committed by Alon Mishne
parent 1ad10ad494
commit d3d46c9475
3 changed files with 88 additions and 13 deletions
@@ -7,18 +7,19 @@
*/
import type {
ɵAcxComponentDebugMetadata as AcxComponentDebugMetadata,
ɵAngularComponentDebugMetadata as AngularComponentDebugMetadata,
ClassProvider,
ExistingProvider,
FactoryProvider,
InjectOptions,
InjectionToken,
Injector,
ɵProviderRecord as ProviderRecord,
Type,
ValueProvider,
ɵAngularComponentDebugMetadata as AngularComponentDebugMetadata,
ɵAcxComponentDebugMetadata as AcxComponentDebugMetadata,
ɵProviderRecord as ProviderRecord,
} from '@angular/core';
import {buildDirectiveForestWithStrategy} from '..';
import {
ChangeDetection,
ComponentExplorerViewQuery,
@@ -32,7 +33,11 @@ import {
SerializedProviderRecord,
UpdatedStateData,
} from '../../../../../protocol';
import {buildDirectiveForestWithStrategy} from '..';
import {
ComponentInstanceType,
ComponentTreeNode,
DirectiveInstanceType,
} from '../../shared/interfaces';
import {
ngDebugApiIsSupported,
ngDebugClient,
@@ -42,16 +47,11 @@ import {
deeplySerializeSelectedProperties,
serializeDirectiveState,
} from '../../shared/state-serializer/state-serializer';
import {mutateNestedProp} from '../property-mutation/property-mutation';
import {
ComponentTreeNode,
DirectiveInstanceType,
ComponentInstanceType,
} from '../../shared/interfaces';
import {getAppRoots} from './get-roots';
import {AcxChangeDetectionStrategy, ChangeDetectionStrategy, Framework} from '../core-enums';
import {unwrapSignal} from '../../shared/utils/general';
import {AcxChangeDetectionStrategy, ChangeDetectionStrategy, Framework} from '../core-enums';
import {mutateNestedProp} from '../property-mutation/property-mutation';
import {getLViewFromDirectiveOrElementInstance} from '../tree-strategies/ltree';
import {getAppRoots} from './get-roots';
export const injectorToId = new WeakMap<Injector | HTMLElement, string>();
export const nodeInjectorToResolutionPath = new WeakMap<HTMLElement, SerializedInjector[]>();
@@ -734,7 +734,7 @@ export const findNodeInForest = (
export const findNodeFromSerializedPosition = (
serializedPosition: string,
): ComponentTreeNode | null => {
const position: number[] = serializedPosition.split(',').map((index) => parseInt(index, 10));
const position: number[] = JSON.parse(serializedPosition);
return queryDirectiveForest(position, buildDirectiveForest());
};
@@ -108,6 +108,27 @@ ts_project(
],
)
ts_test_library(
name = "chrome_application_operations_test_lib",
srcs = ["chrome-application-operations.spec.ts"],
deps = [
":chrome_application_operations",
"//:node_modules/@angular/cdk",
"//:node_modules/@angular/core",
"//:node_modules/@types/chrome",
"//:node_modules/jasmine",
"//devtools/projects/ng-devtools",
"//devtools/projects/protocol",
],
)
zoneless_web_test_suite(
name = "chrome_application_operations_test",
deps = [
":chrome_application_operations_test_lib",
],
)
ts_project(
name = "same_page_message_bus",
srcs = [
@@ -0,0 +1,54 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
/// <reference types="chrome"/>
import {Platform} from '@angular/cdk/platform';
import {TestBed} from '@angular/core/testing';
import {Frame} from '../../../ng-devtools';
import {ChromeApplicationOperations} from './chrome-application-operations';
describe('ChromeApplicationOperations', () => {
let operations: ChromeApplicationOperations;
beforeEach(() => {
// Mock chrome global
(globalThis as any).chrome = {
devtools: {
inspectedWindow: {
eval: jasmine.createSpy('eval'),
},
},
};
TestBed.configureTestingModule({
providers: [ChromeApplicationOperations, {provide: Platform, useValue: {FIREFOX: false}}],
});
operations = TestBed.inject(ChromeApplicationOperations);
});
afterEach(() => {
delete (globalThis as any).chrome;
});
describe('viewSource', () => {
it('should call chrome.devtools.inspectedWindow.eval with correct string', () => {
const target: Frame = {
name: 'test1',
id: 0,
url: new URL('http://localhost:4200/url'),
} as any;
operations.viewSource([0, 0], target, 0);
expect(chrome.devtools.inspectedWindow.eval).toHaveBeenCalledWith(
'inspect(inspectedApplication.findConstructorByPosition("[0,0]", 0))',
{frameURL: 'http://localhost:4200/url'},
);
});
});
});