/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @emails react-core
 * @jest-environment node
 */

'use strict';

let React;
let ReactNoop;
let waitForAll;

// This is a new feature in Fiber so I put it in its own test file. It could
// probably move to one of the other test files once it is official.
describe('ReactTopLevelFragment', function () {
  beforeEach(function () {
    jest.resetModules();
    React = require('react');
    ReactNoop = require('react-noop-renderer');

    const InternalTestUtils = require('internal-test-utils');
    waitForAll = InternalTestUtils.waitForAll;
  });

  it('should render a simple fragment at the top of a component', async function () {
    function Fragment() {
      return [<div key="a">Hello</div>, <div key="b">World</div>];
    }
    ReactNoop.render(<Fragment />);
    await waitForAll([]);
  });

  it('should preserve state when switching from a single child', async function () {
    let instance = null;

    class Stateful extends React.Component {
      render() {
        instance = this;
        return <div>Hello</div>;
      }
    }

    function Fragment({condition}) {
      return condition ? (
        <Stateful key="a" />
      ) : (
        [<Stateful key="a" />, <div key="b">World</div>]
      );
    }
    ReactNoop.render(<Fragment />);
    await waitForAll([]);

    const instanceA = instance;

    expect(instanceA).not.toBe(null);

    ReactNoop.render(<Fragment condition={true} />);
    await waitForAll([]);

    const instanceB = instance;

    expect(instanceB).toBe(instanceA);
  });

  it('should not preserve state when switching to a nested array', async function () {
    let instance = null;

    class Stateful extends React.Component {
      render() {
        instance = this;
        return <div>Hello</div>;
      }
    }

    function Fragment({condition}) {
      return condition ? (
        <Stateful key="a" />
      ) : (
        [[<Stateful key="a" />, <div key="b">World</div>], <div key="c" />]
      );
    }
    ReactNoop.render(<Fragment />);
    await waitForAll([]);

    const instanceA = instance;

    expect(instanceA).not.toBe(null);

    ReactNoop.render(<Fragment condition={true} />);
    await waitForAll([]);

    const instanceB = instance;

    expect(instanceB).not.toBe(instanceA);
  });

  it('preserves state if an implicit key slot switches from/to null', async function () {
    let instance = null;

    class Stateful extends React.Component {
      render() {
        instance = this;
        return <div>World</div>;
      }
    }

    function Fragment({condition}) {
      return condition
        ? [null, <Stateful key="a" />]
        : [<div key="b">Hello</div>, <Stateful key="a" />];
    }
    ReactNoop.render(<Fragment />);
    await waitForAll([]);

    const instanceA = instance;

    expect(instanceA).not.toBe(null);

    ReactNoop.render(<Fragment condition={true} />);
    await waitForAll([]);

    const instanceB = instance;

    expect(instanceB).toBe(instanceA);

    ReactNoop.render(<Fragment condition={false} />);
    await waitForAll([]);

    const instanceC = instance;

    expect(instanceC === instanceA).toBe(true);
  });

  it('should preserve state in a reorder', async function () {
    let instance = null;

    class Stateful extends React.Component {
      render() {
        instance = this;
        return <div>Hello</div>;
      }
    }

    function Fragment({condition}) {
      return condition
        ? [[<div key="b">World</div>, <Stateful key="a" />]]
        : [[<Stateful key="a" />, <div key="b">World</div>], <div key="c" />];
    }
    ReactNoop.render(<Fragment />);
    await waitForAll([]);

    const instanceA = instance;

    expect(instanceA).not.toBe(null);

    ReactNoop.render(<Fragment condition={true} />);
    await waitForAll([]);

    const instanceB = instance;

    expect(instanceB).toBe(instanceA);
  });
});