/**
 * 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.
 */

import {tmpdir} from 'os';
import * as path from 'path';
import {onNodeVersions} from '@jest/test-utils';
import {
  cleanup,
  createEmptyPackage,
  extractSummary,
  writeFiles,
} from '../Utils';
import runJest from '../runJest';

const DIR = path.resolve(tmpdir(), 'globalVariables.test');
const TEST_DIR = path.resolve(DIR, '__tests__');

function cleanStderr(stderr: string) {
  const {rest} = extractSummary(stderr);
  return rest.replaceAll(/.*(jest-jasmine2).*\n/g, '');
}

beforeEach(() => {
  cleanup(DIR);
  createEmptyPackage(DIR);
});

afterAll(() => cleanup(DIR));

test('basic test constructs', () => {
  const filename = 'basic.testConstructs.test.js';
  const content = `
    it('it', () => {});
    test('test', () => {});

    describe('describe', () => {
      it('it', () => {});
      test('test', () => {});
    });
  `;

  writeFiles(TEST_DIR, {[filename]: content});
  const {stderr, exitCode} = runJest(DIR);

  const {summary, rest} = extractSummary(stderr);
  expect(rest).toMatchSnapshot();
  expect(summary).toMatchSnapshot();
  expect(exitCode).toBe(0);
});

test('interleaved describe and test children order', () => {
  const filename = 'interleaved.test.js';
  const content = `
    let lastTest;
    test('above', () => {
      try {
        expect(lastTest).toBe(undefined);
      } finally {
        lastTest = 'above';
      }
    });
    describe('describe', () => {
      test('inside', () => {
        try {
          expect(lastTest).toBe('above');
        } finally {
          lastTest = 'inside';
        }
      });
    });
    test('below', () => {
      try {
        expect(lastTest).toBe('inside');
      } finally {
        lastTest = 'below';
      }
    });
  `;

  writeFiles(TEST_DIR, {[filename]: content});
  const {stderr, exitCode} = runJest(DIR);

  const {summary, rest} = extractSummary(stderr);
  expect(rest).toMatchSnapshot();
  expect(summary).toMatchSnapshot();
  expect(exitCode).toBe(0);
});

test('skips', () => {
  const filename = 'skipsConstructs.test.js';
  const content = `
    it('it', () => {});
    xtest('xtest', () => {});
    xit('xit', () => {});
    it.skip('it.skip', () => {});
    test.skip('test.skip', () => {});

    xdescribe('xdescribe', () => {
      it('it', () => {});
      test('test', () => {});
    });

    describe.skip('describe.skip', () => {
      test('test', () => {});
      describe('describe', () => {
        test('test', () => {});
      });
    });
  `;

  writeFiles(TEST_DIR, {[filename]: content});
  const {stderr, exitCode} = runJest(DIR);

  const {summary, rest} = extractSummary(stderr);
  expect(rest).toMatchSnapshot();
  expect(summary).toMatchSnapshot();
  expect(exitCode).toBe(0);
});

test('only', () => {
  const filename = 'onlyConstructs.test.js';
  const content = `
    it('it', () => {});
    test.only('test.only', () => {});
    it.only('it.only', () => {});
    fit('fit', () => {});

    fdescribe('fdescribe', () => {
      it('it', () => {});
      test('test', () => {});
    });

    describe.only('describe.only', () => {
      test('test', () => {});
      describe('describe', () => {
        test('test', () => {});
      });
    });
  `;

  writeFiles(TEST_DIR, {[filename]: content});
  const {stderr, exitCode} = runJest(DIR);

  const {summary, rest} = extractSummary(stderr);
  expect(rest).toMatchSnapshot();
  expect(summary).toMatchSnapshot();
  expect(exitCode).toBe(0);
});

test('cannot have describe with no implementation', () => {
  const filename = 'onlyConstructs.test.js';
  const content = `
    describe('describe, no implementation');
  `;

  writeFiles(TEST_DIR, {[filename]: content});
  const {stderr, exitCode} = runJest(DIR);

  const rest = cleanStderr(stderr);
  const {summary} = extractSummary(stderr);

  expect(rest).toMatchSnapshot();
  expect(summary).toMatchSnapshot();
  expect(exitCode).toBe(1);
});

