Skip to content

Commit

Permalink
React DevTools: Show symbols used as keys in state (facebook#19786)
Browse files Browse the repository at this point in the history
Co-authored-by: Brian Vaughn <bvaughn@fb.com>
  • Loading branch information
omarsy and Brian Vaughn committed Sep 14, 2020
1 parent 11ee82d commit 917cb01
Show file tree
Hide file tree
Showing 9 changed files with 415 additions and 8 deletions.
65 changes: 65 additions & 0 deletions fixtures/devtools/standalone/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,70 @@ <h1>List</h1>
return <ChildComponent customObject={new Custom()} />;
}

const baseInheritedKeys = Object.create(Object.prototype, {
enumerableStringBase: {
value: 1,
writable: true,
enumerable: true,
configurable: true,
},
[Symbol('enumerableSymbolBase')]: {
value: 1,
writable: true,
enumerable: true,
configurable: true,
},
nonEnumerableStringBase: {
value: 1,
writable: true,
enumerable: false,
configurable: true,
},
[Symbol('nonEnumerableSymbolBase')]: {
value: 1,
writable: true,
enumerable: false,
configurable: true,
},
});

const inheritedKeys = Object.create(baseInheritedKeys, {
enumerableString: {
value: 2,
writable: true,
enumerable: true,
configurable: true,
},
nonEnumerableString: {
value: 3,
writable: true,
enumerable: false,
configurable: true,
},
123: {
value: 3,
writable: true,
enumerable: true,
configurable: true,
},
[Symbol('nonEnumerableSymbol')]: {
value: 2,
writable: true,
enumerable: false,
configurable: true,
},
[Symbol('enumerableSymbol')]: {
value: 3,
writable: true,
enumerable: true,
configurable: true,
},
});

function InheritedKeys() {
return <ChildComponent data={inheritedKeys} />;
}

