import {
  ensureProject,
  newApp,
  newLib,
  readFile,
  readJson,
  runCommand,
  runsInWSL,
  uniq,
  updateFile
} from '../utils';

describe('Affected', () => {
  it('should print, build, and test affected apps', () => {
    ensureProject();
    const myapp = uniq('myapp');
    const myapp2 = uniq('myapp2');
    const mylib = uniq('mylib');
    const mylib2 = uniq('mylib2');
    const mypublishablelib = uniq('mypublishablelib');
    newApp(myapp);
    newApp(myapp2);
    newLib(`${mylib} --framework angular`);
    newLib(`${mylib2} --framework angular`);
    newLib(`${mypublishablelib} --framework angular --publishable`);

    updateFile(
      `apps/${myapp}/src/app/app.component.spec.ts`,
      `
            import '@proj/${mylib}';
            describe('sample test', () => {
              it('should test', () => {
                expect(1).toEqual(1);
              });
            });
          `
    );
    updateFile(
      `libs/${mypublishablelib}/src/lib/${mypublishablelib}.module.spec.ts`,
      `
            import '@proj/${mylib}';
            describe('sample test', () => {
              it('should test', () => {
                expect(1).toEqual(1);
              });
            });
          `
    );

    const affectedApps = runCommand(
      `npm run affected:apps -- --files="libs/${mylib}/src/index.ts"`
    );
    expect(affectedApps).toContain(myapp);
    expect(affectedApps).not.toContain(myapp2);
    expect(affectedApps).not.toContain(`${myapp}-e2e`);

    const implicitlyAffectedApps = runCommand(
      'npm run affected:apps -- --files="package.json"'
    );
    expect(implicitlyAffectedApps).toContain(myapp);
    expect(implicitlyAffectedApps).toContain(myapp2);

    const noAffectedApps = runCommand(
      'npm run affected:apps -- --files="README.md"'
    );
    expect(noAffectedApps).not.toContain(myapp);
    expect(noAffectedApps).not.toContain(myapp2);

    const affectedLibs = runCommand(
      `npm run affected:libs -- --files="libs/${mylib}/src/index.ts"`
    );
    expect(affectedLibs).toContain(mypublishablelib);
    expect(affectedLibs).toContain(mylib);
    expect(affectedLibs).not.toContain(mylib2);

    const implicitlyAffectedLibs = runCommand(
      'npm run affected:libs -- --files="package.json"'
    );
    expect(implicitlyAffectedLibs).toContain(mypublishablelib);
    expect(implicitlyAffectedLibs).toContain(mylib);
    expect(implicitlyAffectedLibs).toContain(mylib2);

    const noAffectedLibs = runCommand(
      'npm run affected:libs -- --files="README.md"'
    );
    expect(noAffectedLibs).not.toContain(mypublishablelib);
    expect(noAffectedLibs).not.toContain(mylib);
    expect(noAffectedLibs).not.toContain(mylib2);

    const build = runCommand(
      `npm run affected:build -- --files="libs/${mylib}/src/index.ts"`
    );
    expect(build).toContain(`Running build for ${mypublishablelib}`);
    expect(build).toContain(`Running build for ${myapp}`);
    expect(build).not.toContain('is not registered with the build command');
    expect(build).not.toContain('with flags:');

    // Should work in parallel
    const buildParallel = runCommand(
      `npm run affected:build -- --files="libs/${mylib}/src/index.ts" --parallel`
    );
    expect(buildParallel).toContain(
      `Running build for projects:\n  ${myapp},\n  ${mypublishablelib}`
    );
    expect(buildParallel).toContain(
      'Running build for affected projects succeeded.'
    );

    const buildExcluded = runCommand(
      `npm run affected:build -- --files="libs/${mylib}/src/index.ts" --exclude ${myapp}`
    );
    expect(buildExcluded).toContain(`Running build for ${mypublishablelib}`);

    // affected:build should pass non-nx flags to the CLI
    const buildWithFlags = runCommand(
      `npm run affected:build -- --files="libs/${mylib}/src/index.ts" --stats-json`
    );
    expect(buildWithFlags).toContain(`Running build for ${mypublishablelib}`);
    expect(buildWithFlags).toContain(`Running build for ${myapp}`);
    expect(buildWithFlags).toContain('With flags: --stats-json=true');

    if (!runsInWSL()) {
      const e2e = runCommand(
        `npm run affected:e2e -- --files="libs/${mylib}/src/index.ts" --headless --no-watch`
      );
      expect(e2e).toContain('should display welcome message');
    }

    const unitTests = runCommand(
      `npm run affected:test -- --files="libs/${mylib}/src/index.ts"`
    );
    expect(unitTests).toContain(`Running test for ${mylib}`);
    expect(unitTests).toContain(`Running test for ${mypublishablelib}`);
    expect(unitTests).toContain(`Running test for ${myapp}`);

    // Fail a Unit Test
    updateFile(
      `apps/${myapp}/src/app/app.component.spec.ts`,
      readFile(`apps/${myapp}/src/app/app.component.spec.ts`).replace(
        '.toEqual(1)',
        '.toEqual(2)'
      )
    );

    const failedTests = runCommand(
      `npm run affected:test -- --files="libs/${mylib}/src/index.ts"`
    );

    expect(failedTests).toContain(`Running test for ${mylib}`);
    expect(failedTests).toContain(`Running test for ${mypublishablelib}`);
    expect(failedTests).toContain(`Running test for ${myapp}`);
    expect(failedTests).toContain(`Failed projects: ${myapp}`);
    expect(failedTests).toContain(
      'You can isolate the above projects by passing --only-failed'
    );
    expect(readJson('dist/.nx-results')).toEqual({
      command: 'test',
      results: {
        [myapp]: false,
        [mylib]: true,
        [mypublishablelib]: true
      }
    });

    // Fix failing Unit Test
    updateFile(
      `apps/${myapp}/src/app/app.component.spec.ts`,
      readFile(`apps/${myapp}/src/app/app.component.spec.ts`).replace(
        '.toEqual(2)',
        '.toEqual(1)'
      )
    );

    const isolatedTests = runCommand(
      `npm run affected:test -- --files="libs/${mylib}/src/index.ts" --only-failed`
    );
    expect(isolatedTests).toContain(`Running test for ${myapp}`);

    const linting = runCommand(
      `npm run affected:lint -- --files="libs/${mylib}/src/index.ts"`
    );
    expect(linting).toContain(`Running lint for ${mylib}`);
    expect(linting).toContain(`Running lint for ${myapp}`);
    expect(linting).toContain(`Running lint for ${myapp}-e2e`);
    expect(linting).toContain(`Running lint for ${mypublishablelib}`);

    const lintWithJsonFormating = runCommand(
      `npm run affected:lint -- --files="libs/${mylib}/src/index.ts" -- --format json`
    );
    expect(lintWithJsonFormating).toContain('With flags: --format json');

    const unitTestsExcluded = runCommand(
      `npm run affected:test -- --files="libs/${mylib}/src/index.ts" --exclude=${myapp},${mypublishablelib}`
    );
    expect(unitTestsExcluded).toContain(`Running test for ${mylib}`);

    const i18n = runCommand(
      `npm run affected -- --target extract-i18n --files="libs/${mylib}/src/index.ts"`
    );
    expect(i18n).toContain(`Running extract-i18n for ${myapp}`);
  }, 1000000);
});
