From 51348855080e029a7c69212854f22414045ac912 Mon Sep 17 00:00:00 2001 From: Omar SY Date: Mon, 7 Sep 2020 23:42:29 +0200 Subject: [PATCH 1/9] Support Symbol as key --- .../src/__tests__/inspectedElementContext-test.js | 6 +++--- packages/react-devtools-shared/src/hydration.js | 9 +++++---- packages/react-devtools-shared/src/utils.js | 10 +++++----- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js b/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js index 29e8e379e4e43..ecc0879534386 100644 --- a/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js +++ b/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js @@ -530,7 +530,7 @@ describe('InspectedElementContext', () => { ['second', mapShallow], ]); const objectOfObjects = { - inner: {string: 'abc', number: 123, boolean: true}, + inner: {str: 'ab', nb: 12, bool: true, [Symbol('sb')]: 'sb'}, }; const typedArray = Int8Array.from([100, -100, 0]); const arrayBuffer = typedArray.buffer; @@ -723,12 +723,12 @@ describe('InspectedElementContext', () => { ); expect(map_of_maps[meta.preview_short]).toBe('Map(2)'); - expect(object_of_objects.inner[meta.size]).toBe(3); + expect(object_of_objects.inner[meta.size]).toBe(4); expect(object_of_objects.inner[meta.inspectable]).toBe(true); expect(object_of_objects.inner[meta.name]).toBe(''); expect(object_of_objects.inner[meta.type]).toBe('object'); expect(object_of_objects.inner[meta.preview_long]).toBe( - '{boolean: true, number: 123, string: "abc"}', + '{Symbol(sb): "sb", bool: true, nb: 12, str: "ab"}', ); expect(object_of_objects.inner[meta.preview_short]).toBe('{…}'); diff --git a/packages/react-devtools-shared/src/hydration.js b/packages/react-devtools-shared/src/hydration.js index 2815be0f2d220..c2afe17a0a39a 100644 --- a/packages/react-devtools-shared/src/hydration.js +++ b/packages/react-devtools-shared/src/hydration.js @@ -88,7 +88,7 @@ function createDehydrated( if (type === 'array' || type === 'typed_array') { dehydrated.size = data.length; } else if (type === 'object') { - dehydrated.size = Object.keys(data).length; + dehydrated.size = Reflect.ownKeys(data).length; } if (type === 'iterator' || type === 'typed_array') { @@ -291,16 +291,17 @@ export function dehydrate( return createDehydrated(type, true, data, cleaned, path); } else { const object = {}; - for (const name in data) { + Reflect.ownKeys(data).forEach((key) => { + const name = key.toString() object[name] = dehydrate( - data[name], + data[key], cleaned, unserializable, path.concat([name]), isPathAllowed, isPathAllowedCheck ? 1 : level + 1, ); - } + }) return object; } diff --git a/packages/react-devtools-shared/src/utils.js b/packages/react-devtools-shared/src/utils.js index b0b873cca163d..f40029caba052 100644 --- a/packages/react-devtools-shared/src/utils.js +++ b/packages/react-devtools-shared/src/utils.js @@ -52,10 +52,10 @@ const cachedDisplayNames: WeakMap = new WeakMap(); // Try to reuse the already encoded strings. const encodedStringCache = new LRU({max: 1000}); -export function alphaSortKeys(a: string, b: string): number { - if (a > b) { +export function alphaSortKeys(a: string|number|Symbol, b: string|number|Symbol): number { + if (a.toString() > b.toString()) { return 1; - } else if (b > a) { + } else if (b.toString() > a.toString()) { return -1; } else { return 0; @@ -657,7 +657,7 @@ export function formatDataForPreview( return data.toString(); case 'object': if (showFormattedValue) { - const keys = Object.keys(data).sort(alphaSortKeys); + const keys = Reflect.ownKeys(data).sort(alphaSortKeys); let formatted = ''; for (let i = 0; i < keys.length; i++) { @@ -665,7 +665,7 @@ export function formatDataForPreview( if (i > 0) { formatted += ', '; } - formatted += `${key}: ${formatDataForPreview(data[key], false)}`; + formatted += `${key.toString()}: ${formatDataForPreview(data[key], false)}`; if (formatted.length > MAX_PREVIEW_STRING_LENGTH) { // Prevent doing a lot of unnecessary iteration... break; From ef7cdf567aa78c33a0c281e66c71d6c5f91c796f Mon Sep 17 00:00:00 2001 From: Omar SY Date: Tue, 8 Sep 2020 08:38:43 +0200 Subject: [PATCH 2/9] correct lint error --- packages/react-devtools-shared/src/hydration.js | 6 +++--- packages/react-devtools-shared/src/utils.js | 10 ++++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/react-devtools-shared/src/hydration.js b/packages/react-devtools-shared/src/hydration.js index c2afe17a0a39a..e087e21619565 100644 --- a/packages/react-devtools-shared/src/hydration.js +++ b/packages/react-devtools-shared/src/hydration.js @@ -291,8 +291,8 @@ export function dehydrate( return createDehydrated(type, true, data, cleaned, path); } else { const object = {}; - Reflect.ownKeys(data).forEach((key) => { - const name = key.toString() + Reflect.ownKeys(data).forEach(key => { + const name = key.toString(); object[name] = dehydrate( data[key], cleaned, @@ -301,7 +301,7 @@ export function dehydrate( isPathAllowed, isPathAllowedCheck ? 1 : level + 1, ); - }) + }); return object; } diff --git a/packages/react-devtools-shared/src/utils.js b/packages/react-devtools-shared/src/utils.js index f40029caba052..69d05d2c7cde8 100644 --- a/packages/react-devtools-shared/src/utils.js +++ b/packages/react-devtools-shared/src/utils.js @@ -52,7 +52,10 @@ const cachedDisplayNames: WeakMap = new WeakMap(); // Try to reuse the already encoded strings. const encodedStringCache = new LRU({max: 1000}); -export function alphaSortKeys(a: string|number|Symbol, b: string|number|Symbol): number { +export function alphaSortKeys( + a: string | number | Symbol, + b: string | number | Symbol, +): number { if (a.toString() > b.toString()) { return 1; } else if (b.toString() > a.toString()) { @@ -665,7 +668,10 @@ export function formatDataForPreview( if (i > 0) { formatted += ', '; } - formatted += `${key.toString()}: ${formatDataForPreview(data[key], false)}`; + formatted += `${key.toString()}: ${formatDataForPreview( + data[key], + false, + )}`; if (formatted.length > MAX_PREVIEW_STRING_LENGTH) { // Prevent doing a lot of unnecessary iteration... break; From 71ab637e400cde65e8ac5331d2bb7163fdc9a641 Mon Sep 17 00:00:00 2001 From: Omar SY Date: Tue, 8 Sep 2020 19:55:00 +0200 Subject: [PATCH 3/9] add symbole test --- .../inspectedElementContext-test.js.snap | 3 +++ .../src/__tests__/inspectedElementContext-test.js | 13 ++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/react-devtools-shared/src/__tests__/__snapshots__/inspectedElementContext-test.js.snap b/packages/react-devtools-shared/src/__tests__/__snapshots__/inspectedElementContext-test.js.snap index 1989aee6391a9..efb6d1667179a 100644 --- a/packages/react-devtools-shared/src/__tests__/__snapshots__/inspectedElementContext-test.js.snap +++ b/packages/react-devtools-shared/src/__tests__/__snapshots__/inspectedElementContext-test.js.snap @@ -541,6 +541,9 @@ exports[`InspectedElementContext should support complex data types: 1: Inspected "object_of_objects": { "inner": {} }, + "object_with_symbol": { + "Symbol(name)": "Hello World!" + }, "proxy": {}, "react_element": {}, "regexp": {}, diff --git a/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js b/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js index ecc0879534386..3cd0538055e64 100644 --- a/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js +++ b/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js @@ -530,7 +530,10 @@ describe('InspectedElementContext', () => { ['second', mapShallow], ]); const objectOfObjects = { - inner: {str: 'ab', nb: 12, bool: true, [Symbol('sb')]: 'sb'}, + inner: {string: 'abc', number: 123, boolean: true}, + }; + const objectWithSymbol = { + [Symbol('name')]: 'Hello World!', }; const typedArray = Int8Array.from([100, -100, 0]); const arrayBuffer = typedArray.buffer; @@ -575,6 +578,7 @@ describe('InspectedElementContext', () => { map={mapShallow} map_of_maps={mapOfMaps} object_of_objects={objectOfObjects} + object_with_symbol={objectWithSymbol} proxy={proxyInstance} react_element={} regexp={/abc/giu} @@ -628,6 +632,7 @@ describe('InspectedElementContext', () => { map, map_of_maps, object_of_objects, + object_with_symbol, proxy, react_element, regexp, @@ -723,14 +728,16 @@ describe('InspectedElementContext', () => { ); expect(map_of_maps[meta.preview_short]).toBe('Map(2)'); - expect(object_of_objects.inner[meta.size]).toBe(4); + expect(object_of_objects.inner[meta.size]).toBe(3); expect(object_of_objects.inner[meta.inspectable]).toBe(true); expect(object_of_objects.inner[meta.name]).toBe(''); expect(object_of_objects.inner[meta.type]).toBe('object'); expect(object_of_objects.inner[meta.preview_long]).toBe( - '{Symbol(sb): "sb", bool: true, nb: 12, str: "ab"}', + '{boolean: true, number: 123, string: "abc"}', ); expect(object_of_objects.inner[meta.preview_short]).toBe('{…}'); + + expect(object_with_symbol['Symbol(name)']).toBe('Hello World!'); expect(proxy[meta.inspectable]).toBe(false); expect(proxy[meta.name]).toBe('function'); From 7caa608ee0817f8f3c186e6e0a3e6e69fa0d09f5 Mon Sep 17 00:00:00 2001 From: Omar SY Date: Tue, 8 Sep 2020 20:03:51 +0200 Subject: [PATCH 4/9] correct lint --- .../src/__tests__/inspectedElementContext-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js b/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js index 3cd0538055e64..33d1db240a712 100644 --- a/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js +++ b/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js @@ -736,7 +736,7 @@ describe('InspectedElementContext', () => { '{boolean: true, number: 123, string: "abc"}', ); expect(object_of_objects.inner[meta.preview_short]).toBe('{…}'); - + expect(object_with_symbol['Symbol(name)']).toBe('Hello World!'); expect(proxy[meta.inspectable]).toBe(false); From f51b99a0f177db831d3f6c03f616b4cc7c47422b Mon Sep 17 00:00:00 2001 From: Omar SY Date: Tue, 8 Sep 2020 21:29:26 +0200 Subject: [PATCH 5/9] create getAllEnumerableKeys method --- .../inspectedElementContext-test.js.snap | 2 +- .../src/__tests__/inspectedElementContext-test.js | 4 ++-- packages/react-devtools-shared/src/hydration.js | 5 +++-- packages/react-devtools-shared/src/utils.js | 13 ++++++++++++- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/react-devtools-shared/src/__tests__/__snapshots__/inspectedElementContext-test.js.snap b/packages/react-devtools-shared/src/__tests__/__snapshots__/inspectedElementContext-test.js.snap index efb6d1667179a..6e2ec848f7e19 100644 --- a/packages/react-devtools-shared/src/__tests__/__snapshots__/inspectedElementContext-test.js.snap +++ b/packages/react-devtools-shared/src/__tests__/__snapshots__/inspectedElementContext-test.js.snap @@ -542,7 +542,7 @@ exports[`InspectedElementContext should support complex data types: 1: Inspected "inner": {} }, "object_with_symbol": { - "Symbol(name)": "Hello World!" + "Symbol(name)": "hello" }, "proxy": {}, "react_element": {}, diff --git a/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js b/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js index 33d1db240a712..057ef2b06fb08 100644 --- a/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js +++ b/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js @@ -533,7 +533,7 @@ describe('InspectedElementContext', () => { inner: {string: 'abc', number: 123, boolean: true}, }; const objectWithSymbol = { - [Symbol('name')]: 'Hello World!', + [Symbol('name')]: 'hello', }; const typedArray = Int8Array.from([100, -100, 0]); const arrayBuffer = typedArray.buffer; @@ -737,7 +737,7 @@ describe('InspectedElementContext', () => { ); expect(object_of_objects.inner[meta.preview_short]).toBe('{…}'); - expect(object_with_symbol['Symbol(name)']).toBe('Hello World!'); + expect(object_with_symbol['Symbol(name)']).toBe('hello'); expect(proxy[meta.inspectable]).toBe(false); expect(proxy[meta.name]).toBe('function'); diff --git a/packages/react-devtools-shared/src/hydration.js b/packages/react-devtools-shared/src/hydration.js index e087e21619565..1f6c0d2af6efa 100644 --- a/packages/react-devtools-shared/src/hydration.js +++ b/packages/react-devtools-shared/src/hydration.js @@ -10,6 +10,7 @@ import { getDataType, getDisplayNameForReactElement, + getAllEnumerableKeys, getInObject, formatDataForPreview, setInObject, @@ -88,7 +89,7 @@ function createDehydrated( if (type === 'array' || type === 'typed_array') { dehydrated.size = data.length; } else if (type === 'object') { - dehydrated.size = Reflect.ownKeys(data).length; + dehydrated.size = Object.keys(data).length; } if (type === 'iterator' || type === 'typed_array') { @@ -291,7 +292,7 @@ export function dehydrate( return createDehydrated(type, true, data, cleaned, path); } else { const object = {}; - Reflect.ownKeys(data).forEach(key => { + getAllEnumerableKeys(data).forEach(key => { const name = key.toString(); object[name] = dehydrate( data[key], diff --git a/packages/react-devtools-shared/src/utils.js b/packages/react-devtools-shared/src/utils.js index 69d05d2c7cde8..cfc7e17a1923c 100644 --- a/packages/react-devtools-shared/src/utils.js +++ b/packages/react-devtools-shared/src/utils.js @@ -65,6 +65,17 @@ export function alphaSortKeys( } } +export function getAllEnumerableKeys( + obj: Object, +): Array { + const keys = []; + for (const key in obj) { + keys.push(key); + } + + return keys.concat(Object.getOwnPropertySymbols(obj)); +} + export function getDisplayName( type: Function, fallbackName: string = 'Anonymous', @@ -660,7 +671,7 @@ export function formatDataForPreview( return data.toString(); case 'object': if (showFormattedValue) { - const keys = Reflect.ownKeys(data).sort(alphaSortKeys); + const keys = getAllEnumerableKeys(data).sort(alphaSortKeys); let formatted = ''; for (let i = 0; i < keys.length; i++) { From de1bfaec32e22a28b34c123e3919c0a47be2a864 Mon Sep 17 00:00:00 2001 From: Omar SY Date: Fri, 11 Sep 2020 19:57:22 +0200 Subject: [PATCH 6/9] Add all symbol enumerable --- fixtures/devtools/standalone/index.html | 52 +++++++++++ .../inspectedElementContext-test.js.snap | 19 ++++ .../__tests__/inspectedElementContext-test.js | 92 +++++++++++++++++++ .../__snapshots__/inspectElement-test.js.snap | 23 +++++ .../__tests__/legacy/inspectElement-test.js | 65 +++++++++++++ packages/react-devtools-shared/src/utils.js | 18 +++- 6 files changed, 265 insertions(+), 4 deletions(-) diff --git a/fixtures/devtools/standalone/index.html b/fixtures/devtools/standalone/index.html index 28255cb67ee6c..bfb35d2453421 100644 --- a/fixtures/devtools/standalone/index.html +++ b/fixtures/devtools/standalone/index.html @@ -208,6 +208,57 @@

List

return ; } + const baseInheritedKeys = Object.create(Object.prototype, { + value1: { + value: 1, + writable: true, + enumerable: true, + configurable: true, + }, + [Symbol('value1')]: { + value: 1, + writable: true, + enumerable: true, + configurable: true, + }, + }); + + const inheritedKeys = Object.create(baseInheritedKeys, { + value2: { + value: 2, + writable: true, + enumerable: true, + configurable: true, + }, + value3: { + value: 3, + writable: true, + enumerable: false, + configurable: true, + }, + 3: { + value: 3, + writable: true, + enumerable: true, + configurable: true, + }, + [Symbol('value2')]: { + value: 2, + writable: true, + enumerable: false, + configurable: true, + }, + [Symbol('value3')]: { + value: 3, + writable: true, + enumerable: true, + configurable: true, + }, + }); + function InheritedKeys() { + return ; + } + const object = { string: "abc", longString: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKJLMNOPQRSTUVWXYZ1234567890", @@ -294,6 +345,7 @@

List

+ ); } diff --git a/packages/react-devtools-shared/src/__tests__/__snapshots__/inspectedElementContext-test.js.snap b/packages/react-devtools-shared/src/__tests__/__snapshots__/inspectedElementContext-test.js.snap index 6e2ec848f7e19..e41e498df11cf 100644 --- a/packages/react-devtools-shared/src/__tests__/__snapshots__/inspectedElementContext-test.js.snap +++ b/packages/react-devtools-shared/src/__tests__/__snapshots__/inspectedElementContext-test.js.snap @@ -615,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": { + "3": 3, + "value2": 2, + "Symbol(value3)": 3, + "value1": 1, + "Symbol(value1)": 1 + } + }, + "state": null +} +`; + exports[`InspectedElementContext should support simple data types: 1: Initial inspection 1`] = ` { "id": 2, diff --git a/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js b/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js index 057ef2b06fb08..19187753d4bbf 100644 --- a/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js +++ b/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js @@ -941,6 +941,98 @@ describe('InspectedElementContext', () => { done(); }); + it('should support objects with with inherited keys', async done => { + const Example = () => null; + + const base = Object.create(Object.prototype, { + value1: { + value: 1, + writable: true, + enumerable: true, + configurable: true, + }, + [Symbol('value1')]: { + value: 1, + writable: true, + enumerable: true, + configurable: true, + }, + }); + + const object = Object.create(base, { + value2: { + value: 2, + writable: true, + enumerable: true, + configurable: true, + }, + value3: { + value: 3, + writable: true, + enumerable: false, + configurable: true, + }, + 3: { + value: 3, + writable: true, + enumerable: true, + configurable: true, + }, + [Symbol('value2')]: { + value: 2, + writable: true, + enumerable: false, + configurable: true, + }, + [Symbol('value3')]: { + value: 3, + writable: true, + enumerable: true, + configurable: true, + }, + }); + const container = document.createElement('div'); + await utils.actAsync(() => + ReactDOM.render(, 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( + + + + + , + ), + false, + ); + + expect(inspectedElement).not.toBeNull(); + expect(inspectedElement).toMatchSnapshot(`1: Inspected element ${id}`); + expect(inspectedElement.props.object).toEqual({ + '3': 3, + 'Symbol(value1)': 1, + 'Symbol(value3)': 3, + value1: 1, + value2: 2, + }); + + done(); + }); + it('should not dehydrate nested values until explicitly requested', async done => { const Example = () => { const [state] = React.useState({ diff --git a/packages/react-devtools-shared/src/__tests__/legacy/__snapshots__/inspectElement-test.js.snap b/packages/react-devtools-shared/src/__tests__/legacy/__snapshots__/inspectElement-test.js.snap index 18ff7483c3f5e..8fbfe5df4ea99 100644 --- a/packages/react-devtools-shared/src/__tests__/legacy/__snapshots__/inspectElement-test.js.snap +++ b/packages/react-devtools-shared/src/__tests__/legacy/__snapshots__/inspectElement-test.js.snap @@ -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": { + "3": 3, + "value2": 2, + "Symbol(value3)": 3, + "value1": 1, + "Symbol(value1)": 1 + } + }, + "state": null +}, +} +`; + exports[`InspectedElementContext should support simple data types: 1: Initial inspection 1`] = ` Object { "id": 2, diff --git a/packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js b/packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js index 7f1c461d93542..39c57da6db3e1 100644 --- a/packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js +++ b/packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js @@ -432,6 +432,71 @@ describe('InspectedElementContext', () => { done(); }); + it('should support objects with with inherited keys', async done => { + const Example = () => null; + + const base = Object.create(Object.prototype, { + value1: { + value: 1, + writable: true, + enumerable: true, + configurable: true, + }, + [Symbol('value1')]: { + value: 1, + writable: true, + enumerable: true, + configurable: true, + }, + }); + + const object = Object.create(base, { + value2: { + value: 2, + writable: true, + enumerable: true, + configurable: true, + }, + value3: { + value: 3, + writable: true, + enumerable: false, + configurable: true, + }, + 3: { + value: 3, + writable: true, + enumerable: true, + configurable: true, + }, + [Symbol('value2')]: { + value: 2, + writable: true, + enumerable: false, + configurable: true, + }, + [Symbol('value3')]: { + value: 3, + writable: true, + enumerable: true, + configurable: true, + }, + }); + act(() => + ReactDOM.render( + , + 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; diff --git a/packages/react-devtools-shared/src/utils.js b/packages/react-devtools-shared/src/utils.js index cfc7e17a1923c..6356d9d2b9d2f 100644 --- a/packages/react-devtools-shared/src/utils.js +++ b/packages/react-devtools-shared/src/utils.js @@ -69,11 +69,21 @@ export function getAllEnumerableKeys( obj: Object, ): Array { const keys = []; - for (const key in obj) { - keys.push(key); + let current = obj; + while (current != null) { + const currentKeys = [ + ...Object.keys(current), + ...Object.getOwnPropertySymbols(current), + ]; + const descriptors = Object.getOwnPropertyDescriptors(current); + currentKeys.forEach(key => { + if (descriptors[key].enumerable) { + keys.push(key); + } + }); + current = Object.getPrototypeOf(current); } - - return keys.concat(Object.getOwnPropertySymbols(obj)); + return keys; } export function getDisplayName( From db0912718a76880a5202ded2348c4cc02de2b3b1 Mon Sep 17 00:00:00 2001 From: Omar SY Date: Fri, 11 Sep 2020 20:10:03 +0200 Subject: [PATCH 7/9] fix lint error --- .../src/__tests__/legacy/inspectElement-test.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js b/packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js index 39c57da6db3e1..c9ca02ee4c851 100644 --- a/packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js +++ b/packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js @@ -483,10 +483,7 @@ describe('InspectedElementContext', () => { }, }); act(() => - ReactDOM.render( - , - document.createElement('div'), - ), + ReactDOM.render(, document.createElement('div')), ); const id = ((store.getElementIDAtIndex(0): any): number); From dbf29c3fc9ca9f2a139db6793ca9db37954d8cc2 Mon Sep 17 00:00:00 2001 From: Omar SY Date: Fri, 11 Sep 2020 20:28:12 +0200 Subject: [PATCH 8/9] fix flow issue --- packages/react-devtools-shared/src/utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-devtools-shared/src/utils.js b/packages/react-devtools-shared/src/utils.js index 6356d9d2b9d2f..a0ec1038e75dc 100644 --- a/packages/react-devtools-shared/src/utils.js +++ b/packages/react-devtools-shared/src/utils.js @@ -77,6 +77,7 @@ export function getAllEnumerableKeys( ]; const descriptors = Object.getOwnPropertyDescriptors(current); 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); } From 6b06f419991700ee74ea368b79c977c8586203ac Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 14 Sep 2020 09:45:17 -0400 Subject: [PATCH 9/9] Minor tweaks to DevTools test data --- fixtures/devtools/standalone/index.html | 29 +++++-- .../inspectedElementContext-test.js.snap | 10 +-- .../__tests__/inspectedElementContext-test.js | 37 ++++++--- .../__snapshots__/inspectElement-test.js.snap | 10 +-- .../__tests__/legacy/inspectElement-test.js | 27 +++++-- .../InspectableElements.js | 2 + .../src/app/InspectableElements/SymbolKeys.js | 78 +++++++++++++++++++ 7 files changed, 156 insertions(+), 37 deletions(-) create mode 100644 packages/react-devtools-shell/src/app/InspectableElements/SymbolKeys.js diff --git a/fixtures/devtools/standalone/index.html b/fixtures/devtools/standalone/index.html index bfb35d2453421..235ceeb16ed76 100644 --- a/fixtures/devtools/standalone/index.html +++ b/fixtures/devtools/standalone/index.html @@ -209,54 +209,67 @@

List

} const baseInheritedKeys = Object.create(Object.prototype, { - value1: { + enumerableStringBase: { value: 1, writable: true, enumerable: true, configurable: true, }, - [Symbol('value1')]: { + [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, { - value2: { + enumerableString: { value: 2, writable: true, enumerable: true, configurable: true, }, - value3: { + nonEnumerableString: { value: 3, writable: true, enumerable: false, configurable: true, }, - 3: { + 123: { value: 3, writable: true, enumerable: true, configurable: true, }, - [Symbol('value2')]: { + [Symbol('nonEnumerableSymbol')]: { value: 2, writable: true, enumerable: false, configurable: true, }, - [Symbol('value3')]: { + [Symbol('enumerableSymbol')]: { value: 3, writable: true, enumerable: true, configurable: true, }, }); + function InheritedKeys() { - return ; + return ; } const object = { diff --git a/packages/react-devtools-shared/src/__tests__/__snapshots__/inspectedElementContext-test.js.snap b/packages/react-devtools-shared/src/__tests__/__snapshots__/inspectedElementContext-test.js.snap index e41e498df11cf..f56288c12e640 100644 --- a/packages/react-devtools-shared/src/__tests__/__snapshots__/inspectedElementContext-test.js.snap +++ b/packages/react-devtools-shared/src/__tests__/__snapshots__/inspectedElementContext-test.js.snap @@ -623,11 +623,11 @@ exports[`InspectedElementContext should support objects with with inherited keys "hooks": null, "props": { "object": { - "3": 3, - "value2": 2, - "Symbol(value3)": 3, - "value1": 1, - "Symbol(value1)": 1 + "123": 3, + "enumerableString": 2, + "Symbol(enumerableSymbol)": 3, + "enumerableStringBase": 1, + "Symbol(enumerableSymbolBase)": 1 } }, "state": null diff --git a/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js b/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js index 19187753d4bbf..ae6fdfbc831d0 100644 --- a/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js +++ b/packages/react-devtools-shared/src/__tests__/inspectedElementContext-test.js @@ -945,52 +945,65 @@ describe('InspectedElementContext', () => { const Example = () => null; const base = Object.create(Object.prototype, { - value1: { + enumerableStringBase: { value: 1, writable: true, enumerable: true, configurable: true, }, - [Symbol('value1')]: { + [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, { - value2: { + enumerableString: { value: 2, writable: true, enumerable: true, configurable: true, }, - value3: { + nonEnumerableString: { value: 3, writable: true, enumerable: false, configurable: true, }, - 3: { + [123]: { value: 3, writable: true, enumerable: true, configurable: true, }, - [Symbol('value2')]: { + [Symbol('nonEnumerableSymbol')]: { value: 2, writable: true, enumerable: false, configurable: true, }, - [Symbol('value3')]: { + [Symbol('enumerableSymbol')]: { value: 3, writable: true, enumerable: true, configurable: true, }, }); + const container = document.createElement('div'); await utils.actAsync(() => ReactDOM.render(, container), @@ -1023,11 +1036,11 @@ describe('InspectedElementContext', () => { expect(inspectedElement).not.toBeNull(); expect(inspectedElement).toMatchSnapshot(`1: Inspected element ${id}`); expect(inspectedElement.props.object).toEqual({ - '3': 3, - 'Symbol(value1)': 1, - 'Symbol(value3)': 3, - value1: 1, - value2: 2, + 123: 3, + 'Symbol(enumerableSymbol)': 3, + 'Symbol(enumerableSymbolBase)': 1, + enumerableString: 2, + enumerableStringBase: 1, }); done(); diff --git a/packages/react-devtools-shared/src/__tests__/legacy/__snapshots__/inspectElement-test.js.snap b/packages/react-devtools-shared/src/__tests__/legacy/__snapshots__/inspectElement-test.js.snap index 8fbfe5df4ea99..11eb16474363a 100644 --- a/packages/react-devtools-shared/src/__tests__/legacy/__snapshots__/inspectElement-test.js.snap +++ b/packages/react-devtools-shared/src/__tests__/legacy/__snapshots__/inspectElement-test.js.snap @@ -247,11 +247,11 @@ Object { "hooks": null, "props": { "data": { - "3": 3, - "value2": 2, - "Symbol(value3)": 3, - "value1": 1, - "Symbol(value1)": 1 + "123": 3, + "enumerableString": 2, + "Symbol(enumerableSymbol)": 3, + "enumerableStringBase": 1, + "Symbol(enumerableSymbolBase)": 1 } }, "state": null diff --git a/packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js b/packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js index c9ca02ee4c851..18a965b24b56e 100644 --- a/packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js +++ b/packages/react-devtools-shared/src/__tests__/legacy/inspectElement-test.js @@ -436,52 +436,65 @@ describe('InspectedElementContext', () => { const Example = () => null; const base = Object.create(Object.prototype, { - value1: { + enumerableStringBase: { value: 1, writable: true, enumerable: true, configurable: true, }, - [Symbol('value1')]: { + [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, { - value2: { + enumerableString: { value: 2, writable: true, enumerable: true, configurable: true, }, - value3: { + nonEnumerableString: { value: 3, writable: true, enumerable: false, configurable: true, }, - 3: { + [123]: { value: 3, writable: true, enumerable: true, configurable: true, }, - [Symbol('value2')]: { + [Symbol('nonEnumerableSymbol')]: { value: 2, writable: true, enumerable: false, configurable: true, }, - [Symbol('value3')]: { + [Symbol('enumerableSymbol')]: { value: 3, writable: true, enumerable: true, configurable: true, }, }); + act(() => ReactDOM.render(, document.createElement('div')), ); diff --git a/packages/react-devtools-shell/src/app/InspectableElements/InspectableElements.js b/packages/react-devtools-shell/src/app/InspectableElements/InspectableElements.js index 49fc1c4f2edd6..1403ece9f6dbf 100644 --- a/packages/react-devtools-shell/src/app/InspectableElements/InspectableElements.js +++ b/packages/react-devtools-shell/src/app/InspectableElements/InspectableElements.js @@ -17,6 +17,7 @@ import CustomObject from './CustomObject'; import EdgeCaseObjects from './EdgeCaseObjects.js'; import NestedProps from './NestedProps'; import SimpleValues from './SimpleValues'; +import SymbolKeys from './SymbolKeys'; // TODO Add Immutable JS example @@ -32,6 +33,7 @@ export default function InspectableElements() { + ); } diff --git a/packages/react-devtools-shell/src/app/InspectableElements/SymbolKeys.js b/packages/react-devtools-shell/src/app/InspectableElements/SymbolKeys.js new file mode 100644 index 0000000000000..3aae9c8837bc8 --- /dev/null +++ b/packages/react-devtools-shell/src/app/InspectableElements/SymbolKeys.js @@ -0,0 +1,78 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import * as React from 'react'; + +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 data = 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, + }, +}); + +export default function SymbolKeys() { + return ; +} + +function ChildComponent(props: any) { + return null; +}