const object = {
string: "abc",
longString: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKJLMNOPQRSTUVWXYZ1234567890",
Expand Down Expand Up @@ -294,6 +358,7 @@ <h1>List</h1>
<ObjectProps />
<UnserializableProps />
<CustomObject />
<InheritedKeys />
</Fragment>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,9 @@ exports[`InspectedElementContext should support complex data types: 1: Inspected
"object_of_objects": {
"inner": {}
},
"object_with_symbol": {
"Symbol(name)": "hello"
},
"proxy": {},
"react_element": {},
"regexp": {},
Expand Down Expand Up @@ -612,6 +615,25 @@ exports[`InspectedElementContext should support objects with overridden hasOwnPr
}
`;

exports[`InspectedElementContext should support objects with with inherited keys: 1: Inspected element 2 1`] = `
{
"id": 2,
"owners": null,
"context": null,
"hooks": null,
"props": {
"object": {
"123": 3,
"enumerableString": 2,
"Symbol(enumerableSymbol)": 3,
"enumerableStringBase": 1,
"Symbol(enumerableSymbolBase)": 1
}
},
"state": null
}
`;

exports[`InspectedElementContext should support simple data types: 1: Initial inspection 1`] = `
{
"id": 2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,9 @@ describe('InspectedElementContext', () => {
const objectOfObjects = {
inner: {string: 'abc', number: 123, boolean: true},
};
const objectWithSymbol = {
[Symbol('name')]: 'hello',
};
const typedArray = Int8Array.from([100, -100, 0]);
const arrayBuffer = typedArray.buffer;
const dataView = new DataView(arrayBuffer);
Expand Down Expand Up @@ -580,6 +583,7 @@ describe('InspectedElementContext', () => {
map={mapShallow}
map_of_maps={mapOfMaps}
object_of_objects={objectOfObjects}
object_with_symbol={objectWithSymbol}
proxy={proxyInstance}
react_element={<span />}
regexp={/abc/giu}
Expand Down Expand Up @@ -633,6 +637,7 @@ describe('InspectedElementContext', () => {
map,
map_of_maps,
object_of_objects,
object_with_symbol,
proxy,
react_element,
regexp,
Expand Down Expand Up @@ -737,6 +742,8 @@ describe('InspectedElementContext', () => {
);
expect(object_of_objects.inner[meta.preview_short]).toBe('{…}');

expect(object_with_symbol['Symbol(name)']).toBe('hello');

expect(proxy[meta.inspectable]).toBe(false);
expect(proxy[meta.name]).toBe('function');
expect(proxy[meta.type]).toBe('function');
Expand Down Expand Up @@ -939,6 +946,111 @@ describe('InspectedElementContext', () => {
done();
});

it('should support objects with with inherited keys', async done => {
const Example = () => null;

const base = Object.create(Object.prototype, {
enumerableStringBase: {
value: 1,
writable: true,
enumerable: true,
configurable: true,
},
[Symbol('enumerableSymbolBase')]: {
value: 1,
writable: true,
enumerable: true,
configurable: true,
},
nonEnumerableStringBase: {
value: 1,
writable: true,
enumerable: false,
configurable: true,
},
[Symbol('nonEnumerableSymbolBase')]: {
value: 1,
writable: true,
enumerable: false,
configurable: true,
},
});

const object = Object.create(base, {
enumerableString: {
value: 2,
writable: true,
enumerable: true,
configurable: true,
},
nonEnumerableString: {
value: 3,
writable: true,
enumerable: false,
configurable: true,
},
[123]: {
value: 3,
writable: true,
enumerable: true,
configurable: true,
},
[Symbol('nonEnumerableSymbol')]: {
value: 2,
writable: true,
enumerable: false,
configurable: true,
},
[Symbol('enumerableSymbol')]: {
value: 3,
writable: true,
enumerable: true,
configurable: true,
},
});

const container = document.createElement('div');
await utils.actAsync(() =>
ReactDOM.render(<Example object={object} />, 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.object).toEqual({
123: 3,
'Symbol(enumerableSymbol)': 3,
'Symbol(enumerableSymbolBase)': 1,
enumerableString: 2,
enumerableStringBase: 1,
});

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
Expand Up @@ -236,6 +236,29 @@ Object {
}
`;

exports[`InspectedElementContext should support objects with with inherited keys: 1: Initial inspection 1`] = `
Object {
"id": 2,
"type": "full-data",
"value": {
"id": 2,
"owners": null,
"context": {},
"hooks": null,
"props": {
"data": {
"123": 3,
"enumerableString": 2,
"Symbol(enumerableSymbol)": 3,
"enumerableStringBase": 1,
"Symbol(enumerableSymbolBase)": 1
}
},
"state": null
},
}
`;

exports[`InspectedElementContext should support simple data types: 1: Initial inspection 1`] = `
Object {
"id": 2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,81 @@ describe('InspectedElementContext', () => {
done();
});

it('should support objects with with inherited keys', async done => {
const Example = () => null;

const base = Object.create(Object.prototype, {
enumerableStringBase: {
value: 1,
writable: true,
enumerable: true,
configurable: true,
},
[Symbol('enumerableSymbolBase')]: {
value: 1,
writable: true,
enumerable: true,
configurable: true,
},
nonEnumerableStringBase: {
value: 1,
writable: true,
enumerable: false,
configurable: true,
},
[Symbol('nonEnumerableSymbolBase')]: {
value: 1,
writable: true,
enumerable: false,
configurable: true,
},
});

const object = Object.create(base, {
enumerableString: {
value: 2,
writable: true,
enumerable: true,
configurable: true,
},
nonEnumerableString: {
value: 3,
writable: true,
enumerable: false,
configurable: true,
},
[123]: {
value: 3,
writable: true,
enumerable: true,
configurable: true,
},
[Symbol('nonEnumerableSymbol')]: {
value: 2,
writable: true,
enumerable: false,
configurable: true,
},
[Symbol('enumerableSymbol')]: {
value: 3,
writable: true,
enumerable: true,
configurable: true,
},
});

act(() =>
ReactDOM.render(<Example data={object} />, document.createElement('div')),
);

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

expect(inspectedElement).toMatchSnapshot('1: Initial inspection');

done();
});

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

Expand Down
Loading

0 comments on commit 917cb01

Please sign in to comment.