/**
 * @license
 * Copyright Google Inc. All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */
import {AbsoluteFsPath, resolve} from '@angular/compiler-cli/src/ngtsc/file_system';
import {runInEachFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system/testing';
import {AbsoluteSourceSpan, IdentifierKind, IndexedComponent, TopLevelIdentifier} from '@angular/compiler-cli/src/ngtsc/indexer';
import {ParseSourceFile} from '@angular/compiler/src/compiler';
import {NgtscTestEnvironment} from './env';
runInEachFileSystem(() => {
  describe('ngtsc component indexing', () => {
    let env !: NgtscTestEnvironment;
    let testSourceFile: AbsoluteFsPath;
    let testTemplateFile: AbsoluteFsPath;
    beforeEach(() => {
      env = NgtscTestEnvironment.setup();
      env.tsconfig();
      testSourceFile = resolve(env.basePath, 'test.ts');
      testTemplateFile = resolve(env.basePath, 'test.html');
    });
    describe('indexing metadata', () => {
      it('should generate component metadata', () => {
        const componentContent = `
        import {Component} from '@angular/core';
        @Component({
          selector: 'test-cmp',
          template: '
',
        })
        export class TestCmp {}
    `;
        env.write(testSourceFile, componentContent);
        const indexed = env.driveIndexer();
        expect(indexed.size).toBe(1);
        const [[decl, indexedComp]] = Array.from(indexed.entries());
        expect(decl.getText()).toContain('export class TestCmp {}');
        expect(indexedComp).toEqual(jasmine.objectContaining({
          name: 'TestCmp',
          selector: 'test-cmp',
          file: new ParseSourceFile(componentContent, testSourceFile),
        }));
      });
      it('should index inline templates', () => {
        const componentContent = `
        import {Component} from '@angular/core';
        @Component({
          selector: 'test-cmp',
          template: '{{foo}}',
        })
        export class TestCmp { foo = 0; }
      `;
        env.write(testSourceFile, componentContent);
        const indexed = env.driveIndexer();
        const [[_, indexedComp]] = Array.from(indexed.entries());
        const template = indexedComp.template;
        expect(template).toEqual({
          identifiers: new Set([{
            name: 'foo',
            kind: IdentifierKind.Property,
            span: new AbsoluteSourceSpan(127, 130),
            target: null,
          }]),
          usedComponents: new Set(),
          isInline: true,
          file: new ParseSourceFile(componentContent, testSourceFile),
        });
      });
      it('should index external templates', () => {
        env.write(testSourceFile, `
        import {Component} from '@angular/core';
        @Component({
          selector: 'test-cmp',
          templateUrl: './test.html',
        })
        export class TestCmp { foo = 0; }
      `);
        env.write(testTemplateFile, '{{foo}}');
        const indexed = env.driveIndexer();
        const [[_, indexedComp]] = Array.from(indexed.entries());
        const template = indexedComp.template;
        expect(template).toEqual({
          identifiers: new Set([{
            name: 'foo',
            kind: IdentifierKind.Property,
            span: new AbsoluteSourceSpan(2, 5),
            target: null,
          }]),
          usedComponents: new Set(),
          isInline: false,
          file: new ParseSourceFile('{{foo}}', testTemplateFile),
        });
      });
      it('should index templates compiled without preserving whitespace', () => {
        env.tsconfig({
          preserveWhitespaces: false,
        });
        env.write(testSourceFile, `
        import {Component} from '@angular/core';
        @Component({
          selector: 'test-cmp',
          templateUrl: './test.html',
        })
        export class TestCmp { foo = 0; }
      `);
        env.write(testTemplateFile, '  \n  {{foo}}');
        const indexed = env.driveIndexer();
        const [[_, indexedComp]] = Array.from(indexed.entries());
        const template = indexedComp.template;
        expect(template).toEqual({
          identifiers: new Set([{
            name: 'foo',
            kind: IdentifierKind.Property,
            span: new AbsoluteSourceSpan(7, 10),
            target: null,
          }]),
          usedComponents: new Set(),
          isInline: false,
          file: new ParseSourceFile('  \n  {{foo}}', testTemplateFile),
        });
      });
      it('should generate information about used components', () => {
        env.write(testSourceFile, `
        import {Component} from '@angular/core';
        @Component({
          selector: 'test-cmp',
          templateUrl: './test.html',
        })
        export class TestCmp {}
      `);
        env.write(testTemplateFile, '');
        env.write('test_import.ts', `
        import {Component, NgModule} from '@angular/core';
        import {TestCmp} from './test';
        @Component({
          templateUrl: './test_import.html',
        })
        export class TestImportCmp {}
        @NgModule({
          declarations: [
            TestCmp,
            TestImportCmp,
          ],
          bootstrap: [TestImportCmp]
        })
        export class TestModule {}
      `);
        env.write('test_import.html', '');
        const indexed = env.driveIndexer();
        expect(indexed.size).toBe(2);
        const indexedComps = Array.from(indexed.values());
        const testComp = indexedComps.find(comp => comp.name === 'TestCmp');
        const testImportComp = indexedComps.find(cmp => cmp.name === 'TestImportCmp');
        expect(testComp).toBeDefined();
        expect(testImportComp).toBeDefined();
        expect(testComp !.template.usedComponents.size).toBe(0);
        expect(testImportComp !.template.usedComponents.size).toBe(1);
        const [usedComp] = Array.from(testImportComp !.template.usedComponents);
        expect(indexed.get(usedComp)).toEqual(testComp);
      });
    });
  });
});