Skip to content

Commit

Permalink
Temporarily feature flag numeric fallback for symbols (#24401)
Browse files Browse the repository at this point in the history
  • Loading branch information
rickhanlonii authored Apr 19, 2022
1 parent a6d53f3 commit 4175f05
Show file tree
Hide file tree
Showing 15 changed files with 328 additions and 27 deletions.
112 changes: 112 additions & 0 deletions packages/react/src/__tests__/ReactElement-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,26 @@

'use strict';

import {enableSymbolFallbackForWWW} from 'shared/ReactFeatureFlags';

let React;
let ReactDOM;
let ReactTestUtils;

describe('ReactElement', () => {
let ComponentClass;
let originalSymbol;

beforeEach(() => {
jest.resetModules();

if (enableSymbolFallbackForWWW) {
// Delete the native Symbol if we have one to ensure we test the
// unpolyfilled environment.
originalSymbol = global.Symbol;
global.Symbol = undefined;
}

React = require('react');
ReactDOM = require('react-dom');
ReactTestUtils = require('react-dom/test-utils');
Expand All @@ -31,6 +41,17 @@ describe('ReactElement', () => {
};
});

afterEach(() => {
if (enableSymbolFallbackForWWW) {
global.Symbol = originalSymbol;
}
});

// @gate enableSymbolFallbackForWWW
it('uses the fallback value when in an environment without Symbol', () => {
expect((<div />).$$typeof).toBe(0xeac7);
});

it('returns a complete element according to spec', () => {
const element = React.createElement(ComponentClass);
expect(element.type).toBe(ComponentClass);
Expand Down Expand Up @@ -280,6 +301,42 @@ describe('ReactElement', () => {
expect(element.type.someStaticMethod()).toBe('someReturnValue');
});

// NOTE: We're explicitly not using JSX here. This is intended to test
// classic JS without JSX.
// @gate enableSymbolFallbackForWWW
it('identifies valid elements', () => {
class Component extends React.Component {
render() {
return React.createElement('div');
}
}

expect(React.isValidElement(React.createElement('div'))).toEqual(true);
expect(React.isValidElement(React.createElement(Component))).toEqual(true);

expect(React.isValidElement(null)).toEqual(false);
expect(React.isValidElement(true)).toEqual(false);
expect(React.isValidElement({})).toEqual(false);
expect(React.isValidElement('string')).toEqual(false);
if (!__EXPERIMENTAL__) {
let factory;
expect(() => {
factory = React.createFactory('div');
}).toWarnDev(
'Warning: React.createFactory() is deprecated and will be removed in a ' +
'future major release. Consider using JSX or use React.createElement() ' +
'directly instead.',
{withoutStack: true},
);
expect(React.isValidElement(factory)).toEqual(false);
}
expect(React.isValidElement(Component)).toEqual(false);
expect(React.isValidElement({type: 'div', props: {}})).toEqual(false);

const jsonElement = JSON.stringify(React.createElement('div'));
expect(React.isValidElement(JSON.parse(jsonElement))).toBe(true);
});

// NOTE: We're explicitly not using JSX here. This is intended to test
// classic JS without JSX.
it('is indistinguishable from a plain object', () => {
Expand Down Expand Up @@ -397,6 +454,7 @@ describe('ReactElement', () => {

// NOTE: We're explicitly not using JSX here. This is intended to test
// classic JS without JSX.
// @gate !enableSymbolFallbackForWWW
it('identifies elements, but not JSON, if Symbols are supported', () => {
class Component extends React.Component {
render() {
Expand Down Expand Up @@ -429,4 +487,58 @@ describe('ReactElement', () => {
const jsonElement = JSON.stringify(React.createElement('div'));
expect(React.isValidElement(JSON.parse(jsonElement))).toBe(false);
});

// NOTE: We're explicitly not using JSX here. This is intended to test
// classic JS without JSX.
it('identifies elements, but not JSON, if Symbols are supported (with polyfill)', () => {
// Rudimentary polyfill
// Once all jest engines support Symbols natively we can swap this to test
// WITH native Symbols by default.
const REACT_ELEMENT_TYPE = function() {}; // fake Symbol
const OTHER_SYMBOL = function() {}; // another fake Symbol
global.Symbol = function(name) {
return OTHER_SYMBOL;
};
global.Symbol.for = function(key) {
if (key === 'react.element') {
return REACT_ELEMENT_TYPE;
}
return OTHER_SYMBOL;
};

jest.resetModules();

React = require('react');

class Component extends React.Component {
render() {
return React.createElement('div');
}
}

expect(React.isValidElement(React.createElement('div'))).toEqual(true);
expect(React.isValidElement(React.createElement(Component))).toEqual(true);

expect(React.isValidElement(null)).toEqual(false);
expect(React.isValidElement(true)).toEqual(false);
expect(React.isValidElement({})).toEqual(false);
expect(React.isValidElement('string')).toEqual(false);
if (!__EXPERIMENTAL__) {
let factory;
expect(() => {
factory = React.createFactory('div');
}).toWarnDev(
'Warning: React.createFactory() is deprecated and will be removed in a ' +
'future major release. Consider using JSX or use React.createElement() ' +
'directly instead.',
{withoutStack: true},
);
expect(React.isValidElement(factory)).toEqual(false);
}
expect(React.isValidElement(Component)).toEqual(false);
expect(React.isValidElement({type: 'div', props: {}})).toEqual(false);

const jsonElement = JSON.stringify(React.createElement('div'));
expect(React.isValidElement(JSON.parse(jsonElement))).toBe(false);
});
});
113 changes: 113 additions & 0 deletions packages/react/src/__tests__/ReactElementJSX-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

'use strict';

import {enableSymbolFallbackForWWW} from 'shared/ReactFeatureFlags';

let React;
let ReactDOM;
let ReactTestUtils;
Expand All @@ -20,16 +22,31 @@ let JSXDEVRuntime;
// A lot of these tests are pulled from ReactElement-test because
// this api is meant to be backwards compatible.
describe('ReactElement.jsx', () => {
let originalSymbol;

beforeEach(() => {
jest.resetModules();

if (enableSymbolFallbackForWWW) {
// Delete the native Symbol if we have one to ensure we test the
// unpolyfilled environment.
originalSymbol = global.Symbol;
global.Symbol = undefined;
}

React = require('react');
JSXRuntime = require('react/jsx-runtime');
JSXDEVRuntime = require('react/jsx-dev-runtime');
ReactDOM = require('react-dom');
ReactTestUtils = require('react-dom/test-utils');
});

afterEach(() => {
if (enableSymbolFallbackForWWW) {
global.Symbol = originalSymbol;
}
});

it('allows static methods to be called using the type property', () => {
class StaticMethodComponentClass extends React.Component {
render() {
Expand All @@ -42,6 +59,48 @@ describe('ReactElement.jsx', () => {
expect(element.type.someStaticMethod()).toBe('someReturnValue');
});

// @gate enableSymbolFallbackForWWW
it('identifies valid elements', () => {
class Component extends React.Component {
render() {
return JSXRuntime.jsx('div', {});
}
}

expect(React.isValidElement(JSXRuntime.jsx('div', {}))).toEqual(true);
expect(React.isValidElement(JSXRuntime.jsx(Component, {}))).toEqual(true);
expect(
React.isValidElement(JSXRuntime.jsx(JSXRuntime.Fragment, {})),
).toEqual(true);
if (__DEV__) {
expect(React.isValidElement(JSXDEVRuntime.jsxDEV('div', {}))).toEqual(
true,
);
}

expect(React.isValidElement(null)).toEqual(false);
expect(React.isValidElement(true)).toEqual(false);
expect(React.isValidElement({})).toEqual(false);
expect(React.isValidElement('string')).toEqual(false);
if (!__EXPERIMENTAL__) {
let factory;
expect(() => {
factory = React.createFactory('div');
}).toWarnDev(
'Warning: React.createFactory() is deprecated and will be removed in a ' +
'future major release. Consider using JSX or use React.createElement() ' +
'directly instead.',
{withoutStack: true},
);
expect(React.isValidElement(factory)).toEqual(false);
}
expect(React.isValidElement(Component)).toEqual(false);
expect(React.isValidElement({type: 'div', props: {}})).toEqual(false);

const jsonElement = JSON.stringify(JSXRuntime.jsx('div', {}));
expect(React.isValidElement(JSON.parse(jsonElement))).toBe(true);
});

it('is indistinguishable from a plain object', () => {
const element = JSXRuntime.jsx('div', {className: 'foo'});
const object = {};
Expand Down Expand Up @@ -235,6 +294,7 @@ describe('ReactElement.jsx', () => {
);
});

// @gate !enableSymbolFallbackForWWW
it('identifies elements, but not JSON, if Symbols are supported', () => {
class Component extends React.Component {
render() {
Expand Down Expand Up @@ -276,6 +336,59 @@ describe('ReactElement.jsx', () => {
expect(React.isValidElement(JSON.parse(jsonElement))).toBe(false);
});

it('identifies elements, but not JSON, if Symbols are polyfilled', () => {
// Rudimentary polyfill
// Once all jest engines support Symbols natively we can swap this to test
// WITH native Symbols by default.
const REACT_ELEMENT_TYPE = function() {}; // fake Symbol
const OTHER_SYMBOL = function() {}; // another fake Symbol
global.Symbol = function(name) {
return OTHER_SYMBOL;
};
global.Symbol.for = function(key) {
if (key === 'react.element') {
return REACT_ELEMENT_TYPE;
}
return OTHER_SYMBOL;
};

jest.resetModules();

React = require('react');
JSXRuntime = require('react/jsx-runtime');

class Component extends React.Component {
render() {
return JSXRuntime.jsx('div');
}
}

expect(React.isValidElement(JSXRuntime.jsx('div', {}))).toEqual(true);
expect(React.isValidElement(JSXRuntime.jsx(Component, {}))).toEqual(true);

expect(React.isValidElement(null)).toEqual(false);
expect(React.isValidElement(true)).toEqual(false);
expect(React.isValidElement({})).toEqual(false);
expect(React.isValidElement('string')).toEqual(false);
if (!__EXPERIMENTAL__) {
let factory;
expect(() => {
factory = React.createFactory('div');
}).toWarnDev(
'Warning: React.createFactory() is deprecated and will be removed in a ' +
'future major release. Consider using JSX or use React.createElement() ' +
'directly instead.',
{withoutStack: true},
);
expect(React.isValidElement(factory)).toEqual(false);
}
expect(React.isValidElement(Component)).toEqual(false);
expect(React.isValidElement({type: 'div', props: {}})).toEqual(false);

const jsonElement = JSON.stringify(JSXRuntime.jsx('div', {}));
expect(React.isValidElement(JSON.parse(jsonElement))).toBe(false);
});

it('should warn when unkeyed children are passed to jsx', () => {
const container = document.createElement('div');

Expand Down
3 changes: 3 additions & 0 deletions packages/shared/ReactFeatureFlags.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ export const enablePersistentOffscreenHostContainer = false;
// like migrating internal callers or performance testing.
// -----------------------------------------------------------------------------

// This is blocked on adding a symbol polyfill to www.
export const enableSymbolFallbackForWWW = false;

// This rolled out to 10% public in www, so we should be able to land, but some
// internal tests need to be updated. The open source behavior is correct.
export const skipUnmountedBoundaries = true;
Expand Down
Loading

0 comments on commit 4175f05

Please sign in to comment.