'use strict';

const path = require('path');
const {
  touchFile,
  runMochaAsync,
  runMochaWatchAsync,
  copyFixture,
  DEFAULT_FIXTURE,
  resolveFixturePath,
  createTempDir
} = require('../helpers');

describe('global setup/teardown', function () {
  describe('when mocha run in serial mode', function () {
    it('should execute global setup and teardown', async function () {
      return expect(
        runMochaAsync(DEFAULT_FIXTURE, [
          '--require',
          resolveFixturePath('plugins/global-fixtures/global-setup-teardown')
        ]),
        'when fulfilled',
        'to have passed'
      );
    });

    describe('when only global teardown is supplied', function () {
      it('should run global teardown', async function () {
        return expect(
          runMochaAsync(DEFAULT_FIXTURE, [
            '--require',
            resolveFixturePath('plugins/global-fixtures/global-teardown')
          ]),
          'when fulfilled',
          'to contain once',
          /teardown schmeardown/
        );
      });
    });

    describe('when only global setup is supplied', function () {
      it('should run global setup', async function () {
        return expect(
          runMochaAsync(DEFAULT_FIXTURE, [
            '--require',
            resolveFixturePath('plugins/global-fixtures/global-setup')
          ]),
          'when fulfilled',
          'to contain once',
          /setup schmetup/
        );
      });
    });

    it('should share context', async function () {
      return expect(
        runMochaAsync(DEFAULT_FIXTURE, [
          '--require',
          resolveFixturePath('plugins/global-fixtures/global-setup-teardown')
        ]),
        'when fulfilled',
        'to contain once',
        /setup: this\.foo = bar[\s\S]+teardown: this\.foo = bar/
      );
    });

    describe('when supplied multiple functions', function () {
      it('should execute them sequentially', async function () {
        return expect(
          runMochaAsync(DEFAULT_FIXTURE, [
            '--require',
            resolveFixturePath(
              'plugins/global-fixtures/global-setup-teardown-multiple'
            )
          ]),
          'when fulfilled',
          'to contain once',
          /teardown: this.foo = 3/
        );
      });
    });

    describe('when run in watch mode', function () {
      let tempDir;
      let testFile;
      let removeTempDir;

      beforeEach(async function () {
        const tempInfo = await createTempDir();
        tempDir = tempInfo.dirpath;
        removeTempDir = tempInfo.removeTempDir;
        testFile = path.join(tempDir, 'test.js');
        copyFixture(DEFAULT_FIXTURE, testFile);
      });

      afterEach(async function () {
        if (removeTempDir) {
          return removeTempDir();
        }
      });

      it('should execute global setup and teardown', async function () {
        return expect(
          runMochaWatchAsync(
            [
              '--require',
              resolveFixturePath(
                'plugins/global-fixtures/global-setup-teardown'
              ),
              testFile
            ],
            tempDir,
            () => {
              touchFile(testFile);
            }
          ),
          'when fulfilled',
          'to have passed'
        );
      });

      describe('when only global teardown is supplied', function () {
        it('should run global teardown', async function () {
          return expect(
            runMochaWatchAsync(
              [
                '--require',
                resolveFixturePath('plugins/global-fixtures/global-teardown'),
                testFile
              ],
              tempDir,
              () => {
                touchFile(testFile);
              }
            ),
            'when fulfilled',
            'to contain once',
            /teardown schmeardown/
          );
        });
      });

      describe('when only global setup is supplied', function () {
        it('should run global setup', async function () {
          return expect(
            runMochaWatchAsync(
              [
                '--require',
                resolveFixturePath('plugins/global-fixtures/global-setup'),
                testFile
              ],
              tempDir,
              () => {
                touchFile(testFile);
              }
            ),
            'when fulfilled',
            'to contain once',
            /setup schmetup/
          );
        });
      });

      it('should not re-execute the global fixtures', async function () {
        return expect(
          runMochaWatchAsync(
            [
              '--require',
              resolveFixturePath(
                'plugins/global-fixtures/global-setup-teardown-multiple'
              ),
              testFile
            ],
            tempDir,
            () => {
              touchFile(testFile);
            }
          ),
          'when fulfilled',
          'to contain once',
          /teardown: this.foo = 3/
        );
      });
    });
  });

  describe('when mocha run in parallel mode', function () {
    it('should execute global setup and teardown', async function () {
      return expect(
        runMochaAsync(DEFAULT_FIXTURE, [
          '--parallel',
          '--require',
          resolveFixturePath('plugins/global-fixtures/global-setup-teardown')
        ]),
        'when fulfilled',
        'to have passed'
      );
    });

    it('should share context', async function () {
      return expect(
        runMochaAsync(DEFAULT_FIXTURE, [
          '--parallel',
          '--require',
          resolveFixturePath('plugins/global-fixtures/global-setup-teardown')
        ]),
        'when fulfilled',
        'to contain once',
        /setup: this.foo = bar/
      ).and('when fulfilled', 'to contain once', /teardown: this.foo = bar/);
    });

    describe('when supplied multiple functions', function () {
      it('should execute them sequentially', async function () {
        return expect(
          runMochaAsync(DEFAULT_FIXTURE, [
            '--parallel',
            '--require',
            resolveFixturePath(
              'plugins/global-fixtures/global-setup-teardown-multiple'
            )
          ]),
          'when fulfilled',
          'to contain once',
          /teardown: this.foo = 3/
        );
      });
    });

    describe('when run in watch mode', function () {
      let tempDir;
      let testFile;
      let removeTempDir;

      beforeEach(async function () {
        const tempInfo = await createTempDir();
        tempDir = tempInfo.dirpath;
        removeTempDir = tempInfo.removeTempDir;
        testFile = path.join(tempDir, 'test.js');
        copyFixture(DEFAULT_FIXTURE, testFile);
      });

      afterEach(async function () {
        if (removeTempDir) {
          return removeTempDir();
        }
      });

      it('should execute global setup and teardown', async function () {
        return expect(
          runMochaWatchAsync(
            [
              '--parallel',
              '--require',
              resolveFixturePath(
                'plugins/global-fixtures/global-setup-teardown'
              ),
              testFile
            ],
            tempDir,
            () => {
              touchFile(testFile);
            }
          ),
          'when fulfilled',
          'to have passed'
        );
      });

      it('should not re-execute the global fixtures', async function () {
        return expect(
          runMochaWatchAsync(
            [
              '--parallel',
              '--require',
              resolveFixturePath(
                'plugins/global-fixtures/global-setup-teardown-multiple'
              ),
              testFile
            ],
            tempDir,
            () => {
              touchFile(testFile);
            }
          ),
          'when fulfilled',
          'to contain once',
          /teardown: this.foo = 3/
        );
      });
    });
  });
});
