import { normalize } from '@angular-devkit/core';
import * as path from 'path';
import RunCommandsBuilder from './run-commands.builder';
import { fileSync } from 'tmp';
import { readFileSync } from 'fs';

function readFile(f: string) {
  return readFileSync(f)
    .toString()
    .replace(/\s/g, '');
}

describe('Command Runner Builder', () => {
  let builder: RunCommandsBuilder;

  beforeEach(() => {
    builder = new RunCommandsBuilder();
  });

  it('should error when no commands are given', async () => {
    const root = normalize('/root');
    try {
      const result = await builder
        .run({
          root,
          builder: '@nrwl/run-commands',
          projectType: 'application',
          options: {} as any
        })
        .toPromise();
      fail('should throw');
    } catch (e) {
      expect(e).toEqual(
        `ERROR: Bad builder config for @nrwl/run-command - "commands" option is required`
      );
    }
  });

  it('should error when no command is given', async () => {
    const root = normalize('/root');
    try {
      const result = await builder
        .run({
          root,
          builder: '@nrwl/run-commands',
          projectType: 'application',
          options: {
            commands: [{}] as any
          }
        })
        .toPromise();
      fail('should throw');
    } catch (e) {
      expect(e).toEqual(
        `ERROR: Bad builder config for @nrwl/run-command - "command" option is required`
      );
    }
  });

  describe('no readyCondition', () => {
    it('should run commands serially', async () => {
      const root = normalize('/root');
      const f = fileSync().name;
      const result = await builder
        .run({
          root,
          builder: '@nrwl/run-commands',
          projectType: 'application',
          options: {
            commands: [
              {
                command: `sleep 0.2 && echo 1 >> ${f}`
              },
              {
                command: `sleep 0.1 && echo 2 >> ${f}`
              }
            ],
            parallel: false
          }
        })
        .toPromise();

      expect(result).toEqual({ success: true });
      expect(readFile(f)).toEqual('12');
    });

    it('should run commands in parallel', async () => {
      const root = normalize('/root');
      const f = fileSync().name;
      const result = await builder
        .run({
          root,
          builder: '@nrwl/run-commands',
          projectType: 'application',
          options: {
            commands: [
              {
                command: `sleep 0.2 && echo 1 >> ${f}`
              },
              {
                command: `sleep 0.1 && echo 2 >> ${f}`
              }
            ],
            parallel: true
          }
        })
        .toPromise();

      expect(result).toEqual({ success: true });
      expect(readFile(f)).toEqual('21');
    });
  });

  describe('readyWhen', () => {
    it('should error when parallel = false', async () => {
      const root = normalize('/root');
      try {
        const result = await builder
          .run({
            root,
            builder: '@nrwl/run-commands',
            projectType: 'application',
            options: {
              commands: [{ command: 'some command' }],
              parallel: false,
              readyWhen: 'READY'
            }
          })
          .toPromise();
        fail('should throw');
      } catch (e) {
        expect(e).toEqual(
          `ERROR: Bad builder config for @nrwl/run-command - "readyWhen" can only be used when parallel=true`
        );
      }
    });

    it('should return success true when the string specified is ready condition is found', async done => {
      const root = normalize('/root');
      const f = fileSync().name;
      let successEmitted = false;
      builder
        .run({
          root,
          builder: '@nrwl/run-commands',
          projectType: 'application',
          options: {
            commands: [
              {
                command: `echo READY && sleep 0.1 && echo 1 >> ${f}`
              }
            ],
            parallel: true,
            readyWhen: 'READY'
          }
        })
        .subscribe(result => {
          successEmitted = true;
          expect(result).toEqual({ success: true });
          expect(readFile(f)).toEqual('');
        });

      setTimeout(() => {
        if (!successEmitted) {
          fail('Success must be emitted');
        }
        expect(readFile(f)).toEqual('1');
        done();
      }, 150);
    });
  });

  it('should stop execution when a command fails', async () => {
    const root = normalize('/root');
    const f = fileSync().name;
    const result = await builder
      .run({
        root,
        builder: '@nrwl/run-commands',
        projectType: 'application',
        options: {
          commands: [
            {
              command: `echo 1 >> ${f} && exit 1`
            },
            {
              command: `echo 2 >> ${f}`
            }
          ]
        }
      })
      .toPromise();

    expect(result).toEqual({ success: false });
    expect(readFile(f)).toEqual('1');
  });
});
