it('warns if calculateChangedBits returns larger than a 31-bit integer', () => {
    spyOnDev(console, 'error');

    const Context = React.unstable_createContext(
      0,
      (a, b) => Math.pow(2, 32) - 1, // Return 32 bit int
    );

    function Provider(props) {
      return Context.provide(props.value, props.children);
    }

    ReactNoop.render(<Provider value={1} />);
    ReactNoop.flush();

    // Update
    ReactNoop.render(<Provider value={2} />);
    ReactNoop.flush();

    if (__DEV__) {
      expect(console.error.calls.count()).toBe(1);
      expect(console.error.calls.argsFor(0)[0]).toContain(
        'calculateChangedBits: Expected the return value to be a 31-bit ' +
          'integer. Instead received: 4294967295',
      );
    }
  });
  it('compares context values with Object.is semantics', () => {
    const Context = React.unstable_createContext(1);

    function Provider(props) {
      ReactNoop.yield('Provider');
      return Context.provide(props.value, props.children);
    }

    function Consumer(props) {
      ReactNoop.yield('Consumer');
      return Context.consume(value => {
        ReactNoop.yield('Consumer render prop');
        return <span prop={'Result: ' + value} />;
      });
    }

    class Indirection extends React.Component {
      shouldComponentUpdate() {
        return false;
      }
      render() {
        ReactNoop.yield('Indirection');
        return this.props.children;
      }
    }

    function App(props) {
      ReactNoop.yield('App');
      return (
        <Provider value={props.value}>
          <Indirection>
            <Indirection>
              <Consumer />
            </Indirection>
          </Indirection>
        </Provider>
      );
    }

    ReactNoop.render(<App value={NaN} />);
    expect(ReactNoop.flush()).toEqual([
      'App',
      'Provider',
      'Indirection',
      'Indirection',
      'Consumer',
      'Consumer render prop',
    ]);
    expect(ReactNoop.getChildren()).toEqual([span('Result: NaN')]);

    // Update
    ReactNoop.render(<App value={NaN} />);
    expect(ReactNoop.flush()).toEqual([
      'App',
      'Provider',
      // Consumer should not re-render again
      // 'Consumer render prop',
    ]);
    expect(ReactNoop.getChildren()).toEqual([span('Result: NaN')]);
  });
  it('propagates through shouldComponentUpdate false', () => {
    const Context = React.unstable_createContext(1);

    function Provider(props) {
      ReactNoop.yield('Provider');
      return Context.provide(props.value, props.children);
    }

    function Consumer(props) {
      ReactNoop.yield('Consumer');
      return Context.consume(value => {
        ReactNoop.yield('Consumer render prop');
        return <span prop={'Result: ' + value} />;
      });
    }

    class Indirection extends React.Component {
      shouldComponentUpdate() {
        return false;
      }
      render() {
        ReactNoop.yield('Indirection');
        return this.props.children;
      }
    }

    function App(props) {
      ReactNoop.yield('App');
      return (
        <Provider value={props.value}>
          <Indirection>
            <Indirection>
              <Consumer />
            </Indirection>
          </Indirection>
        </Provider>
      );
    }

    ReactNoop.render(<App value={2} />);
    expect(ReactNoop.flush()).toEqual([
      'App',
      'Provider',
      'Indirection',
      'Indirection',
      'Consumer',
      'Consumer render prop',
    ]);
    expect(ReactNoop.getChildren()).toEqual([span('Result: 2')]);

    // Update
    ReactNoop.render(<App value={3} />);
    expect(ReactNoop.flush()).toEqual([
      'App',
      'Provider',
      'Consumer render prop',
    ]);
    expect(ReactNoop.getChildren()).toEqual([span('Result: 3')]);
  });
  it('context unwinds when interrupted', () => {
    const Context = React.unstable_createContext('Default');

    function Provider(props) {
      return Context.provide(props.value, props.children);
    }

    function Consumer(props) {
      return Context.consume(value => {
        return <span prop={'Result: ' + value} />;
      });
    }

    function BadRender() {
      throw new Error('Bad render');
    }

    class ErrorBoundary extends React.Component {
      state = {error: null};
      componentDidCatch(error) {
        this.setState({error});
      }
      render() {
        if (this.state.error) {
          return null;
        }
        return this.props.children;
      }
    }

    function App(props) {
      return (
        <React.Fragment>
          <Provider value="Does not unwind">
            <ErrorBoundary>
              <Provider value="Unwinds after BadRender throws">
                <BadRender />
              </Provider>
            </ErrorBoundary>
            <Consumer />
          </Provider>
        </React.Fragment>
      );
    }

    ReactNoop.render(<App value="A" />);
    ReactNoop.flush();
    expect(ReactNoop.getChildren()).toEqual([
      // The second provider should use the default value. This proves the
      span('Result: Does not unwind'),
    ]);
  });
  it('nested providers', () => {
    const Context = React.unstable_createContext(1);

    function Provider(props) {
      return Context.consume(contextValue =>
        // Multiply previous context value by 2, unless prop overrides
        Context.provide(props.value || contextValue * 2, props.children),
      );
    }

    function Consumer(props) {
      return Context.consume(value => {
        return <span prop={'Result: ' + value} />;
      });
    }

    class Indirection extends React.Component {
      shouldComponentUpdate() {
        return false;
      }
      render() {
        return this.props.children;
      }
    }

    function App(props) {
      return (
        <Provider value={props.value}>
          <Indirection>
            <Provider>
              <Indirection>
                <Provider>
                  <Indirection>
                    <Consumer />
                  </Indirection>
                </Provider>
              </Indirection>
            </Provider>
          </Indirection>
        </Provider>
      );
    }

    ReactNoop.render(<App value={2} />);
    ReactNoop.flush();
    expect(ReactNoop.getChildren()).toEqual([span('Result: 8')]);

    // Update
    ReactNoop.render(<App value={3} />);
    ReactNoop.flush();
    expect(ReactNoop.getChildren()).toEqual([span('Result: 12')]);
  });
  it('warns if multiple renderers concurrently render the same context', () => {
    spyOnDev(console, 'error');
    const Context = React.unstable_createContext(0);

    function Foo(props) {
      ReactNoop.yield('Foo');
      return null;
    }
    function Provider(props) {
      return Context.provide(props.value, props.children);
    }

    function App(props) {
      return (
        <Provider value={props.value}>
          <Foo />
          <Foo />
        </Provider>
      );
    }

    ReactNoop.render(<App value={1} />);
    // Render past the Provider, but don't commit yet
    ReactNoop.flushThrough(['Foo']);

    // Get a new copy of ReactNoop
    jest.resetModules();
    ReactFeatureFlags = require('shared/ReactFeatureFlags');
    ReactFeatureFlags.enableNewContextAPI = true;
    React = require('react');
    ReactNoop = require('react-noop-renderer');

    // Render the provider again using a different renderer
    ReactNoop.render(<App value={1} />);
    ReactNoop.flush();

    if (__DEV__) {
      expect(console.error.calls.argsFor(0)[0]).toContain(
        'Detected multiple renderers concurrently rendering the same ' +
          'context provider. This is currently unsupported',
      );
    }
  });
  it('simple mount and update', () => {
    const Context = React.unstable_createContext(1);

    function Provider(props) {
      return Context.provide(props.value, props.children);
    }

    function Consumer(props) {
      return Context.consume(value => {
        return <span prop={'Result: ' + value} />;
      });
    }

    const Indirection = React.Fragment;

    function App(props) {
      return (
        <Provider value={props.value}>
          <Indirection>
            <Indirection>
              <Consumer />
            </Indirection>
          </Indirection>
        </Provider>
      );
    }

    ReactNoop.render(<App value={2} />);
    ReactNoop.flush();
    expect(ReactNoop.getChildren()).toEqual([span('Result: 2')]);

    // Update
    ReactNoop.render(<App value={3} />);
    ReactNoop.flush();
    expect(ReactNoop.getChildren()).toEqual([span('Result: 3')]);
  });
 contextKeys.map(key => {
   const Context = React.unstable_createContext(0);
   Context.displayName = 'Context' + key;
   return [key, Context];
 }),
  it('can skip consumers with bitmask', () => {
    const Context = React.unstable_createContext({foo: 0, bar: 0}, (a, b) => {
      let result = 0;
      if (a.foo !== b.foo) {
        result |= 0b01;
      }
      if (a.bar !== b.bar) {
        result |= 0b10;
      }
      return result;
    });

    function Provider(props) {
      return Context.provide({foo: props.foo, bar: props.bar}, props.children);
    }

    function Foo() {
      return Context.consume(value => {
        ReactNoop.yield('Foo');
        return <span prop={'Foo: ' + value.foo} />;
      }, 0b01);
    }

    function Bar() {
      return Context.consume(value => {
        ReactNoop.yield('Bar');
        return <span prop={'Bar: ' + value.bar} />;
      }, 0b10);
    }

    class Indirection extends React.Component {
      shouldComponentUpdate() {
        return false;
      }
      render() {
        return this.props.children;
      }
    }

    function App(props) {
      return (
        <Provider foo={props.foo} bar={props.bar}>
          <Indirection>
            <Indirection>
              <Foo />
            </Indirection>
            <Indirection>
              <Bar />
            </Indirection>
          </Indirection>
        </Provider>
      );
    }

    ReactNoop.render(<App foo={1} bar={1} />);
    expect(ReactNoop.flush()).toEqual(['Foo', 'Bar']);
    expect(ReactNoop.getChildren()).toEqual([span('Foo: 1'), span('Bar: 1')]);

    // Update only foo
    ReactNoop.render(<App foo={2} bar={1} />);
    expect(ReactNoop.flush()).toEqual(['Foo']);
    expect(ReactNoop.getChildren()).toEqual([span('Foo: 2'), span('Bar: 1')]);

    // Update only bar
    ReactNoop.render(<App foo={2} bar={2} />);
    expect(ReactNoop.flush()).toEqual(['Bar']);
    expect(ReactNoop.getChildren()).toEqual([span('Foo: 2'), span('Bar: 2')]);

    // Update both
    ReactNoop.render(<App foo={3} bar={3} />);
    expect(ReactNoop.flush()).toEqual(['Foo', 'Bar']);
    expect(ReactNoop.getChildren()).toEqual([span('Foo: 3'), span('Bar: 3')]);
  });