Skip to content

Commit

Permalink
[devtools] Fix can't expand prop value in some scenarios
Browse files Browse the repository at this point in the history
  • Loading branch information
iChenLei committed Jan 5, 2021
1 parent 50393dc commit 8201a8f
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`InspectedElementContext should allow component prop value and value\`s prototype has same name params.: 1: Inspected element 2 1`] = `
{
"id": 2,
"owners": null,
"context": null,
"hooks": null,
"props": {
"data": {
"b": null,
"c": null,
"d": "normal"
}
},
"state": null
}
`;

exports[`InspectedElementContext should dehydrate complex nested values when requested: 1: Initially inspect element 1`] = `
{
"id": 2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1107,6 +1107,82 @@ describe('InspectedElementContext', () => {
done();
});

it('should allow component prop value and value`s prototype has same name params.', async done => {
const testData = Object.create(
{
a: undefined,
b: Infinity,
c: NaN,
d: 'normal',
},
{
a: {
value: undefined,
writable: true,
enumerable: true,
configurable: true,
},
b: {
value: Infinity,
writable: true,
enumerable: true,
configurable: true,
},
c: {
value: NaN,
writable: true,
enumerable: true,
configurable: true,
},
d: {
value: 'normal',
writable: true,
enumerable: true,
configurable: true,
},
},
);
const Example = ({data}) => null;
const container = document.createElement('div');
await utils.actAsync(() =>
ReactDOM.render(<Example data={testData} />, container),
);

const id = ((store.getElementIDAtIndex(0): any): number);

let inspectedElement = null;

function Suspender({target}) {
const {getInspectedElement} = React.useContext(InspectedElementContext);
inspectedElement = getInspectedElement(id);
return null;
}

await utils.actAsync(
() =>
TestRenderer.create(
<Contexts
defaultSelectedElementID={id}
defaultSelectedElementIndex={0}>
<React.Suspense fallback={null}>
<Suspender target={id} />
</React.Suspense>
</Contexts>,
),
false,
);
expect(inspectedElement).not.toBeNull();
expect(inspectedElement).toMatchSnapshot(`1: Inspected element ${id}`);
expect(inspectedElement.props.data).toEqual({
a: undefined,
b: Infinity,
c: NaN,
d: 'normal',
});

done();
});

it('should not dehydrate nested values until explicitly requested', async done => {
const Example = () => {
const [state] = React.useState({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`InspectedElementContext should allow component prop value and value\`s prototype has same name params.: 1: Initially inspect element 1`] = `
Object {
"id": 2,
"type": "full-data",
"value": {
"id": 2,
"owners": null,
"context": {},
"hooks": null,
"props": {
"data": {
"b": null,
"c": null,
"d": "normal"
}
},
"state": null
},
}
`;

exports[`InspectedElementContext should inspect the currently selected element: 1: Initial inspection 1`] = `
Object {
"id": 2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {InspectedElementPayload} from 'react-devtools-shared/src/backend/ty
import type {DehydratedData} from 'react-devtools-shared/src/devtools/views/Components/types';
import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
import type Store from 'react-devtools-shared/src/devtools/store';
import {element} from 'prop-types';

describe('InspectedElementContext', () => {
let React;
Expand Down Expand Up @@ -537,6 +538,59 @@ describe('InspectedElementContext', () => {
done();
});

it('should allow component prop value and value`s prototype has same name params.', async done => {
const testData = Object.create(
{
a: undefined,
b: Infinity,
c: NaN,
d: 'normal',
},
{
a: {
value: undefined,
writable: true,
enumerable: true,
configurable: true,
},
b: {
value: Infinity,
writable: true,
enumerable: true,
configurable: true,
},
c: {
value: NaN,
writable: true,
enumerable: true,
configurable: true,
},
d: {
value: 'normal',
writable: true,
enumerable: true,
configurable: true,
},
},
);
const Example = ({data}) => null;
act(() =>
ReactDOM.render(
<Example data={testData} />,
document.createElement('div'),
),
);
const id = ((store.getElementIDAtIndex(0): any): number);
let inspectedElement = await read(id);
expect(inspectedElement).toMatchSnapshot('1: Initially inspect element');
const {props} = inspectedElement.value;
expect(props.data.a).toBeUndefined();
expect(Number.isFinite(props.data.b)).toBe(false);
expect(props.data.c).toBeNaN();

done();
});

it('should not dehydrate nested values until explicitly requested', async done => {
const Example = () => null;

Expand Down
8 changes: 4 additions & 4 deletions packages/react-devtools-shared/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ export function alphaSortKeys(

export function getAllEnumerableKeys(
obj: Object,
): Array<string | number | Symbol> {
const keys = [];
): Set<string | number | Symbol> {
const keys = new Set();
let current = obj;
while (current != null) {
const currentKeys = [
Expand All @@ -82,7 +82,7 @@ export function getAllEnumerableKeys(
currentKeys.forEach(key => {
// $FlowFixMe: key can be a Symbol https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor
if (descriptors[key].enumerable) {
keys.push(key);
keys.add(key);
}
});
current = Object.getPrototypeOf(current);
Expand Down Expand Up @@ -767,7 +767,7 @@ export function formatDataForPreview(
return data.toString();
case 'object':
if (showFormattedValue) {
const keys = getAllEnumerableKeys(data).sort(alphaSortKeys);
const keys = Array.from(getAllEnumerableKeys(data)).sort(alphaSortKeys);

let formatted = '';
for (let i = 0; i < keys.length; i++) {
Expand Down

0 comments on commit 8201a8f

Please sign in to comment.