hunk.code_window:  * 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 { request, runTargetSpec } from '@angular-devkit/architect/testing';
   import { from } from 'rxjs';
   import { concatMap, take, tap } from 'rxjs/operators';
  
hunk.labels: keep keep keep keep replace keep keep
hunk.after_edit: import { TestLogger, request, runTargetSpec } from '@angular-devkit/architect/testing';
  
hunk.file_path: packages/angular_devkit/build_angular/test/dev-server/works_spec_large.ts
hunk.type: replace
file: /**
   * @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
   */
  // tslint:disable:no-big-function
  
  import { DefaultTimeout, runTargetSpec } from '@angular-devkit/architect/testing';
  import { normalize, tags, virtualFs } from '@angular-devkit/core';
  import { concatMap, tap } from 'rxjs/operators';
  import { browserTargetSpec, host } from '../utils';
  
  
  describe('Browser Builder styles', () => {
    const extensionsWithImportSupport = ['css', 'scss', 'less', 'styl'];
    const extensionsWithVariableSupport = ['scss', 'less', 'styl'];
    const imgSvg = `
      <svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
        <circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" />
      </svg>
    `;
  
    beforeEach(done => host.initialize().toPromise().then(done, done.fail));
    afterEach(done => host.restore().toPromise().then(done, done.fail));
  
    it('supports global styles', (done) => {
      const styles: { [path: string]: string } = {
        'src/string-style.css': '.string-style { color: red }',
        'src/input-style.css': '.input-style { color: red }',
        'src/lazy-style.css': '.lazy-style { color: red }',
        'src/pre-rename-style.css': '.pre-rename-style { color: red }',
        'src/pre-rename-lazy-style.css': '.pre-rename-lazy-style { color: red }',
      };
      const getStylesOption = () => [
        'src/input-style.css',
        { input: 'src/lazy-style.css', bundleName: 'lazy-style', lazy: true },
        { input: 'src/pre-rename-style.css', bundleName: 'renamed-style' },
        { input: 'src/pre-rename-lazy-style.css', bundleName: 'renamed-lazy-style', lazy: true },
      ];
      const cssMatches: { [path: string]: string } = {
        './dist/styles.css': '.input-style',
        './dist/lazy-style.css': '.lazy-style',
        './dist/renamed-style.css': '.pre-rename-style',
        './dist/renamed-lazy-style.css': '.pre-rename-lazy-style',
      };
      const cssIndexMatches: { [path: string]: string } = {
        './dist/index.html': '<link rel="stylesheet" href="styles.css">'
          + '<link rel="stylesheet" href="renamed-style.css">',
      };
      const jsMatches: { [path: string]: string } = {
        './dist/styles.js': '.input-style',
        './dist/lazy-style.js': '.lazy-style',
        './dist/renamed-style.js': '.pre-rename-style',
        './dist/renamed-lazy-style.js': '.pre-rename-lazy-style',
      };
      const jsIndexMatches: { [path: string]: string } = {
        './dist/index.html': '<script type="text/javascript" src="runtime.js"></script>'
          + '<script type="text/javascript" src="polyfills.js"></script>'
          + '<script type="text/javascript" src="styles.js"></script>'
          + '<script type="text/javascript" src="renamed-style.js"></script>'
          + '<script type="text/javascript" src="vendor.js"></script>'
          + '<script type="text/javascript" src="main.js"></script>',
      };
  
      host.writeMultipleFiles(styles);
  
      const overrides = { extractCss: true, styles: getStylesOption() };
  
      runTargetSpec(host, browserTargetSpec, overrides).pipe(
        tap((buildEvent) => expect(buildEvent.success).toBe(true)),
        // Check css files were created.
        tap(() => Object.keys(cssMatches).forEach(fileName => {
          const content = virtualFs.fileBufferToString(host.scopedSync().read(normalize(fileName)));
          expect(content).toMatch(cssMatches[fileName]);
        })),
        // Check no js files are created.
        tap(() => Object.keys(jsMatches).forEach(key =>
          expect(host.scopedSync().exists(normalize(key))).toBe(false),
        )),
        // Check check index has styles in the right order.
        tap(() => Object.keys(cssIndexMatches).forEach(fileName => {
          const content = virtualFs.fileBufferToString(host.scopedSync().read(normalize(fileName)));
          expect(content).toMatch(cssIndexMatches[fileName]);
        })),
        // Also test with extractCss false.
        concatMap(() => runTargetSpec(host, browserTargetSpec,
          { extractCss: false, styles: getStylesOption() })),
        // TODO: figure out why adding this tap breaks typings.
        // This also happens in the output-hashing spec.
        // tap((buildEvent) => expect(buildEvent.success).toBe(true)),
        // Check js files were created.
        tap(() => Object.keys(jsMatches).forEach(fileName => {
          const content = virtualFs.fileBufferToString(host.scopedSync().read(normalize(fileName)));
          expect(content).toMatch(jsMatches[fileName]);
        })),
        // Check no css files are created.
        tap(() => Object.keys(cssMatches).forEach(key =>
          expect(host.scopedSync().exists(normalize(key))).toBe(false),
        )),
        // Check check index has styles in the right order.
        tap(() => Object.keys(jsIndexMatches).forEach(fileName => {
          const content = virtualFs.fileBufferToString(host.scopedSync().read(normalize(fileName)));
          expect(content).toMatch(jsIndexMatches[fileName]);
        })),
      ).toPromise().then(done, done.fail);
    });
  
    it('supports empty styleUrls in components', (done) => {
      host.writeMultipleFiles({
        './src/app/app.component.ts': `
          import { Component } from '@angular/core';
  
          @Component({
            selector: 'app-root',
            templateUrl: './app.component.html',
            styleUrls: []
          })
          export class AppComponent {
            title = 'app';
          }
        `,
      });
  
      const overrides = { extractCss: true };
  
      runTargetSpec(host, browserTargetSpec, overrides).pipe(
        tap((buildEvent) => expect(buildEvent.success).toBe(true)),
      ).toPromise().then(done, done.fail);
    });
  
    extensionsWithImportSupport.forEach(ext => {
      it(`supports imports in ${ext} files`, (done) => {
        host.writeMultipleFiles({
          [`src/styles.${ext}`]: `
            @import './imported-styles.${ext}';
            body { background-color: #00f; }
          `,
          [`src/imported-styles.${ext}`]: 'p { background-color: #f00; }',
          [`src/app/app.component.${ext}`]: `
            @import './imported-component-styles.${ext}';
            .outer {
              .inner {
                background: #fff;
              }
            }
          `,
          [`src/app/imported-component-styles.${ext}`]: 'h1 { background: #000; }',
        });
  
        const matches: { [path: string]: RegExp } = {
          'dist/styles.css': new RegExp(
            // The global style should be there
            /p\s*{\s*background-color: #f00;\s*}(.|\n|\r)*/.source
            // The global style via import should be there
            + /body\s*{\s*background-color: #00f;\s*}/.source,
          ),
          'dist/styles.css.map': /"mappings":".+"/,
          'dist/main.js': new RegExp(
            // The component style should be there
            /h1(.|\n|\r)*background:\s*#000(.|\n|\r)*/.source
            // The component style via import should be there
            + /.outer(.|\n|\r)*.inner(.|\n|\r)*background:\s*#[fF]+/.source,
          ),
        };
  
        const overrides = {
          extractCss: true,
          sourceMap: true,
          styles: [`src/styles.${ext}`],
        };
  
        host.replaceInFile('src/app/app.component.ts', './app.component.css',
          `./app.component.${ext}`);
  
        runTargetSpec(host, browserTargetSpec, overrides).pipe(
          tap((buildEvent) => expect(buildEvent.success).toBe(true)),
          tap(() => Object.keys(matches).forEach(fileName => {
            const content = virtualFs.fileBufferToString(host.scopedSync().read(normalize(fileName)));
            expect(content).toMatch(matches[fileName]);
          })),
        ).toPromise().then(done, done.fail);
      });
    });
  
    extensionsWithImportSupport.forEach(ext => {
      it(`supports material imports in ${ext} files`, (done) => {
        host.writeMultipleFiles({
          [`src/styles.${ext}`]: `
            @import "~@angular/material/prebuilt-themes/indigo-pink.css";
            @import "@angular/material/prebuilt-themes/indigo-pink.css";
          `,
          [`src/app/app.component.${ext}`]: `
            @import "~@angular/material/prebuilt-themes/indigo-pink.css";
            @import "@angular/material/prebuilt-themes/indigo-pink.css";
          `,
        });
        host.replaceInFile('src/app/app.component.ts', './app.component.css',
          `./app.component.${ext}`);
  
        const overrides = {
          extractCss: true,
          styles: [{ input: `src/styles.${ext}` }],
        };
  
        runTargetSpec(host, browserTargetSpec, overrides).pipe(
          tap((buildEvent) => expect(buildEvent.success).toBe(true)),
        ).toPromise().then(done, done.fail);
      });
    });
  
    it(`supports material icons`, (done) => {
      const overrides = {
        extractCss: true,
        optimization: true,
        styles: [
          { input: '../../../../node_modules/material-design-icons/iconfont/material-icons.css' },
        ],
      };
  
      runTargetSpec(host, browserTargetSpec, overrides, DefaultTimeout * 2).pipe(
        tap((buildEvent) => expect(buildEvent.success).toBe(true)),
      ).toPromise().then(done, done.fail);
    });
  
    extensionsWithVariableSupport.forEach(ext => {
      it(`supports ${ext} includePaths`, (done) => {
  
        let variableAssignment = '';
        let variablereference = '';
        if (ext === 'scss') {
          variableAssignment = '$primary-color:';
          variablereference = '$primary-color';
        } else if (ext === 'styl') {
          variableAssignment = '$primary-color =';
          variablereference = '$primary-color';
        } else if (ext === 'less') {
          variableAssignment = '@primary-color:';
          variablereference = '@primary-color';
        }
  
        host.writeMultipleFiles({
          [`src/style-paths/variables.${ext}`]: `${variableAssignment} #f00;`,
          [`src/styles.${ext}`]: `
            @import 'variables';
            h1 { color: ${variablereference}; }
          `,
          [`src/app/app.component.${ext}`]: `
            @import 'variables';
            h2 { color: ${variablereference}; }
          `,
        });
  
        const matches: { [path: string]: RegExp } = {
          'dist/styles.css': /h1\s*{\s*color: #f00;\s*}/,
          'dist/main.js': /h2.*{.*color: #f00;.*}/,
        };
  
        host.replaceInFile('src/app/app.component.ts', './app.component.css',
          `./app.component.${ext}`);
  
        const overrides = {
          extractCss: true,
          styles: [`src/styles.${ext}`],
          stylePreprocessorOptions: {
            includePaths: ['src/style-paths'],
          },
        };
  
        runTargetSpec(host, browserTargetSpec, overrides).pipe(
          tap((buildEvent) => expect(buildEvent.success).toBe(true)),
          tap(() => Object.keys(matches).forEach(fileName => {
            const content = virtualFs.fileBufferToString(host.scopedSync().read(normalize(fileName)));
            expect(content).toMatch(matches[fileName]);
          })),
        ).toPromise().then(done, done.fail);
      });
    });
  
    it(`supports font-awesome imports`, (done) => {
      host.writeMultipleFiles({
        'src/styles.scss': `
          $fa-font-path: "~font-awesome/fonts";
          @import "~font-awesome/scss/font-awesome";
        `,
      });
  
      const overrides = { extractCss: true, styles: [`src/styles.scss`] };
  
      runTargetSpec(host, browserTargetSpec, overrides).pipe(
        tap((buildEvent) => expect(buildEvent.success).toBe(true)),
      ).toPromise().then(done, done.fail);
    }, 30000);
  
    it(`uses autoprefixer`, (done) => {
      host.writeMultipleFiles({
        'src/styles.css': tags.stripIndents`
          /* normal-comment */
          /*! important-comment */
          div { flex: 1 }`,
      });
  
      const overrides = { extractCss: true, optimization: false };
  
      runTargetSpec(host, browserTargetSpec, overrides).pipe(
        tap((buildEvent) => expect(buildEvent.success).toBe(true)),
        tap(() => {
          const fileName = 'dist/styles.css';
          const content = virtualFs.fileBufferToString(host.scopedSync().read(normalize(fileName)));
          expect(content).toContain(tags.stripIndents`
            /* normal-comment */
            /*! important-comment */
            div { -ms-flex: 1; flex: 1 }`);
        }),
      ).toPromise().then(done, done.fail);
    });
  
    it(`minimizes css`, (done) => {
      host.writeMultipleFiles({
        'src/styles.css': tags.stripIndents`
          /* normal-comment */
          /*! important-comment */
          div { flex: 1 }`,
      });
  
      const overrides = { extractCss: true, optimization: true };
  
      runTargetSpec(host, browserTargetSpec, overrides, DefaultTimeout * 2).pipe(
        tap((buildEvent) => expect(buildEvent.success).toBe(true)),
        tap(() => {
          const fileName = 'dist/styles.css';
          const content = virtualFs.fileBufferToString(host.scopedSync().read(normalize(fileName)));
          expect(content).toContain(
            '/*! important-comment */div{-ms-flex:1;flex:1}');
        }),
      ).toPromise().then(done, done.fail);
    });
  
    // TODO: consider making this a unit test in the url processing plugins.
    it(`supports baseHref and deployUrl in resource urls`, (done) => {
      // Use a large image for the relative ref so it cannot be inlined.
      host.copyFile('src/spectrum.png', './src/assets/global-img-relative.png');
      host.copyFile('src/spectrum.png', './src/assets/component-img-relative.png');
      host.writeMultipleFiles({
        'src/styles.css': `
          h1 { background: url('/assets/global-img-absolute.svg'); }
          h2 { background: url('./assets/global-img-relative.png'); }
        `,
        'src/app/app.component.css': `
          h3 { background: url('/assets/component-img-absolute.svg'); }
          h4 { background: url('../assets/component-img-relative.png'); }
        `,
        'src/assets/global-img-absolute.svg': imgSvg,
        'src/assets/component-img-absolute.svg': imgSvg,
      });
  
      const stylesBundle = 'dist/styles.css';
      const mainBundle = 'dist/main.js';
  
      // Check base paths are correctly generated.
      runTargetSpec(host, browserTargetSpec, { aot: true, extractCss: true }).pipe(
        tap(() => {
          const styles = virtualFs.fileBufferToString(
            host.scopedSync().read(normalize(stylesBundle)),
          );
          const main = virtualFs.fileBufferToString(host.scopedSync().read(normalize(mainBundle)));
          expect(styles).toContain(`url('/assets/global-img-absolute.svg')`);
          expect(styles).toContain(`url('global-img-relative.png')`);
          expect(main).toContain(`url('/assets/component-img-absolute.svg')`);
          expect(main).toContain(`url('component-img-relative.png')`);
          expect(host.scopedSync().exists(normalize('dist/assets/global-img-absolute.svg')))
            .toBe(true);
          expect(host.scopedSync().exists(normalize('dist/global-img-relative.png')))
            .toBe(true);
          expect(host.scopedSync().exists(normalize('dist/assets/component-img-absolute.svg')))
            .toBe(true);
          expect(host.scopedSync().exists(normalize('dist/component-img-relative.png')))
            .toBe(true);
        }),
        // Check urls with deploy-url scheme are used as is.
        concatMap(() => runTargetSpec(host, browserTargetSpec,
          { extractCss: true, baseHref: '/base/', deployUrl: 'http://deploy.url/' },
        )),
        tap(() => {
          const styles = virtualFs.fileBufferToString(
            host.scopedSync().read(normalize(stylesBundle)),
          );
          const main = virtualFs.fileBufferToString(host.scopedSync().read(normalize(mainBundle)));
          expect(styles)
            .toContain(`url('http://deploy.url/assets/global-img-absolute.svg')`);
          expect(main)
            .toContain(`url('http://deploy.url/assets/component-img-absolute.svg')`);
        }),
        // Check urls with base-href scheme are used as is (with deploy-url).
        concatMap(() => runTargetSpec(host, browserTargetSpec,
          { extractCss: true, baseHref: 'http://base.url/', deployUrl: 'deploy/' },
        )),
        tap(() => {
          const styles = virtualFs.fileBufferToString(
            host.scopedSync().read(normalize(stylesBundle)),
          );
          const main = virtualFs.fileBufferToString(host.scopedSync().read(normalize(mainBundle)));
          expect(styles)
            .toContain(`url('http://base.url/deploy/assets/global-img-absolute.svg')`);
          expect(main)
            .toContain(`url('http://base.url/deploy/assets/component-img-absolute.svg')`);
        }),
        // Check urls with deploy-url and base-href scheme only use deploy-url.
        concatMap(() => runTargetSpec(host, browserTargetSpec, {
          extractCss: true,
          baseHref: 'http://base.url/',
          deployUrl: 'http://deploy.url/',
        },
        )),
        tap(() => {
          const styles = virtualFs.fileBufferToString(
            host.scopedSync().read(normalize(stylesBundle)),
          );
          const main = virtualFs.fileBufferToString(host.scopedSync().read(normalize(mainBundle)));
          expect(styles).toContain(`url('http://deploy.url/assets/global-img-absolute.svg')`);
          expect(main).toContain(`url('http://deploy.url/assets/component-img-absolute.svg')`);
        }),
        // Check with schemeless base-href and deploy-url flags.
        concatMap(() => runTargetSpec(host, browserTargetSpec,
          { extractCss: true, baseHref: '/base/', deployUrl: 'deploy/' },
        )),
        tap(() => {
          const styles = virtualFs.fileBufferToString(
            host.scopedSync().read(normalize(stylesBundle)),
          );
          const main = virtualFs.fileBufferToString(host.scopedSync().read(normalize(mainBundle)));
          expect(styles).toContain(`url('/base/deploy/assets/global-img-absolute.svg')`);
          expect(main).toContain(`url('/base/deploy/assets/component-img-absolute.svg')`);
        }),
        // Check with identical base-href and deploy-url flags.
        concatMap(() => runTargetSpec(host, browserTargetSpec,
          { extractCss: true, baseHref: '/base/', deployUrl: '/base/' },
        )),
        tap(() => {
          const styles = virtualFs.fileBufferToString(
            host.scopedSync().read(normalize(stylesBundle)),
          );
          const main = virtualFs.fileBufferToString(host.scopedSync().read(normalize(mainBundle)));
          expect(styles).toContain(`url('/base/assets/global-img-absolute.svg')`);
          expect(main).toContain(`url('/base/assets/component-img-absolute.svg')`);
        }),
        // Check with only base-href flag.
        concatMap(() => runTargetSpec(host, browserTargetSpec,
          { extractCss: true, baseHref: '/base/' },
        )),
        tap(() => {
          const styles = virtualFs.fileBufferToString(
            host.scopedSync().read(normalize(stylesBundle)),
          );
          const main = virtualFs.fileBufferToString(host.scopedSync().read(normalize(mainBundle)));
          expect(styles).toContain(`url('/base/assets/global-img-absolute.svg')`);
          expect(main).toContain(`url('/base/assets/component-img-absolute.svg')`);
        }),
      ).toPromise().then(done, done.fail);
    }, 90000);
  
    it(`supports bootstrap@4`, (done) => {
      const overrides = {
        extractCss: true,
        styles: ['../../../../node_modules/bootstrap/dist/css/bootstrap.css'],
        scripts: ['../../../../node_modules/bootstrap/dist/js/bootstrap.js'],
      };
  
      runTargetSpec(host, browserTargetSpec, overrides).pipe(
        tap((buildEvent) => expect(buildEvent.success).toBe(true)),
      ).toPromise().then(done, done.fail);
    });
  
    it(`supports inline javascript in less`, (done) => {
      const overrides = { styles: [`src/styles.less`] };
      host.writeMultipleFiles({
        'src/styles.less': `
          .myFunction() {
            @functions: ~\`(function () {
              return '';
            })()\`;
          }
          .myFunction();
        `,
      });
  
      runTargetSpec(host, browserTargetSpec, overrides).pipe(
        tap((buildEvent) => expect(buildEvent.success).toBe(true)),
      ).toPromise().then(done, done.fail);
    });
  });
  