To improve cross platform support, all file access (and path manipulation) is now done through a well known interface (`FileSystem`). For testing a number of `MockFileSystem` implementations are provided. These provide an in-memory file-system which emulates operating systems like OS/X, Unix and Windows. The current file system is always available via the static method, `FileSystem.getFileSystem()`. This is also used by a number of static methods on `AbsoluteFsPath` and `PathSegment`, to avoid having to pass `FileSystem` objects around all the time. The result of this is that one must be careful to ensure that the file-system has been initialized before using any of these static methods. To prevent this happening accidentally the current file system always starts out as an instance of `InvalidFileSystem`, which will throw an error if any of its methods are called. You can set the current file-system by calling `FileSystem.setFileSystem()`. During testing you can call the helper function `initMockFileSystem(os)` which takes a string name of the OS to emulate, and will also monkey-patch aspects of the TypeScript library to ensure that TS is also using the current file-system. Finally there is the `NgtscCompilerHost` to be used for any TypeScript compilation, which uses a given file-system. All tests that interact with the file-system should be tested against each of the mock file-systems. A series of helpers have been provided to support such tests: * `runInEachFileSystem()` - wrap your tests in this helper to run all the wrapped tests in each of the mock file-systems. * `addTestFilesToFileSystem()` - use this to add files and their contents to the mock file system for testing. * `loadTestFilesFromDisk()` - use this to load a mirror image of files on disk into the in-memory mock file-system. * `loadFakeCore()` - use this to load a fake version of `@angular/core` into the mock file-system. All ngcc and ngtsc source and tests now use this virtual file-system setup. PR Close #30921
		
			
				
	
	
		
			561 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			561 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
/**
 | 
						|
 * @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
 | 
						|
 */
 | 
						|
 | 
						|
/// <reference types="node" />
 | 
						|
import {inspect} from 'util';
 | 
						|
 | 
						|
import {runInEachFileSystem} from '../../src/ngtsc/file_system/testing';
 | 
						|
import {tsSourceMapBug29300Fixed} from '../../src/ngtsc/util/src/ts_source_map_bug_29300';
 | 
						|
import {loadStandardTestFiles} from '../helpers/src/mock_file_loading';
 | 
						|
 | 
						|
import {NgtscTestEnvironment} from './env';
 | 
						|
import {SegmentMapping, getMappedSegments} from './sourcemap_utils';
 | 
						|
 | 
						|
const testFiles = loadStandardTestFiles();
 | 
						|
 | 
						|