test('cannot test with no implementation', () => {
  const filename = 'onlyConstructs.test.js';
  const content = `
    it('it', () => {});
    it('it, no implementation');
    test('test, no implementation');
  `;

  writeFiles(TEST_DIR, {[filename]: content});
  const {stderr, exitCode} = runJest(DIR);

  const {summary} = extractSummary(stderr);
  expect(cleanStderr(stderr)).toMatchSnapshot();
  expect(summary).toMatchSnapshot();
  expect(exitCode).toBe(1);
});

test('skips with expand arg', () => {
  const filename = 'skipsConstructs.test.js';
  const content = `
    it('it', () => {});
    xtest('xtest', () => {});
    xit('xit', () => {});
    it.skip('it.skip', () => {});
    test.skip('test.skip', () => {});

    xdescribe('xdescribe', () => {
      it('it', () => {});
      test('test', () => {});
    });

    describe.skip('describe.skip', () => {
      test('test', () => {});
      describe('describe', () => {
        test('test', () => {});
      });
    });
  `;

  writeFiles(TEST_DIR, {[filename]: content});
  const {stderr, exitCode} = runJest(DIR, ['--expand']);

  const {summary, rest} = extractSummary(stderr);
  expect(rest).toMatchSnapshot();
  expect(summary).toMatchSnapshot();
  expect(exitCode).toBe(0);
});

test('only with expand arg', () => {
  const filename = 'onlyConstructs.test.js';
  const content = `
    it('it', () => {});
    test.only('test.only', () => {});
    it.only('it.only', () => {});
    fit('fit', () => {});

    fdescribe('fdescribe', () => {
      it('it', () => {});
      test('test', () => {});
    });

    describe.only('describe.only', () => {
      test('test', () => {});
      describe('describe', () => {
        test('test', () => {});
      });
    });
  `;

  writeFiles(TEST_DIR, {[filename]: content});
  const {stderr, exitCode} = runJest(DIR, ['--expand']);

  const {summary, rest} = extractSummary(stderr);
  expect(rest).toMatchSnapshot();
  expect(summary).toMatchSnapshot();
  expect(exitCode).toBe(0);
});

test('cannot test with no implementation with expand arg', () => {
  const filename = 'onlyConstructs.test.js';
  const content = `
    it('it', () => {});
    it('it, no implementation');
    test('test, no implementation');
  `;

  writeFiles(TEST_DIR, {[filename]: content});
  const {stderr, exitCode} = runJest(DIR, ['--expand']);

  const {summary} = extractSummary(stderr);
  expect(cleanStderr(stderr)).toMatchSnapshot();
  expect(summary).toMatchSnapshot();
  expect(exitCode).toBe(1);
});

test('function as describe() descriptor', () => {
  const filename = 'functionAsDescriptor.test.js';
  const content = `
    function Foo() {}
    describe(Foo, () => {
      it('it', () => {});
    });
  `;

  writeFiles(TEST_DIR, {[filename]: content});
  const {stderr, exitCode} = runJest(DIR);

  const {summary, rest} = extractSummary(stderr);
  expect(rest).toMatchSnapshot();
  expect(summary).toMatchSnapshot();
  expect(exitCode).toBe(0);
});

test('function as it() descriptor', () => {
  const filename = 'functionAsDescriptor.test.js';
  const content = `
    function Foo() {}
    it(Foo, () => {});
  `;

  writeFiles(TEST_DIR, {[filename]: content});
  const {stderr, exitCode} = runJest(DIR);

  const {summary, rest} = extractSummary(stderr);
  expect(rest).toMatchSnapshot();
  expect(summary).toMatchSnapshot();
  expect(exitCode).toBe(0);
});

onNodeVersions('^18.18.0 || >=20.4.0', () => {
  test("Symbol's `dispose` are available", () => {
    const filename = 'symbolDispose.test.js';
    const content = `
    it('test', () => {
      expect(Symbol.dispose).toBeDefined();
      expect(Symbol.asyncDispose).toBeDefined();
    });
  `;

    writeFiles(TEST_DIR, {[filename]: content});
    const {stderr, exitCode} = runJest(DIR);

    const {summary, rest} = extractSummary(stderr);
    expect(rest).toMatchSnapshot();
    expect(summary).toMatchSnapshot();
    expect(exitCode).toBe(0);
  });
});
