import { getCompiler, compile } from './helpers';

describe('validate options', () => {
  const tests = {
    type: {
      success: ['commonjs', 'module'],
      failure: [true, false, 'foo'],
    },
    exports: {
      success: [
        'Foo',
        'default Foo',
        'named Foo',
        'named Foo FooA',
        'named|Foo FooA',
        'named|Foo|FooA',
        ['named Foo', 'named Bar'],
        ['named Foo FooA', 'named Bar BarA'],
        ['default Foo', 'named Bar BarA'],
        'single Foo',
        'multiple Foo',
        'multiple Foo FooA',
        ['multiple Foo', 'multiple Bar'],
        ['multiple Foo FooA', 'multiple Bar BarA'],
        { name: 'Foo' },
        { syntax: 'default', name: 'Foo' },
        { syntax: 'named', name: 'Foo' },
        { syntax: 'single', name: 'Foo' },
        { syntax: 'multiple', name: 'Foo' },
        { syntax: 'named', name: 'Foo', alias: 'FooA' },
        { syntax: 'multiple', name: 'Foo', alias: 'FooA' },
        ['Foo', { syntax: 'default', name: 'Bar' }],
        [
          { syntax: 'named', name: 'Foo' },
          { syntax: 'named', name: 'Bar' },
        ],
      ],
      failure: [
        true,
        () => {},
        [1, 2, 3],
        { syntax: 'default' },
        { alias: 'FooA' },
        { syntax: 'foo', name: 'Foo' },
        { name: '' },
        { name: 'Foo', alias: '' },
      ],
    },
  };

  function stringifyValue(value) {
    if (
      Array.isArray(value) ||
      (value && typeof value === 'object' && value.constructor === Object)
    ) {
      return JSON.stringify(value);
    }

    return value;
  }

  async function createTestCase(key, value, type) {
    it(`should ${
      type === 'success' ? 'successfully validate' : 'throw an error on'
    } the "${key}" option with "${stringifyValue(value)}" value`, async () => {
      // eslint-disable-next-line no-shadow
      const getOptions = (key, value) => {
        if (key === 'type') {
          return { [key]: value, exports: 'Foo' };
        }

        if (key === 'exports') {
          let isModule = false;

          if (typeof value === 'string') {
            isModule = value.includes('default') || value.includes('named');
          } else if (Array.isArray(value)) {
            isModule =
              value.filter((item) =>
                typeof item === 'string'
                  ? item.includes('default') || item.includes('named')
                  : item.syntax
                  ? item.syntax === 'default' || item.syntax === 'named'
                  : false
              ).length > 0;
          } else {
            isModule =
              value.syntax &&
              (value.syntax === 'default' || value.syntax === 'named');
          }

          return { type: isModule ? 'module' : 'commonjs', [key]: value };
        }

        return { [key]: value };
      };

      const compiler = getCompiler('simple.js', getOptions(key, value));

      let stats;

      try {
        stats = await compile(compiler);
      } finally {
        if (type === 'success') {
          expect(stats.hasErrors()).toBe(false);
        } else if (type === 'failure') {
          const {
            compilation: { errors },
          } = stats;

          expect(errors).toHaveLength(1);
          expect(() => {
            throw new Error(errors[0].error.message);
          }).toThrowErrorMatchingSnapshot();
        }
      }
    });
  }

  for (const [key, values] of Object.entries(tests)) {
    for (const type of Object.keys(values)) {
      for (const value of values[type]) {
        createTestCase(key, value, type);
      }
    }
  }
});