runInEachFileSystem((os) => {
 | 
						|
  describe('template source-mapping', () => {
 | 
						|
    let env !: NgtscTestEnvironment;
 | 
						|
 | 
						|
    beforeEach(() => {
 | 
						|
      env = NgtscTestEnvironment.setup(testFiles);
 | 
						|
      env.tsconfig();
 | 
						|
    });
 | 
						|
 | 
						|
    describe('Inline templates', () => {
 | 
						|
      describe('(element creation)', () => {
 | 
						|
        it('should map simple element with content', () => {
 | 
						|
          const mappings = compileAndMap('<h1>Heading 1</h1>');
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: '<h1>', generated: 'i0.ɵɵelementStart(0, "h1")', sourceUrl: '../test.ts'});
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: 'Heading 1',
 | 
						|
            generated: 'i0.ɵɵtext(1, "Heading 1")',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: '</h1>', generated: 'i0.ɵɵelementEnd()', sourceUrl: '../test.ts'});
 | 
						|
        });
 | 
						|
 | 
						|
        it('should map void element', () => {
 | 
						|
          const mappings = compileAndMap('<hr>');
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: '<hr>', generated: 'i0.ɵɵelement(0, "hr")', sourceUrl: '../test.ts'});
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      describe('(interpolations)', () => {
 | 
						|
        it('should map a mix of interpolated and static content', () => {
 | 
						|
          const mappings = compileAndMap('<h3>Hello {{ name }}</h3>');
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: '<h3>', generated: 'i0.ɵɵelementStart(0, "h3")', sourceUrl: '../test.ts'});
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: 'Hello {{ name }}',
 | 
						|
            generated: 'i0.ɵɵtextInterpolate1("Hello ", ctx.name, "")',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: '</h3>', generated: 'i0.ɵɵelementEnd()', sourceUrl: '../test.ts'});
 | 
						|
        });
 | 
						|
 | 
						|
        it('should map a complex interpolated expression', () => {
 | 
						|
          const mappings = compileAndMap('<h2>{{ greeting + " " + name }}</h2>');
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: '<h2>', generated: 'i0.ɵɵelementStart(0, "h2")', sourceUrl: '../test.ts'});
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: '{{ greeting + " " + name }}',
 | 
						|
            generated: 'i0.ɵɵtextInterpolate(ctx.greeting + " " + ctx.name)',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: '</h2>', generated: 'i0.ɵɵelementEnd()', sourceUrl: '../test.ts'});
 | 
						|
        });
 | 
						|
 | 
						|
        it('should map interpolated properties', () => {
 | 
						|
          const mappings = compileAndMap('<div id="{{name}}"></div>');
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: '<div id="{{name}}"></div>',
 | 
						|
            generated: 'i0.ɵɵelement(0, "div", _c0)',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: 'id="{{name}}"',
 | 
						|
            generated: 'i0.ɵɵpropertyInterpolate("id", ctx.name)',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
        });
 | 
						|
 | 
						|
        it('should map interpolation with pipe', () => {
 | 
						|
          const mappings = compileAndMap('<div>{{200.3 | percent : 2 }}</div>');
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: '<div>', generated: 'i0.ɵɵelementStart(0, "div")', sourceUrl: '../test.ts'});
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: '{{200.3 | percent : 2 }}',
 | 
						|
            generated: 'i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(2, 1, 200.3, 2))',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: '</div>', generated: 'i0.ɵɵelementEnd()', sourceUrl: '../test.ts'});
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      describe('(property bindings)', () => {
 | 
						|
        it('should map a simple input binding expression', () => {
 | 
						|
          const mappings = compileAndMap('<div [attr]="name"></div>');
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: '<div [attr]="name"></div>',
 | 
						|
            generated: 'i0.ɵɵelement(0, "div", _c0)',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: '[attr]="name"',
 | 
						|
            generated: 'i0.ɵɵproperty("attr", ctx.name)',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
        });
 | 
						|
 | 
						|
        it('should map a complex input binding expression', () => {
 | 
						|
          const mappings = compileAndMap('<div [attr]="greeting + name"></div>');
 | 
						|
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: '<div [attr]="greeting + name"></div>',
 | 
						|
            generated: 'i0.ɵɵelement(0, "div", _c0)',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: '[attr]="greeting + name"',
 | 
						|
            generated: 'i0.ɵɵproperty("attr", ctx.greeting + ctx.name)',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
        });
 | 
						|
 | 
						|
        it('should map a longhand input binding expression', () => {
 | 
						|
          const mappings = compileAndMap('<div bind-attr="name"></div>');
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: '<div bind-attr="name"></div>',
 | 
						|
            generated: 'i0.ɵɵelement(0, "div", _c0)',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: 'bind-attr="name"',
 | 
						|
            generated: 'i0.ɵɵproperty("attr", ctx.name)',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
        });
 | 
						|
 | 
						|
        it('should map a simple output binding expression', () => {
 | 
						|
          const mappings = compileAndMap('<button (click)="doSomething()">Do it</button>');
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: '<button (click)="doSomething()">',
 | 
						|
            generated: 'i0.ɵɵelementStart(0, "button", _c0)',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: 'Do it', generated: 'i0.ɵɵtext(1, "Do it")', sourceUrl: '../test.ts'});
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: 'doSomething()', generated: 'ctx.doSomething()', sourceUrl: '../test.ts'});
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: '</button>', generated: 'i0.ɵɵelementEnd()', sourceUrl: '../test.ts'});
 | 
						|
        });
 | 
						|
 | 
						|
        it('should map a complex output binding expression', () => {
 | 
						|
          const mappings = compileAndMap(
 | 
						|
              `<button (click)="items.push('item' + items.length)">Add Item</button>`);
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: `<button (click)="items.push('item' + items.length)">`,
 | 
						|
            generated: 'i0.ɵɵelementStart(0, "button", _c0)',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: 'Add Item', generated: 'i0.ɵɵtext(1, "Add Item")', sourceUrl: '../test.ts'});
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: 'items.push(', generated: 'ctx.items.push(', sourceUrl: '../test.ts'});
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: `'item' `, generated: `"item"`, sourceUrl: '../test.ts'});
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: '+ items.length)',
 | 
						|
            generated: ' + ctx.items.length)',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: '</button>', generated: 'i0.ɵɵelementEnd()', sourceUrl: '../test.ts'});
 | 
						|
        });
 | 
						|
 | 
						|
        it('should map a longhand output binding expression', () => {
 | 
						|
          const mappings = compileAndMap('<button on-click="doSomething()">Do it</button>');
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: '<button on-click="doSomething()">',
 | 
						|
            generated: 'i0.ɵɵelementStart(0, "button", _c0)',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: 'Do it', generated: 'i0.ɵɵtext(1, "Do it")', sourceUrl: '../test.ts'});
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: 'doSomething()', generated: 'ctx.doSomething()', sourceUrl: '../test.ts'});
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: '</button>', generated: 'i0.ɵɵelementEnd()', sourceUrl: '../test.ts'});
 | 
						|
        });
 | 
						|
 | 
						|
        it('should map a two-way binding expression', () => {
 | 
						|
          const mappings = compileAndMap('Name: <input [(ngModel)]="name">');
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: '<input [(ngModel)]="name">',
 | 
						|
            generated: 'i0.ɵɵelementStart(1, "input", _c0)',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
          // TODO: improve mappings here
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: '[(ngModel)]="name"',
 | 
						|
            generated:
 | 
						|
                'i0.ɵɵlistener("ngModelChange", function TestCmp_Template_input_ngModelChange_1_listener($event) { return ctx.name = $event; })',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: '<input [(ngModel)]="name">',
 | 
						|
            generated: 'i0.ɵɵelementEnd()',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
        });
 | 
						|
 | 
						|
        it('should map a longhand two-way binding expression', () => {
 | 
						|
          const mappings = compileAndMap('Name: <input bindon-ngModel="name">');
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: '<input bindon-ngModel="name">',
 | 
						|
            generated: 'i0.ɵɵelementStart(1, "input", _c0)',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
          // TODO: improve mappings here
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: 'bindon-ngModel="name"',
 | 
						|
            generated:
 | 
						|
                'i0.ɵɵlistener("ngModelChange", function TestCmp_Template_input_ngModelChange_1_listener($event) { return ctx.name = $event; })',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: '<input bindon-ngModel="name">',
 | 
						|
            generated: 'i0.ɵɵelementEnd()',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
        });
 | 
						|
 | 
						|
        it('should map a class input binding', () => {
 | 
						|
          const mappings = compileAndMap('<div [class.initial]="isInitial">Message</div>');
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: '<div [class.initial]="isInitial">',
 | 
						|
            generated: 'i0.ɵɵelementStart(0, "div")',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
 | 
						|
          // TODO: Add better mappings for binding
 | 
						|
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: 'Message', generated: 'i0.ɵɵtext(1, "Message")', sourceUrl: '../test.ts'});
 | 
						|
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: '</div>', generated: 'i0.ɵɵelementEnd()', sourceUrl: '../test.ts'});
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      describe('(structural directives)', () => {
 | 
						|
        it('should map *ngIf scenario', () => {
 | 
						|
          const mappings = compileAndMap('<div *ngIf="showMessage()">{{ name }}</div>');
 | 
						|
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: '<div *ngIf="showMessage()">',
 | 
						|
            generated: 'i0.ɵɵelementStart(0, "div")',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
 | 
						|
          // TODO - map the bindings better
 | 
						|
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: '</div>', generated: 'i0.ɵɵelementEnd()', sourceUrl: '../test.ts'});
 | 
						|
 | 
						|
          // TODO: the `ctx_r...` appears to be dependent upon previous tests!!!
 | 
						|
 | 
						|
          // expect(mappings).toContain({
 | 
						|
          //   source: '{{ name }}',
 | 
						|
          //   generated: 'i0.ɵɵtextInterpolate(ctx_r0.name)',
 | 
						|
          //   sourceUrl: '../test.ts'
 | 
						|
          // });
 | 
						|
        });
 | 
						|
 | 
						|
        it('should map ng-template [ngIf] scenario', () => {
 | 
						|
          const mappings = compileAndMap(
 | 
						|
              `<ng-template [ngIf]="showMessage()">\n` +
 | 
						|
              `  <div>{{ name }}</div>\n` +
 | 
						|
              `  <hr>\n` +
 | 
						|
              `</ng-template>`);
 | 
						|
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: '<div>', generated: 'i0.ɵɵelementStart(0, "div")', sourceUrl: '../test.ts'});
 | 
						|
 | 
						|
          // TODO - map the bindings better
 | 
						|
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: '</div>', generated: 'i0.ɵɵelementEnd()', sourceUrl: '../test.ts'});
 | 
						|
 | 
						|
          // TODO: the `ctx_r...` appears to be dependent upon previous tests!!!
 | 
						|
 | 
						|
          // expect(mappings).toContain({
 | 
						|
          //   source: '{{ name }}',
 | 
						|
          //   generated: 'i0.ɵɵtextInterpolate(ctx_r0.name)',
 | 
						|
          //   sourceUrl: '../test.ts'
 | 
						|
          // });
 | 
						|
        });
 | 
						|
 | 
						|
        it('should map *ngFor scenario', () => {
 | 
						|
          const mappings = compileAndMap(
 | 
						|
              '<div *ngFor="let item of items; index as i; trackBy: trackByFn">{{ item }}</div>');
 | 
						|
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: '<div *ngFor="let item of items; index as i; trackBy: trackByFn">',
 | 
						|
            generated: 'i0.ɵɵelementStart(0, "div")',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
 | 
						|
          // TODO - map the bindings better
 | 
						|
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: '</div>', generated: 'i0.ɵɵelementEnd()', sourceUrl: '../test.ts'});
 | 
						|
 | 
						|
        });
 | 
						|
 | 
						|
        it('should map ng-template [ngFor] scenario', () => {
 | 
						|
          const mappings = compileAndMap(
 | 
						|
              `<ng-template ngFor [ngForOf]="items" let-item>{{ item }}</ng-template>`);
 | 
						|
 | 
						|
          // TODO - map the bindings better
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      describe('(content projection)', () => {
 | 
						|
        it('should map default and selected projection', () => {
 | 
						|
          const mappings = compileAndMap(
 | 
						|
              `<h3><ng-content select="title"></ng-content></h3>\n` +
 | 
						|
              `<div><ng-content></ng-content></div>`);
 | 
						|
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: '<h3>', generated: 'i0.ɵɵelementStart(0, "h3")', sourceUrl: '../test.ts'});
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: '<ng-content select="title">',
 | 
						|
            generated: 'i0.ɵɵprojection(1)',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: '</h3>', generated: 'i0.ɵɵelementEnd()', sourceUrl: '../test.ts'});
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: '<div>', generated: 'i0.ɵɵelementStart(2, "div")', sourceUrl: '../test.ts'});
 | 
						|
          expect(mappings).toContain({
 | 
						|
            source: '<ng-content>',
 | 
						|
            generated: 'i0.ɵɵprojection(3, 1)',
 | 
						|
            sourceUrl: '../test.ts'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {source: '</div>', generated: 'i0.ɵɵelementEnd()', sourceUrl: '../test.ts'});
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      it('should create (simple string) inline template source-mapping', () => {
 | 
						|
        const mappings = compileAndMap('<div>this is a test</div><div>{{ 1 + 2 }}</div>');
 | 
						|
 | 
						|
        // Creation mode
 | 
						|
        expect(mappings).toContain(
 | 
						|
            {generated: 'i0.ɵɵelementStart(0, "div")', source: '<div>', sourceUrl: '../test.ts'});
 | 
						|
        expect(mappings).toContain({
 | 
						|
          generated: 'i0.ɵɵtext(1, "this is a test")',
 | 
						|
          source: 'this is a test',
 | 
						|
          sourceUrl: '../test.ts'
 | 
						|
        });
 | 
						|
        expect(mappings).toContain(
 | 
						|
            {generated: 'i0.ɵɵelementEnd()', source: '</div>', sourceUrl: '../test.ts'});
 | 
						|
        expect(mappings).toContain(
 | 
						|
            {generated: 'i0.ɵɵelementStart(2, "div")', source: '<div>', sourceUrl: '../test.ts'});
 | 
						|
        expect(mappings).toContain(
 | 
						|
            {generated: 'i0.ɵɵtext(3)', source: '{{ 1 + 2 }}', sourceUrl: '../test.ts'});
 | 
						|
        expect(mappings).toContain(
 | 
						|
            {generated: 'i0.ɵɵelementEnd()', source: '</div>', sourceUrl: '../test.ts'});
 | 
						|
 | 
						|
        // Update mode
 | 
						|
        expect(mappings).toContain({
 | 
						|
          generated: 'i0.ɵɵtextInterpolate(1 + 2)',
 | 
						|
          source: '{{ 1 + 2 }}',
 | 
						|
          sourceUrl: '../test.ts'
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      it('should create (simple backtick string) inline template source-mapping', () => {
 | 
						|
        const mappings = compileAndMap('<div>this is a test</div><div>{{ 1 + 2 }}</div>');
 | 
						|
 | 
						|
        // Creation mode
 | 
						|
        expect(mappings).toContain(
 | 
						|
            {generated: 'i0.ɵɵelementStart(0, "div")', source: '<div>', sourceUrl: '../test.ts'});
 | 
						|
        expect(mappings).toContain({
 | 
						|
          generated: 'i0.ɵɵtext(1, "this is a test")',
 | 
						|
          source: 'this is a test',
 | 
						|
          sourceUrl: '../test.ts'
 | 
						|
        });
 | 
						|
        expect(mappings).toContain(
 | 
						|
            {generated: 'i0.ɵɵelementEnd()', source: '</div>', sourceUrl: '../test.ts'});
 | 
						|
        expect(mappings).toContain(
 | 
						|
            {generated: 'i0.ɵɵelementStart(2, "div")', source: '<div>', sourceUrl: '../test.ts'});
 | 
						|
        expect(mappings).toContain(
 | 
						|
            {generated: 'i0.ɵɵtext(3)', source: '{{ 1 + 2 }}', sourceUrl: '../test.ts'});
 | 
						|
        expect(mappings).toContain(
 | 
						|
            {generated: 'i0.ɵɵelementEnd()', source: '</div>', sourceUrl: '../test.ts'});
 | 
						|
 | 
						|
        // TODO(benlesh): We need to circle back and prevent the extra parens from being generated.
 | 
						|
        // Update mode
 | 
						|
        expect(mappings).toContain({
 | 
						|
          generated: 'i0.ɵɵtextInterpolate(1 + 2)',
 | 
						|
          source: '{{ 1 + 2 }}',
 | 
						|
          sourceUrl: '../test.ts'
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      it('should create correct inline template source-mapping when the source contains escape sequences',
 | 
						|
         () => {
 | 
						|
           // Note that the escaped double quotes, which need un-escaping to be parsed correctly.
 | 
						|
           const mappings = compileAndMap('<div class=\\"some-class\\">this is a test</div>');
 | 
						|
 | 
						|
           expect(mappings).toContain({
 | 
						|
             generated: 'i0.ɵɵelementStart(0, "div", _c0)',
 | 
						|
             source: '<div class=\\"some-class\\">',
 | 
						|
             sourceUrl: '../test.ts'
 | 
						|
           });
 | 
						|
 | 
						|
           const c2Mapping =
 | 
						|
               mappings.find(mapping => /var _c0 = \[1, "some-class"\];/.test(mapping.generated));
 | 
						|
           expect(c2Mapping).toBeDefined();
 | 
						|
         });
 | 
						|
    });
 | 
						|
 | 
						|
    if (tsSourceMapBug29300Fixed()) {
 | 
						|
      describe('External templates (where TS supports source-mapping)', () => {
 | 
						|
        it('should create external template source-mapping', () => {
 | 
						|
          const mappings =
 | 
						|
              compileAndMap('<div>this is a test</div><div>{{ 1 + 2 }}</div>', './dir/test.html');
 | 
						|
 | 
						|
          // Creation mode
 | 
						|
          expect(mappings).toContain({
 | 
						|
            generated: 'i0.ɵɵelementStart(0, "div")',
 | 
						|
            source: '<div>',
 | 
						|
            sourceUrl: '../dir/test.html'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain({
 | 
						|
            generated: 'i0.ɵɵtext(1, "this is a test")',
 | 
						|
            source: 'this is a test',
 | 
						|
            sourceUrl: '../dir/test.html'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {generated: 'i0.ɵɵelementEnd()', source: '</div>', sourceUrl: '../dir/test.html'});
 | 
						|
          expect(mappings).toContain({
 | 
						|
            generated: 'i0.ɵɵelementStart(2, "div")',
 | 
						|
            source: '<div>',
 | 
						|
            sourceUrl: '../dir/test.html'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {generated: 'i0.ɵɵtext(3)', source: '{{ 1 + 2 }}', sourceUrl: '../dir/test.html'});
 | 
						|
          expect(mappings).toContain(
 | 
						|
              {generated: 'i0.ɵɵelementEnd()', source: '</div>', sourceUrl: '../dir/test.html'});
 | 
						|
 | 
						|
          // Update mode
 | 
						|
          expect(mappings).toContain({
 | 
						|
            generated: 'i0.ɵɵtextInterpolate(1 + 2)',
 | 
						|
            source: '{{ 1 + 2 }}',
 | 
						|
            sourceUrl: '../dir/test.html'
 | 
						|
          });
 | 
						|
        });
 | 
						|
 | 
						|
        it('should create correct mappings when templateUrl is in a different rootDir', () => {
 | 
						|
          const mappings = compileAndMap(
 | 
						|
              '<div>this is a test</div><div>{{ 1 + 2 }}</div>', 'extraRootDir/test.html');
 | 
						|
 | 
						|
          // Creation mode
 | 
						|
          expect(mappings).toContain({
 | 
						|
            generated: 'i0.ɵɵelementStart(0, "div")',
 | 
						|
            source: '<div>',
 | 
						|
            sourceUrl: '../extraRootDir/test.html'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain({
 | 
						|
            generated: 'i0.ɵɵtext(1, "this is a test")',
 | 
						|
            source: 'this is a test',
 | 
						|
            sourceUrl: '../extraRootDir/test.html'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain({
 | 
						|
            generated: 'i0.ɵɵelementEnd()',
 | 
						|
            source: '</div>',
 | 
						|
            sourceUrl: '../extraRootDir/test.html'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain({
 | 
						|
            generated: 'i0.ɵɵelementStart(2, "div")',
 | 
						|
            source: '<div>',
 | 
						|
            sourceUrl: '../extraRootDir/test.html'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain({
 | 
						|
            generated: 'i0.ɵɵtext(3)',
 | 
						|
            source: '{{ 1 + 2 }}',
 | 
						|
            sourceUrl: '../extraRootDir/test.html'
 | 
						|
          });
 | 
						|
          expect(mappings).toContain({
 | 
						|
            generated: 'i0.ɵɵelementEnd()',
 | 
						|
            source: '</div>',
 | 
						|
            sourceUrl: '../extraRootDir/test.html'
 | 
						|
          });
 | 
						|
 | 
						|
          // Update mode
 | 
						|
          expect(mappings).toContain({
 | 
						|
            generated: 'i0.ɵɵtextInterpolate(1 + 2)',
 | 
						|
            source: '{{ 1 + 2 }}',
 | 
						|
            sourceUrl: '../extraRootDir/test.html'
 | 
						|
          });
 | 
						|
        });
 | 
						|
      });
 | 
						|
    }
 | 
						|
 | 
						|
    function compileAndMap(template: string, templateUrl: string | null = null) {
 | 
						|
      const templateConfig = templateUrl ? `templateUrl: '${templateUrl}'` :
 | 
						|
                                           ('template: `' + template.replace(/`/g, '\\`') + '`');
 | 
						|
      env.tsconfig({sourceMap: true});
 | 
						|
      env.write('test.ts', `
 | 
						|
        import {Component} from '@angular/core';
 | 
						|
 | 
						|
        @Component({
 | 
						|
          selector: 'test-cmp',
 | 
						|
          ${templateConfig}
 | 
						|
        })
 | 
						|
        export class TestCmp {}
 | 
						|
    `);
 | 
						|
      if (templateUrl) {
 | 
						|
        env.write(templateUrl, template);
 | 
						|
      }
 | 
						|
      env.driveMain();
 | 
						|
      return getMappedSegments(env, 'test.js');
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Helper function for debugging failed mappings.
 | 
						|
     * This lays out the segment mappings in the console to make it easier to compare.
 | 
						|
     */
 | 
						|
    function dumpMappings(mappings: SegmentMapping[]) {
 | 
						|
      mappings.forEach(map => {
 | 
						|
        // tslint:disable-next-line:no-console
 | 
						|
        console.log(
 | 
						|
            padValue(map.sourceUrl, 20, 0) + ' : ' + padValue(inspect(map.source), 100, 23) +
 | 
						|
            ' : ' + inspect(map.generated));
 | 
						|
      });
 | 
						|
      function padValue(value: string, max: number, start: number) {
 | 
						|
        const padding = value.length > max ? ('\n' +
 | 
						|
                                              ' '.repeat(max + start)) :
 | 
						|
                                             ' '.repeat(max - value.length);
 | 
						|
        return value + padding;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  });
 | 
						|
});
 |