import {
  readProjectConfiguration,
  stripIndents,
  Tree,
  updateProjectConfiguration,
  updateJson,
  readJson,
} from '@nx/devkit';
import { createTree, createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { libraryGenerator as workspaceLib } from '@nx/js';
import {
  updateExportsJestConfig,
  updateRootFiles,
  updateToDefaultExport,
} from './update-exports-jest-config';

describe('Jest Migration (v14.1.2)', () => {
  let tree: Tree;
  beforeEach(() => {
    tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
  });

  it('should update root jest files', () => {
    tree.write(
      'jest.config.ts',
      stripIndents`
      const { getJestProjects } = require('@nrwl/jest');

      module.exports = {
        projects: getJestProjects()
      };`
    );

    tree.write(
      'jest.preset.ts',
      stripIndents`
      const nxPreset = require('@nrwl/jest/preset');

      module.exports = { ...nxPreset };`
    );

    const status = updateRootFiles(tree);

    expect(status).toEqual({ didUpdateRootPreset: true });
    expect(tree.read('jest.config.ts', 'utf-8')).toEqual(stripIndents`
      const { getJestProjects } = require('@nrwl/jest');

      export default {
        projects: getJestProjects()
      };
    `);
    expect(tree.read('jest.preset.js', 'utf-8')).toEqual(stripIndents`
      const nxPreset = require('@nrwl/jest/preset').default;

      module.exports = { ...nxPreset };`);
  });

  it('should update individual project jest configs', async () => {
    await workspaceLib(tree, { name: 'lib-one' });
    const projectConfiguration = readProjectConfiguration(tree, 'lib-one');
    updateProjectConfiguration(tree, 'lib-one', {
      ...projectConfiguration,
      targets: {
        ...projectConfiguration.targets,
        test: {
          ...projectConfiguration.targets.test,
          executor: '@nrwl/jest:jest',
        },
      },
    });
    tree.rename('jest.preset.js', 'jest.preset.ts');
    tree.write(
      'libs/lib-one/jest.config.ts',
      `
const nxPreset = require('@nrwl/jest/preset');
const someOtherImport = require('../something/else.js');
module.exports = {
  ...someOtherImport,
  ...nxPreset,
  displayName: 'lib-one',
  preset: '../../jest.preset.ts',
  setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
  globals: {
    'ts-jest': {
      tsconfig: '<rootDir>/tsconfig.spec.json',
      stringifyContentPathRegex: '\\\\.(html|svg)$',
    },
  },
  coverageDirectory: '../../coverage/apps/lib-one',
  transform: {
    '^.+\\\\.(ts|mjs|js|html)$': 'jest-preset-angular',
  },
  transformIgnorePatterns: ['node_modules/(?!.*\\\\.mjs$)'],
  snapshotSerializers: [
    'jest-preset-angular/build/serializers/no-ng-attributes',
    'jest-preset-angular/build/serializers/ng-snapshot',
    'jest-preset-angular/build/serializers/html-comment',
  ],
};
`
    );
    updateJson(tree, 'package.json', (json) => {
      delete json.devDependencies['ts-node'];
      return json;
    });
    expect(
      readJson(tree, 'package.json').devDependencies['ts-node']
    ).toBeUndefined();
    updateExportsJestConfig(tree);

    const config = tree.read('libs/lib-one/jest.config.ts', 'utf-8');
    expect(readJson(tree, 'package.json').devDependencies['ts-node']).toEqual(
      '10.9.1'
    );
    expect(config).toMatchSnapshot();
  });

  it('should work with multiple configurations', async () => {
    await workspaceLib(tree, { name: 'lib-one' });
    tree.rename('jest.preset.js', 'jest.preset.ts');
    updateProjectConfiguration(tree, 'lib-one', {
      ...readProjectConfiguration(tree, 'lib-one'),
      targets: {
        test: {
          executor: '@nrwl/jest:jest',
          options: {
            jestConfig: 'libs/lib-one/jest.config.ts',
            passWithoutTests: true,
          },
          configurations: {
            production: {
              silent: true,
            },
          },
        },
      },
    });

    tree.write(
      'libs/lib-one/jest.config.ts',
      `
const nxPreset = require('@nrwl/jest/preset');
const someOtherImport = require('../something/else.js');
module.exports = {
  ...someOtherImport,
  ...nxPreset,
  displayName: 'lib-one',
  preset: '../../jest.preset.ts',
  setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
  globals: {
    'ts-jest': {
      tsconfig: '<rootDir>/tsconfig.spec.json',
      stringifyContentPathRegex: '\\\\.(html|svg)$',
    },
  },
  coverageDirectory: '../../coverage/apps/lib-one',
  transform: {
    '^.+\\\\.(ts|mjs|js|html)$': 'jest-preset-angular',
  },
  transformIgnorePatterns: ['node_modules/(?!.*\\\\.mjs$)'],
  snapshotSerializers: [
    'jest-preset-angular/build/serializers/no-ng-attributes',
    'jest-preset-angular/build/serializers/ng-snapshot',
    'jest-preset-angular/build/serializers/html-comment',
  ],
};
`
    );

    updateExportsJestConfig(tree);

    const config = tree.read('libs/lib-one/jest.config.ts', 'utf-8');
    expect(config).toMatchSnapshot();
    expect(tree.exists('jest.preset.ts')).toBeFalsy();
    expect(tree.exists('jest.preset.js')).toBeTruthy();
  });

  it('should convert module.exports => export default', () => {
    tree = createTree();

    tree.write(
      'jest.config.js',
      stripIndents`
  const { getJestProjects } = require('@nrwl/jest');
  const nxPreset = require('@nrwl/jest/preset');
  
  
  const someFn = () => ({more: 'stuff'});
  module.export.abc = someFn;
  module.exports = {
    ...nxPreset,
    more: 'stuff',
    someFn,
    projects: getJestProjects()
  };`
    );
    updateToDefaultExport(tree, 'jest.config.js');

    expect(tree.read('jest.config.js', 'utf-8')).toMatchSnapshot();
  });
});
