Ejemplo n.º 1
0
  describe('UrlResolver', () => {
    let resolver = new UrlResolver();

    describe('absolute base url', () => {
      it('should add a relative path to the base url', () => {
        expect(resolver.resolve('http://www.foo.com', 'bar')).toEqual('http://www.foo.com/bar');
        expect(resolver.resolve('http://www.foo.com/', 'bar')).toEqual('http://www.foo.com/bar');
        expect(resolver.resolve('http://www.foo.com', './bar')).toEqual('http://www.foo.com/bar');
        expect(resolver.resolve('http://www.foo.com/', './bar')).toEqual('http://www.foo.com/bar');
      });

      it('should replace the base path', () => {
        expect(resolver.resolve('http://www.foo.com/baz', 'bar')).toEqual('http://www.foo.com/bar');
        expect(resolver.resolve('http://www.foo.com/baz', './bar'))
            .toEqual('http://www.foo.com/bar');
      });

      it('should append to the base path', () => {
        expect(resolver.resolve('http://www.foo.com/baz/', 'bar'))
            .toEqual('http://www.foo.com/baz/bar');
        expect(resolver.resolve('http://www.foo.com/baz/', './bar'))
            .toEqual('http://www.foo.com/baz/bar');
      });

      it('should support ".." in the path', () => {
        expect(resolver.resolve('http://www.foo.com/baz/', '../bar'))
            .toEqual('http://www.foo.com/bar');
        expect(resolver.resolve('http://www.foo.com/1/2/3/', '../../bar'))
            .toEqual('http://www.foo.com/1/bar');
        expect(resolver.resolve('http://www.foo.com/1/2/3/', '../biz/bar'))
            .toEqual('http://www.foo.com/1/2/biz/bar');
        expect(resolver.resolve('http://www.foo.com/1/2/baz', '../../bar'))
            .toEqual('http://www.foo.com/bar');
      });

      it('should ignore the base path when the url has a scheme', () => {
        expect(resolver.resolve('http://www.foo.com', 'http://www.bar.com'))
            .toEqual('http://www.bar.com');
      });

      it('should support absolute urls', () => {
        expect(resolver.resolve('http://www.foo.com', '/bar')).toEqual('http://www.foo.com/bar');
        expect(resolver.resolve('http://www.foo.com/', '/bar')).toEqual('http://www.foo.com/bar');
        expect(resolver.resolve('http://www.foo.com/baz', '/bar'))
            .toEqual('http://www.foo.com/bar');
        expect(resolver.resolve('http://www.foo.com/baz/', '/bar'))
            .toEqual('http://www.foo.com/bar');
      });
    });

    describe('relative base url', () => {
      it('should add a relative path to the base url', () => {
        expect(resolver.resolve('foo/', './bar')).toEqual('foo/bar');
        expect(resolver.resolve('foo/baz', './bar')).toEqual('foo/bar');
        expect(resolver.resolve('foo/baz', 'bar')).toEqual('foo/bar');

      });

      it('should support ".." in the path', () => {
        expect(resolver.resolve('foo/baz', '../bar')).toEqual('bar');
        expect(resolver.resolve('foo/baz', '../biz/bar')).toEqual('biz/bar');
      });

      it('should support absolute urls', () => {
        expect(resolver.resolve('foo/baz', '/bar')).toEqual('/bar');
        expect(resolver.resolve('foo/baz/', '/bar')).toEqual('/bar');
      });

      it('should not resolve urls against the baseUrl when the url contains a scheme', () => {
        resolver = new UrlResolver('my_packages_dir');
        expect(resolver.resolve('base/', 'package:file')).toEqual('my_packages_dir/file');
        expect(resolver.resolve('base/', 'http:super_file')).toEqual('http:super_file');
        expect(resolver.resolve('base/', './mega_file')).toEqual('base/mega_file');
      });
    });

    describe('packages', () => {
      it('should resolve a url based on the application package', () => {
        resolver = new UrlResolver('my_packages_dir');
        expect(resolver.resolve(null !, 'package:some/dir/file.txt'))
            .toEqual('my_packages_dir/some/dir/file.txt');
        expect(resolver.resolve(null !, 'some/dir/file.txt')).toEqual('some/dir/file.txt');
      });

      it('should contain a default value of "/" when nothing is provided',
         inject([UrlResolver], (resolver: UrlResolver) => {
           expect(resolver.resolve(null !, 'package:file')).toEqual('/file');
         }));

      it('should resolve a package value when present within the baseurl', () => {
        resolver = new UrlResolver('/my_special_dir');
        expect(resolver.resolve('package:some_dir/', 'matias.html'))
            .toEqual('/my_special_dir/some_dir/matias.html');
      });
    });

    describe('corner and error cases', () => {
      it('should encode URLs before resolving',
         () => {
           expect(resolver.resolve('foo/baz', `<p #p>Hello
        </p>`)).toEqual('foo/%3Cp%20#p%3EHello%0A%20%20%20%20%20%20%20%20%3C/p%3E');
         });
    });
  });
Ejemplo n.º 2
0
    describe('disable() & enable()', () => {
      let a: FormArray;
      let c: FormControl;
      let c2: FormControl;

      beforeEach(() => {
        c = new FormControl(null);
        c2 = new FormControl(null);
        a = new FormArray([c, c2]);
      });

      it('should mark the array as disabled', () => {
        expect(a.disabled).toBe(false);
        expect(a.valid).toBe(true);

        a.disable();
        expect(a.disabled).toBe(true);
        expect(a.valid).toBe(false);

        a.enable();
        expect(a.disabled).toBe(false);
        expect(a.valid).toBe(true);
      });

      it('should set the array status as disabled', () => {
        expect(a.status).toBe('VALID');

        a.disable();
        expect(a.status).toBe('DISABLED');

        a.enable();
        expect(a.status).toBe('VALID');
      });

      it('should mark children of the array as disabled', () => {
        expect(c.disabled).toBe(false);
        expect(c2.disabled).toBe(false);

        a.disable();
        expect(c.disabled).toBe(true);
        expect(c2.disabled).toBe(true);

        a.enable();
        expect(c.disabled).toBe(false);
        expect(c2.disabled).toBe(false);
      });

      it('should ignore disabled controls in validation', () => {
        const g = new FormGroup({
          nested: new FormArray([new FormControl(null, Validators.required)]),
          two: new FormControl('two')
        });
        expect(g.valid).toBe(false);

        g.get('nested') !.disable();
        expect(g.valid).toBe(true);

        g.get('nested') !.enable();
        expect(g.valid).toBe(false);
      });

      it('should ignore disabled controls when serializing value', () => {
        const g = new FormGroup(
            {nested: new FormArray([new FormControl('one')]), two: new FormControl('two')});
        expect(g.value).toEqual({'nested': ['one'], 'two': 'two'});

        g.get('nested') !.disable();
        expect(g.value).toEqual({'two': 'two'});

        g.get('nested') !.enable();
        expect(g.value).toEqual({'nested': ['one'], 'two': 'two'});
      });

      it('should ignore disabled controls when determining dirtiness', () => {
        const g = new FormGroup({nested: a, two: new FormControl('two')});
        g.get(['nested', 0]) !.markAsDirty();
        expect(g.dirty).toBe(true);

        g.get('nested') !.disable();
        expect(g.get('nested') !.dirty).toBe(true);
        expect(g.dirty).toEqual(false);

        g.get('nested') !.enable();
        expect(g.dirty).toEqual(true);
      });

      it('should ignore disabled controls when determining touched state', () => {
        const g = new FormGroup({nested: a, two: new FormControl('two')});
        g.get(['nested', 0]) !.markAsTouched();
        expect(g.touched).toBe(true);

        g.get('nested') !.disable();
        expect(g.get('nested') !.touched).toBe(true);
        expect(g.touched).toEqual(false);

        g.get('nested') !.enable();
        expect(g.touched).toEqual(true);
      });

      it('should keep empty, disabled arrays disabled when updating validity', () => {
        const arr = new FormArray([]);
        expect(arr.status).toEqual('VALID');

        arr.disable();
        expect(arr.status).toEqual('DISABLED');

        arr.updateValueAndValidity();
        expect(arr.status).toEqual('DISABLED');

        arr.push(new FormControl({value: '', disabled: true}));
        expect(arr.status).toEqual('DISABLED');

        arr.push(new FormControl());
        expect(arr.status).toEqual('VALID');
      });

      it('should re-enable empty, disabled arrays', () => {
        const arr = new FormArray([]);
        arr.disable();
        expect(arr.status).toEqual('DISABLED');

        arr.enable();
        expect(arr.status).toEqual('VALID');
      });

      it('should not run validators on disabled controls', () => {
        const validator = jasmine.createSpy('validator');
        const arr = new FormArray([new FormControl()], validator);
        expect(validator.calls.count()).toEqual(1);

        arr.disable();
        expect(validator.calls.count()).toEqual(1);

        arr.setValue(['value']);
        expect(validator.calls.count()).toEqual(1);

        arr.enable();
        expect(validator.calls.count()).toEqual(2);
      });

      describe('disabled errors', () => {
        it('should clear out array errors when disabled', () => {
          const arr = new FormArray([new FormControl()], () => ({'expected': true}));
          expect(arr.errors).toEqual({'expected': true});

          arr.disable();
          expect(arr.errors).toEqual(null);

          arr.enable();
          expect(arr.errors).toEqual({'expected': true});
        });

        it('should re-populate array errors when enabled from a child', () => {
          const arr = new FormArray([new FormControl()], () => ({'expected': true}));
          arr.disable();
          expect(arr.errors).toEqual(null);

          arr.push(new FormControl());
          expect(arr.errors).toEqual({'expected': true});
        });

        it('should clear out async array errors when disabled', fakeAsync(() => {
             const arr = new FormArray([new FormControl()], null !, asyncValidator('expected'));
             tick();
             expect(arr.errors).toEqual({'async': true});

             arr.disable();
             expect(arr.errors).toEqual(null);

             arr.enable();
             tick();
             expect(arr.errors).toEqual({'async': true});
           }));

        it('should re-populate async array errors when enabled from a child', fakeAsync(() => {
             const arr = new FormArray([new FormControl()], null !, asyncValidator('expected'));
             tick();
             expect(arr.errors).toEqual({'async': true});

             arr.disable();
             expect(arr.errors).toEqual(null);

             arr.push(new FormControl());
             tick();
             expect(arr.errors).toEqual({'async': true});
           }));
      });

      describe('disabled events', () => {
        let logger: string[];
        let c: FormControl;
        let a: FormArray;
        let form: FormGroup;

        beforeEach(() => {
          logger = [];
          c = new FormControl('', Validators.required);
          a = new FormArray([c]);
          form = new FormGroup({a: a});
        });

        it('should emit value change events in the right order', () => {
          c.valueChanges.subscribe(() => logger.push('control'));
          a.valueChanges.subscribe(() => logger.push('array'));
          form.valueChanges.subscribe(() => logger.push('form'));

          a.disable();
          expect(logger).toEqual(['control', 'array', 'form']);
        });

        it('should emit status change events in the right order', () => {
          c.statusChanges.subscribe(() => logger.push('control'));
          a.statusChanges.subscribe(() => logger.push('array'));
          form.statusChanges.subscribe(() => logger.push('form'));

          a.disable();
          expect(logger).toEqual(['control', 'array', 'form']);
        });

        it('should not emit value change events when emitEvent = false', () => {
          c.valueChanges.subscribe(() => logger.push('control'));
          a.valueChanges.subscribe(() => logger.push('array'));
          form.valueChanges.subscribe(() => logger.push('form'));

          a.disable({emitEvent: false});
          expect(logger).toEqual([]);
          a.enable({emitEvent: false});
          expect(logger).toEqual([]);
        });

        it('should not emit status change events when emitEvent = false', () => {
          c.statusChanges.subscribe(() => logger.push('control'));
          a.statusChanges.subscribe(() => logger.push('array'));
          form.statusChanges.subscribe(() => logger.push('form'));

          a.disable({emitEvent: false});
          expect(logger).toEqual([]);
          a.enable({emitEvent: false});
          expect(logger).toEqual([]);
        });

      });

      describe('setControl()', () => {
        let c: FormControl;
        let a: FormArray;

        beforeEach(() => {
          c = new FormControl('one');
          a = new FormArray([c]);
        });

        it('should replace existing control with new control', () => {
          const c2 = new FormControl('new!', Validators.minLength(10));
          a.setControl(0, c2);

          expect(a.controls[0]).toEqual(c2);
          expect(a.value).toEqual(['new!']);
          expect(a.valid).toBe(false);
        });

        it('should add control if control did not exist before', () => {
          const c2 = new FormControl('new!', Validators.minLength(10));
          a.setControl(1, c2);

          expect(a.controls[1]).toEqual(c2);
          expect(a.value).toEqual(['one', 'new!']);
          expect(a.valid).toBe(false);
        });

        it('should remove control if new control is null', () => {
          a.setControl(0, null !);
          expect(a.controls[0]).not.toBeDefined();
          expect(a.value).toEqual([]);
        });

        it('should only emit value change event once', () => {
          const logger: string[] = [];
          const c2 = new FormControl('new!');
          a.valueChanges.subscribe(() => logger.push('change!'));
          a.setControl(0, c2);
          expect(logger).toEqual(['change!']);
        });

      });

    });
Ejemplo n.º 3
0
  describe('console reporter', () => {
    let reporter: ConsoleReporter;
    let log: string[];

    function createReporter(
        {columnWidth = null, sampleId = null, descriptions = null, metrics = null}: {
          columnWidth?: number | null,
          sampleId?: string | null,
          descriptions?: {[key: string]: any}[] | null,
          metrics?: {[key: string]: any} | null
        }) {
      log = [];
      if (!descriptions) {
        descriptions = [];
      }
      if (sampleId == null) {
        sampleId = 'null';
      }
      const providers: StaticProvider[] = [
        ConsoleReporter.PROVIDERS, {
          provide: SampleDescription,
          useValue: new SampleDescription(sampleId, descriptions, metrics !)
        },
        {provide: ConsoleReporter.PRINT, useValue: (line: string) => log.push(line)}
      ];
      if (columnWidth != null) {
        providers.push({provide: ConsoleReporter.COLUMN_WIDTH, useValue: columnWidth});
      }
      reporter = Injector.create(providers).get(ConsoleReporter);
    }

    it('should print the sample id, description and table header', () => {
      createReporter({
        columnWidth: 8,
        sampleId: 'someSample',
        descriptions: [{'a': 1, 'b': 2}],
        metrics: {'m1': 'some desc', 'm2': 'some other desc'}
      });
      expect(log).toEqual([
        'BENCHMARK someSample',
        'Description:',
        '- a: 1',
        '- b: 2',
        'Metrics:',
        '- m1: some desc',
        '- m2: some other desc',
        '',
        '      m1 |       m2',
        '-------- | --------',
      ]);
    });

    it('should print a table row', () => {
      createReporter({columnWidth: 8, metrics: {'a': '', 'b': ''}});
      log = [];
      reporter.reportMeasureValues(mv(0, 0, {'a': 1.23, 'b': 2}));
      expect(log).toEqual(['    1.23 |     2.00']);
    });

    it('should print the table footer and stats when there is a valid sample', () => {
      createReporter({columnWidth: 8, metrics: {'a': '', 'b': ''}});
      log = [];
      reporter.reportSample([], [mv(0, 0, {'a': 3, 'b': 6}), mv(1, 1, {'a': 5, 'b': 9})]);
      expect(log).toEqual(['======== | ========', '4.00+-25% | 7.50+-20%']);
    });

    it('should print the coefficient of variation only when it is meaningful', () => {
      createReporter({columnWidth: 8, metrics: {'a': '', 'b': ''}});
      log = [];
      reporter.reportSample([], [mv(0, 0, {'a': 3, 'b': 0}), mv(1, 1, {'a': 5, 'b': 0})]);
      expect(log).toEqual(['======== | ========', '4.00+-25% |     0.00']);
    });

  });
Ejemplo n.º 4
0
    describe('setValue', () => {
      let c: FormControl, c2: FormControl, a: FormArray;

      beforeEach(() => {
        c = new FormControl('');
        c2 = new FormControl('');
        a = new FormArray([c, c2]);
      });

      it('should set its own value', () => {
        a.setValue(['one', 'two']);
        expect(a.value).toEqual(['one', 'two']);
      });

      it('should set child values', () => {
        a.setValue(['one', 'two']);
        expect(c.value).toEqual('one');
        expect(c2.value).toEqual('two');
      });

      it('should set values for disabled child controls', () => {
        c2.disable();
        a.setValue(['one', 'two']);
        expect(c2.value).toEqual('two');
        expect(a.value).toEqual(['one']);
        expect(a.getRawValue()).toEqual(['one', 'two']);
      });

      it('should set value for disabled arrays', () => {
        a.disable();
        a.setValue(['one', 'two']);
        expect(c.value).toEqual('one');
        expect(c2.value).toEqual('two');
        expect(a.value).toEqual(['one', 'two']);
      });

      it('should set parent values', () => {
        const form = new FormGroup({'parent': a});
        a.setValue(['one', 'two']);
        expect(form.value).toEqual({'parent': ['one', 'two']});
      });

      it('should not update the parent explicitly specified', () => {
        const form = new FormGroup({'parent': a});
        a.setValue(['one', 'two'], {onlySelf: true});

        expect(form.value).toEqual({parent: ['', '']});
      });

      it('should throw if fields are missing from supplied value (subset)', () => {
        expect(() => a.setValue([, 'two']))
            .toThrowError(new RegExp(`Must supply a value for form control at index: 0`));
      });

      it('should throw if a value is provided for a missing control (superset)', () => {
        expect(() => a.setValue([
          'one', 'two', 'three'
        ])).toThrowError(new RegExp(`Cannot find form control at index 2`));
      });

      it('should throw if a value is not provided for a disabled control', () => {
        c2.disable();
        expect(() => a.setValue(['one']))
            .toThrowError(new RegExp(`Must supply a value for form control at index: 1`));
      });

      it('should throw if no controls are set yet', () => {
        const empty = new FormArray([]);
        expect(() => empty.setValue(['one']))
            .toThrowError(new RegExp(`no form controls registered with this array`));
      });

      describe('setValue() events', () => {
        let form: FormGroup;
        let logger: any[];

        beforeEach(() => {
          form = new FormGroup({'parent': a});
          logger = [];
        });

        it('should emit one valueChange event per control', () => {
          form.valueChanges.subscribe(() => logger.push('form'));
          a.valueChanges.subscribe(() => logger.push('array'));
          c.valueChanges.subscribe(() => logger.push('control1'));
          c2.valueChanges.subscribe(() => logger.push('control2'));

          a.setValue(['one', 'two']);
          expect(logger).toEqual(['control1', 'control2', 'array', 'form']);
        });

        it('should not fire an event when explicitly specified', fakeAsync(() => {
             form.valueChanges.subscribe((value) => { throw 'Should not happen'; });
             a.valueChanges.subscribe((value) => { throw 'Should not happen'; });
             c.valueChanges.subscribe((value) => { throw 'Should not happen'; });
             c2.valueChanges.subscribe((value) => { throw 'Should not happen'; });

             a.setValue(['one', 'two'], {emitEvent: false});
             tick();
           }));

        it('should emit one statusChange event per control', () => {
          form.statusChanges.subscribe(() => logger.push('form'));
          a.statusChanges.subscribe(() => logger.push('array'));
          c.statusChanges.subscribe(() => logger.push('control1'));
          c2.statusChanges.subscribe(() => logger.push('control2'));

          a.setValue(['one', 'two']);
          expect(logger).toEqual(['control1', 'control2', 'array', 'form']);
        });
      });
    });
Ejemplo n.º 5
0
  describe('FormArray', () => {

    describe('adding/removing', () => {
      let a: FormArray;
      let c1: FormControl, c2: FormControl, c3: FormControl;

      beforeEach(() => {
        a = new FormArray([]);
        c1 = new FormControl(1);
        c2 = new FormControl(2);
        c3 = new FormControl(3);
      });

      it('should support pushing', () => {
        a.push(c1);
        expect(a.length).toEqual(1);
        expect(a.controls).toEqual([c1]);
      });

      it('should support removing', () => {
        a.push(c1);
        a.push(c2);
        a.push(c3);

        a.removeAt(1);

        expect(a.controls).toEqual([c1, c3]);
      });

      it('should support inserting', () => {
        a.push(c1);
        a.push(c3);

        a.insert(1, c2);

        expect(a.controls).toEqual([c1, c2, c3]);
      });
    });

    describe('value', () => {
      it('should be the reduced value of the child controls', () => {
        const a = new FormArray([new FormControl(1), new FormControl(2)]);
        expect(a.value).toEqual([1, 2]);
      });

      it('should be an empty array when there are no child controls', () => {
        const a = new FormArray([]);
        expect(a.value).toEqual([]);
      });
    });

    describe('getRawValue()', () => {
      let a: FormArray;

      it('should work with nested form groups/arrays', () => {
        a = new FormArray([
          new FormGroup({'c2': new FormControl('v2'), 'c3': new FormControl('v3')}),
          new FormArray([new FormControl('v4'), new FormControl('v5')])
        ]);
        a.at(0).get('c3') !.disable();
        (a.at(1) as FormArray).at(1).disable();

        expect(a.getRawValue()).toEqual([{'c2': 'v2', 'c3': 'v3'}, ['v4', 'v5']]);
      });
    });

    describe('markAllAsTouched', () => {
      it('should mark all descendants as touched', () => {
        const formArray: FormArray = new FormArray([
          new FormControl('v1'), new FormControl('v2'),
          new FormGroup({'c1': new FormControl('v1')}),
          new FormArray([new FormGroup({'c2': new FormControl('v2')})])
        ]);

        expect(formArray.touched).toBe(false);

        const control1 = formArray.at(0) as FormControl;

        expect(control1.touched).toBe(false);

        const group1 = formArray.at(2) as FormGroup;

        expect(group1.touched).toBe(false);

        const group1Control1 = group1.get('c1') as FormControl;

        expect(group1Control1.touched).toBe(false);

        const innerFormArray = formArray.at(3) as FormArray;

        expect(innerFormArray.touched).toBe(false);

        const innerFormArrayGroup = innerFormArray.at(0) as FormGroup;

        expect(innerFormArrayGroup.touched).toBe(false);

        const innerFormArrayGroupControl1 = innerFormArrayGroup.get('c2') as FormControl;

        expect(innerFormArrayGroupControl1.touched).toBe(false);

        formArray.markAllAsTouched();

        expect(formArray.touched).toBe(true);

        expect(control1.touched).toBe(true);

        expect(group1.touched).toBe(true);

        expect(group1Control1.touched).toBe(true);

        expect(innerFormArray.touched).toBe(true);

        expect(innerFormArrayGroup.touched).toBe(true);

        expect(innerFormArrayGroupControl1.touched).toBe(true);
      });
    });

    describe('setValue', () => {
      let c: FormControl, c2: FormControl, a: FormArray;

      beforeEach(() => {
        c = new FormControl('');
        c2 = new FormControl('');
        a = new FormArray([c, c2]);
      });

      it('should set its own value', () => {
        a.setValue(['one', 'two']);
        expect(a.value).toEqual(['one', 'two']);
      });

      it('should set child values', () => {
        a.setValue(['one', 'two']);
        expect(c.value).toEqual('one');
        expect(c2.value).toEqual('two');
      });

      it('should set values for disabled child controls', () => {
        c2.disable();
        a.setValue(['one', 'two']);
        expect(c2.value).toEqual('two');
        expect(a.value).toEqual(['one']);
        expect(a.getRawValue()).toEqual(['one', 'two']);
      });

      it('should set value for disabled arrays', () => {
        a.disable();
        a.setValue(['one', 'two']);
        expect(c.value).toEqual('one');
        expect(c2.value).toEqual('two');
        expect(a.value).toEqual(['one', 'two']);
      });

      it('should set parent values', () => {
        const form = new FormGroup({'parent': a});
        a.setValue(['one', 'two']);
        expect(form.value).toEqual({'parent': ['one', 'two']});
      });

      it('should not update the parent explicitly specified', () => {
        const form = new FormGroup({'parent': a});
        a.setValue(['one', 'two'], {onlySelf: true});

        expect(form.value).toEqual({parent: ['', '']});
      });

      it('should throw if fields are missing from supplied value (subset)', () => {
        expect(() => a.setValue([, 'two']))
            .toThrowError(new RegExp(`Must supply a value for form control at index: 0`));
      });

      it('should throw if a value is provided for a missing control (superset)', () => {
        expect(() => a.setValue([
          'one', 'two', 'three'
        ])).toThrowError(new RegExp(`Cannot find form control at index 2`));
      });

      it('should throw if a value is not provided for a disabled control', () => {
        c2.disable();
        expect(() => a.setValue(['one']))
            .toThrowError(new RegExp(`Must supply a value for form control at index: 1`));
      });

      it('should throw if no controls are set yet', () => {
        const empty = new FormArray([]);
        expect(() => empty.setValue(['one']))
            .toThrowError(new RegExp(`no form controls registered with this array`));
      });

      describe('setValue() events', () => {
        let form: FormGroup;
        let logger: any[];

        beforeEach(() => {
          form = new FormGroup({'parent': a});
          logger = [];
        });

        it('should emit one valueChange event per control', () => {
          form.valueChanges.subscribe(() => logger.push('form'));
          a.valueChanges.subscribe(() => logger.push('array'));
          c.valueChanges.subscribe(() => logger.push('control1'));
          c2.valueChanges.subscribe(() => logger.push('control2'));

          a.setValue(['one', 'two']);
          expect(logger).toEqual(['control1', 'control2', 'array', 'form']);
        });

        it('should not fire an event when explicitly specified', fakeAsync(() => {
             form.valueChanges.subscribe((value) => { throw 'Should not happen'; });
             a.valueChanges.subscribe((value) => { throw 'Should not happen'; });
             c.valueChanges.subscribe((value) => { throw 'Should not happen'; });
             c2.valueChanges.subscribe((value) => { throw 'Should not happen'; });

             a.setValue(['one', 'two'], {emitEvent: false});
             tick();
           }));

        it('should emit one statusChange event per control', () => {
          form.statusChanges.subscribe(() => logger.push('form'));
          a.statusChanges.subscribe(() => logger.push('array'));
          c.statusChanges.subscribe(() => logger.push('control1'));
          c2.statusChanges.subscribe(() => logger.push('control2'));

          a.setValue(['one', 'two']);
          expect(logger).toEqual(['control1', 'control2', 'array', 'form']);
        });
      });
    });

    describe('patchValue', () => {
      let c: FormControl, c2: FormControl, a: FormArray;

      beforeEach(() => {
        c = new FormControl('');
        c2 = new FormControl('');
        a = new FormArray([c, c2]);
      });

      it('should set its own value', () => {
        a.patchValue(['one', 'two']);
        expect(a.value).toEqual(['one', 'two']);
      });

      it('should set child values', () => {
        a.patchValue(['one', 'two']);
        expect(c.value).toEqual('one');
        expect(c2.value).toEqual('two');
      });

      it('should patch disabled control values', () => {
        c2.disable();
        a.patchValue(['one', 'two']);
        expect(c2.value).toEqual('two');
        expect(a.value).toEqual(['one']);
        expect(a.getRawValue()).toEqual(['one', 'two']);
      });

      it('should patch disabled control arrays', () => {
        a.disable();
        a.patchValue(['one', 'two']);
        expect(c.value).toEqual('one');
        expect(c2.value).toEqual('two');
        expect(a.value).toEqual(['one', 'two']);
      });

      it('should set parent values', () => {
        const form = new FormGroup({'parent': a});
        a.patchValue(['one', 'two']);
        expect(form.value).toEqual({'parent': ['one', 'two']});
      });

      it('should not update the parent explicitly specified', () => {
        const form = new FormGroup({'parent': a});
        a.patchValue(['one', 'two'], {onlySelf: true});

        expect(form.value).toEqual({parent: ['', '']});
      });

      it('should ignore fields that are missing from supplied value (subset)', () => {
        a.patchValue([, 'two']);
        expect(a.value).toEqual(['', 'two']);
      });

      it('should not ignore fields that are null', () => {
        a.patchValue([null]);
        expect(a.value).toEqual([null, '']);
      });

      it('should ignore any value provided for a missing control (superset)', () => {
        a.patchValue([, , 'three']);
        expect(a.value).toEqual(['', '']);
      });

      describe('patchValue() events', () => {
        let form: FormGroup;
        let logger: any[];

        beforeEach(() => {
          form = new FormGroup({'parent': a});
          logger = [];
        });

        it('should emit one valueChange event per control', () => {
          form.valueChanges.subscribe(() => logger.push('form'));
          a.valueChanges.subscribe(() => logger.push('array'));
          c.valueChanges.subscribe(() => logger.push('control1'));
          c2.valueChanges.subscribe(() => logger.push('control2'));

          a.patchValue(['one', 'two']);
          expect(logger).toEqual(['control1', 'control2', 'array', 'form']);
        });

        it('should not emit valueChange events for skipped controls', () => {
          form.valueChanges.subscribe(() => logger.push('form'));
          a.valueChanges.subscribe(() => logger.push('array'));
          c.valueChanges.subscribe(() => logger.push('control1'));
          c2.valueChanges.subscribe(() => logger.push('control2'));

          a.patchValue(['one']);
          expect(logger).toEqual(['control1', 'array', 'form']);
        });

        it('should not fire an event when explicitly specified', fakeAsync(() => {
             form.valueChanges.subscribe((value) => { throw 'Should not happen'; });
             a.valueChanges.subscribe((value) => { throw 'Should not happen'; });
             c.valueChanges.subscribe((value) => { throw 'Should not happen'; });
             c2.valueChanges.subscribe((value) => { throw 'Should not happen'; });

             a.patchValue(['one', 'two'], {emitEvent: false});
             tick();
           }));

        it('should emit one statusChange event per control', () => {
          form.statusChanges.subscribe(() => logger.push('form'));
          a.statusChanges.subscribe(() => logger.push('array'));
          c.statusChanges.subscribe(() => logger.push('control1'));
          c2.statusChanges.subscribe(() => logger.push('control2'));

          a.patchValue(['one', 'two']);
          expect(logger).toEqual(['control1', 'control2', 'array', 'form']);
        });
      });
    });

    describe('reset()', () => {
      let c: FormControl, c2: FormControl, a: FormArray;

      beforeEach(() => {
        c = new FormControl('initial value');
        c2 = new FormControl('');
        a = new FormArray([c, c2]);
      });

      it('should set its own value if value passed', () => {
        a.setValue(['new value', 'new value']);

        a.reset(['initial value', '']);
        expect(a.value).toEqual(['initial value', '']);
      });

      it('should not update the parent when explicitly specified', () => {
        const form = new FormGroup({'a': a});
        a.reset(['one', 'two'], {onlySelf: true});

        expect(form.value).toEqual({a: ['initial value', '']});
      });

      it('should set its own value if boxed value passed', () => {
        a.setValue(['new value', 'new value']);

        a.reset([{value: 'initial value', disabled: false}, '']);
        expect(a.value).toEqual(['initial value', '']);
      });

      it('should clear its own value if no value passed', () => {
        a.setValue(['new value', 'new value']);

        a.reset();
        expect(a.value).toEqual([null, null]);
      });

      it('should set the value of each of its child controls if value passed', () => {
        a.setValue(['new value', 'new value']);

        a.reset(['initial value', '']);
        expect(c.value).toBe('initial value');
        expect(c2.value).toBe('');
      });

      it('should clear the value of each of its child controls if no value', () => {
        a.setValue(['new value', 'new value']);

        a.reset();
        expect(c.value).toBe(null);
        expect(c2.value).toBe(null);
      });

      it('should set the value of its parent if value passed', () => {
        const form = new FormGroup({'a': a});
        a.setValue(['new value', 'new value']);

        a.reset(['initial value', '']);
        expect(form.value).toEqual({'a': ['initial value', '']});
      });

      it('should clear the value of its parent if no value passed', () => {
        const form = new FormGroup({'a': a});
        a.setValue(['new value', 'new value']);

        a.reset();
        expect(form.value).toEqual({'a': [null, null]});
      });

      it('should mark itself as pristine', () => {
        a.markAsDirty();
        expect(a.pristine).toBe(false);

        a.reset();
        expect(a.pristine).toBe(true);
      });

      it('should mark all child controls as pristine', () => {
        c.markAsDirty();
        c2.markAsDirty();
        expect(c.pristine).toBe(false);
        expect(c2.pristine).toBe(false);

        a.reset();
        expect(c.pristine).toBe(true);
        expect(c2.pristine).toBe(true);
      });

      it('should mark the parent as pristine if all siblings pristine', () => {
        const c3 = new FormControl('');
        const form = new FormGroup({'a': a, 'c3': c3});

        a.markAsDirty();
        expect(form.pristine).toBe(false);

        a.reset();
        expect(form.pristine).toBe(true);
      });

      it('should not mark the parent pristine if any dirty siblings', () => {
        const c3 = new FormControl('');
        const form = new FormGroup({'a': a, 'c3': c3});

        a.markAsDirty();
        c3.markAsDirty();
        expect(form.pristine).toBe(false);

        a.reset();
        expect(form.pristine).toBe(false);
      });

      it('should mark itself as untouched', () => {
        a.markAsTouched();
        expect(a.untouched).toBe(false);

        a.reset();
        expect(a.untouched).toBe(true);
      });

      it('should mark all child controls as untouched', () => {
        c.markAsTouched();
        c2.markAsTouched();
        expect(c.untouched).toBe(false);
        expect(c2.untouched).toBe(false);

        a.reset();
        expect(c.untouched).toBe(true);
        expect(c2.untouched).toBe(true);
      });

      it('should mark the parent untouched if all siblings untouched', () => {
        const c3 = new FormControl('');
        const form = new FormGroup({'a': a, 'c3': c3});

        a.markAsTouched();
        expect(form.untouched).toBe(false);

        a.reset();
        expect(form.untouched).toBe(true);
      });

      it('should not mark the parent untouched if any touched siblings', () => {
        const c3 = new FormControl('');
        const form = new FormGroup({'a': a, 'c3': c3});

        a.markAsTouched();
        c3.markAsTouched();
        expect(form.untouched).toBe(false);

        a.reset();
        expect(form.untouched).toBe(false);
      });

      it('should retain previous disabled state', () => {
        a.disable();
        a.reset();

        expect(a.disabled).toBe(true);
      });

      it('should set child disabled state if boxed value passed', () => {
        a.disable();
        a.reset([{value: '', disabled: false}, '']);

        expect(c.disabled).toBe(false);
        expect(a.disabled).toBe(false);
      });


      describe('reset() events', () => {
        let form: FormGroup, c3: FormControl, logger: any[];

        beforeEach(() => {
          c3 = new FormControl('');
          form = new FormGroup({'a': a, 'c3': c3});
          logger = [];
        });

        it('should emit one valueChange event per reset control', () => {
          form.valueChanges.subscribe(() => logger.push('form'));
          a.valueChanges.subscribe(() => logger.push('array'));
          c.valueChanges.subscribe(() => logger.push('control1'));
          c2.valueChanges.subscribe(() => logger.push('control2'));
          c3.valueChanges.subscribe(() => logger.push('control3'));

          a.reset();
          expect(logger).toEqual(['control1', 'control2', 'array', 'form']);
        });

        it('should not fire an event when explicitly specified', fakeAsync(() => {
             form.valueChanges.subscribe((value) => { throw 'Should not happen'; });
             a.valueChanges.subscribe((value) => { throw 'Should not happen'; });
             c.valueChanges.subscribe((value) => { throw 'Should not happen'; });
             c2.valueChanges.subscribe((value) => { throw 'Should not happen'; });
             c3.valueChanges.subscribe((value) => { throw 'Should not happen'; });

             a.reset([], {emitEvent: false});
             tick();
           }));

        it('should emit one statusChange event per reset control', () => {
          form.statusChanges.subscribe(() => logger.push('form'));
          a.statusChanges.subscribe(() => logger.push('array'));
          c.statusChanges.subscribe(() => logger.push('control1'));
          c2.statusChanges.subscribe(() => logger.push('control2'));
          c3.statusChanges.subscribe(() => logger.push('control3'));

          a.reset();
          expect(logger).toEqual(['control1', 'control2', 'array', 'form']);
        });
      });
    });

    describe('errors', () => {
      it('should run the validator when the value changes', () => {
        const simpleValidator = (c: FormArray) =>
            c.controls[0].value != 'correct' ? {'broken': true} : null;

        const c = new FormControl(null);
        const g = new FormArray([c], simpleValidator);

        c.setValue('correct');

        expect(g.valid).toEqual(true);
        expect(g.errors).toEqual(null);

        c.setValue('incorrect');

        expect(g.valid).toEqual(false);
        expect(g.errors).toEqual({'broken': true});
      });
    });


    describe('dirty', () => {
      let c: FormControl;
      let a: FormArray;

      beforeEach(() => {
        c = new FormControl('value');
        a = new FormArray([c]);
      });

      it('should be false after creating a control', () => { expect(a.dirty).toEqual(false); });

      it('should be true after changing the value of the control', () => {
        c.markAsDirty();

        expect(a.dirty).toEqual(true);
      });
    });

    describe('touched', () => {
      let c: FormControl;
      let a: FormArray;

      beforeEach(() => {
        c = new FormControl('value');
        a = new FormArray([c]);
      });

      it('should be false after creating a control', () => { expect(a.touched).toEqual(false); });

      it('should be true after child control is marked as touched', () => {
        c.markAsTouched();

        expect(a.touched).toEqual(true);
      });
    });


    describe('pending', () => {
      let c: FormControl;
      let a: FormArray;

      beforeEach(() => {
        c = new FormControl('value');
        a = new FormArray([c]);
      });

      it('should be false after creating a control', () => {
        expect(c.pending).toEqual(false);
        expect(a.pending).toEqual(false);
      });

      it('should be true after changing the value of the control', () => {
        c.markAsPending();

        expect(c.pending).toEqual(true);
        expect(a.pending).toEqual(true);
      });

      it('should not update the parent when onlySelf = true', () => {
        c.markAsPending({onlySelf: true});

        expect(c.pending).toEqual(true);
        expect(a.pending).toEqual(false);
      });

      describe('status change events', () => {
        let logger: string[];

        beforeEach(() => {
          logger = [];
          a.statusChanges.subscribe((status) => logger.push(status));
        });

        it('should emit event after marking control as pending', () => {
          c.markAsPending();
          expect(logger).toEqual(['PENDING']);
        });

        it('should not emit event from parent when onlySelf is true', () => {
          c.markAsPending({onlySelf: true});
          expect(logger).toEqual([]);
        });

        it('should not emit event when emitEvent = false', () => {
          c.markAsPending({emitEvent: false});
          expect(logger).toEqual([]);
        });

        it('should emit event when parent is markedAsPending', () => {
          a.markAsPending();
          expect(logger).toEqual(['PENDING']);
        });
      });

    });

    describe('valueChanges', () => {
      let a: FormArray;
      let c1: any /** TODO #9100 */, c2: any /** TODO #9100 */;

      beforeEach(() => {
        c1 = new FormControl('old1');
        c2 = new FormControl('old2');
        a = new FormArray([c1, c2]);
      });

      it('should fire an event after the value has been updated',
         inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
           a.valueChanges.subscribe({
             next: (value: any) => {
               expect(a.value).toEqual(['new1', 'old2']);
               expect(value).toEqual(['new1', 'old2']);
               async.done();
             }
           });
           c1.setValue('new1');
         }));

      it('should fire an event after the control\'s observable fired an event',
         inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
           let controlCallbackIsCalled = false;


           c1.valueChanges.subscribe({next: (value: any) => { controlCallbackIsCalled = true; }});

           a.valueChanges.subscribe({
             next: (value: any) => {
               expect(controlCallbackIsCalled).toBe(true);
               async.done();
             }
           });

           c1.setValue('new1');
         }));

      it('should fire an event when a control is removed',
         inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
           a.valueChanges.subscribe({
             next: (value: any) => {
               expect(value).toEqual(['old1']);
               async.done();
             }
           });

           a.removeAt(1);
         }));

      it('should fire an event when a control is added',
         inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
           a.removeAt(1);

           a.valueChanges.subscribe({
             next: (value: any) => {
               expect(value).toEqual(['old1', 'old2']);
               async.done();
             }
           });

           a.push(c2);
         }));
    });

    describe('get', () => {
      it('should return null when path is null', () => {
        const g = new FormGroup({});
        expect(g.get(null !)).toEqual(null);
      });

      it('should return null when path is empty', () => {
        const g = new FormGroup({});
        expect(g.get([])).toEqual(null);
      });

      it('should return null when path is invalid', () => {
        const g = new FormGroup({});
        expect(g.get('invalid')).toEqual(null);
      });

      it('should return a child of a control group', () => {
        const g = new FormGroup({
          'one': new FormControl('111'),
          'nested': new FormGroup({'two': new FormControl('222')})
        });

        expect(g.get(['one']) !.value).toEqual('111');
        expect(g.get('one') !.value).toEqual('111');
        expect(g.get(['nested', 'two']) !.value).toEqual('222');
        expect(g.get('nested.two') !.value).toEqual('222');
      });

      it('should return an element of an array', () => {
        const g = new FormGroup({'array': new FormArray([new FormControl('111')])});

        expect(g.get(['array', 0]) !.value).toEqual('111');
      });
    });

    describe('validator', () => {
      function simpleValidator(c: AbstractControl): ValidationErrors|null {
        return c.get([0]) !.value === 'correct' ? null : {'broken': true};
      }

      function arrayRequiredValidator(c: AbstractControl): ValidationErrors|null {
        return Validators.required(c.get([0]) as AbstractControl);
      }

      it('should set a single validator', () => {
        const a = new FormArray([new FormControl()], simpleValidator);
        expect(a.valid).toBe(false);
        expect(a.errors).toEqual({'broken': true});

        a.setValue(['correct']);
        expect(a.valid).toBe(true);
      });

      it('should set a single validator from options obj', () => {
        const a = new FormArray([new FormControl()], {validators: simpleValidator});
        expect(a.valid).toBe(false);
        expect(a.errors).toEqual({'broken': true});

        a.setValue(['correct']);
        expect(a.valid).toBe(true);
      });

      it('should set multiple validators from an array', () => {
        const a = new FormArray([new FormControl()], [simpleValidator, arrayRequiredValidator]);
        expect(a.valid).toBe(false);
        expect(a.errors).toEqual({'required': true, 'broken': true});

        a.setValue(['c']);
        expect(a.valid).toBe(false);
        expect(a.errors).toEqual({'broken': true});

        a.setValue(['correct']);
        expect(a.valid).toBe(true);
      });

      it('should set multiple validators from options obj', () => {
        const a = new FormArray(
            [new FormControl()], {validators: [simpleValidator, arrayRequiredValidator]});
        expect(a.valid).toBe(false);
        expect(a.errors).toEqual({'required': true, 'broken': true});

        a.setValue(['c']);
        expect(a.valid).toBe(false);
        expect(a.errors).toEqual({'broken': true});

        a.setValue(['correct']);
        expect(a.valid).toBe(true);
      });
    });

    describe('asyncValidator', () => {
      function otherObservableValidator() { return of ({'other': true}); }

      it('should run the async validator', fakeAsync(() => {
           const c = new FormControl('value');
           const g = new FormArray([c], null !, asyncValidator('expected'));

           expect(g.pending).toEqual(true);

           tick();

           expect(g.errors).toEqual({'async': true});
           expect(g.pending).toEqual(false);
         }));

      it('should set a single async validator from options obj', fakeAsync(() => {
           const g = new FormArray(
               [new FormControl('value')], {asyncValidators: asyncValidator('expected')});

           expect(g.pending).toEqual(true);

           tick();

           expect(g.errors).toEqual({'async': true});
           expect(g.pending).toEqual(false);
         }));

      it('should set multiple async validators from an array', fakeAsync(() => {
           const g = new FormArray(
               [new FormControl('value')], null !,
               [asyncValidator('expected'), otherObservableValidator]);

           expect(g.pending).toEqual(true);

           tick();

           expect(g.errors).toEqual({'async': true, 'other': true});
           expect(g.pending).toEqual(false);
         }));

      it('should set multiple async validators from options obj', fakeAsync(() => {
           const g = new FormArray(
               [new FormControl('value')],
               {asyncValidators: [asyncValidator('expected'), otherObservableValidator]});

           expect(g.pending).toEqual(true);

           tick();

           expect(g.errors).toEqual({'async': true, 'other': true});
           expect(g.pending).toEqual(false);
         }));
    });

    describe('disable() & enable()', () => {
      let a: FormArray;
      let c: FormControl;
      let c2: FormControl;

      beforeEach(() => {
        c = new FormControl(null);
        c2 = new FormControl(null);
        a = new FormArray([c, c2]);
      });

      it('should mark the array as disabled', () => {
        expect(a.disabled).toBe(false);
        expect(a.valid).toBe(true);

        a.disable();
        expect(a.disabled).toBe(true);
        expect(a.valid).toBe(false);

        a.enable();
        expect(a.disabled).toBe(false);
        expect(a.valid).toBe(true);
      });

      it('should set the array status as disabled', () => {
        expect(a.status).toBe('VALID');

        a.disable();
        expect(a.status).toBe('DISABLED');

        a.enable();
        expect(a.status).toBe('VALID');
      });

      it('should mark children of the array as disabled', () => {
        expect(c.disabled).toBe(false);
        expect(c2.disabled).toBe(false);

        a.disable();
        expect(c.disabled).toBe(true);
        expect(c2.disabled).toBe(true);

        a.enable();
        expect(c.disabled).toBe(false);
        expect(c2.disabled).toBe(false);
      });

      it('should ignore disabled controls in validation', () => {
        const g = new FormGroup({
          nested: new FormArray([new FormControl(null, Validators.required)]),
          two: new FormControl('two')
        });
        expect(g.valid).toBe(false);

        g.get('nested') !.disable();
        expect(g.valid).toBe(true);

        g.get('nested') !.enable();
        expect(g.valid).toBe(false);
      });

      it('should ignore disabled controls when serializing value', () => {
        const g = new FormGroup(
            {nested: new FormArray([new FormControl('one')]), two: new FormControl('two')});
        expect(g.value).toEqual({'nested': ['one'], 'two': 'two'});

        g.get('nested') !.disable();
        expect(g.value).toEqual({'two': 'two'});

        g.get('nested') !.enable();
        expect(g.value).toEqual({'nested': ['one'], 'two': 'two'});
      });

      it('should ignore disabled controls when determining dirtiness', () => {
        const g = new FormGroup({nested: a, two: new FormControl('two')});
        g.get(['nested', 0]) !.markAsDirty();
        expect(g.dirty).toBe(true);

        g.get('nested') !.disable();
        expect(g.get('nested') !.dirty).toBe(true);
        expect(g.dirty).toEqual(false);

        g.get('nested') !.enable();
        expect(g.dirty).toEqual(true);
      });

      it('should ignore disabled controls when determining touched state', () => {
        const g = new FormGroup({nested: a, two: new FormControl('two')});
        g.get(['nested', 0]) !.markAsTouched();
        expect(g.touched).toBe(true);

        g.get('nested') !.disable();
        expect(g.get('nested') !.touched).toBe(true);
        expect(g.touched).toEqual(false);

        g.get('nested') !.enable();
        expect(g.touched).toEqual(true);
      });

      it('should keep empty, disabled arrays disabled when updating validity', () => {
        const arr = new FormArray([]);
        expect(arr.status).toEqual('VALID');

        arr.disable();
        expect(arr.status).toEqual('DISABLED');

        arr.updateValueAndValidity();
        expect(arr.status).toEqual('DISABLED');

        arr.push(new FormControl({value: '', disabled: true}));
        expect(arr.status).toEqual('DISABLED');

        arr.push(new FormControl());
        expect(arr.status).toEqual('VALID');
      });

      it('should re-enable empty, disabled arrays', () => {
        const arr = new FormArray([]);
        arr.disable();
        expect(arr.status).toEqual('DISABLED');

        arr.enable();
        expect(arr.status).toEqual('VALID');
      });

      it('should not run validators on disabled controls', () => {
        const validator = jasmine.createSpy('validator');
        const arr = new FormArray([new FormControl()], validator);
        expect(validator.calls.count()).toEqual(1);

        arr.disable();
        expect(validator.calls.count()).toEqual(1);

        arr.setValue(['value']);
        expect(validator.calls.count()).toEqual(1);

        arr.enable();
        expect(validator.calls.count()).toEqual(2);
      });

      describe('disabled errors', () => {
        it('should clear out array errors when disabled', () => {
          const arr = new FormArray([new FormControl()], () => ({'expected': true}));
          expect(arr.errors).toEqual({'expected': true});

          arr.disable();
          expect(arr.errors).toEqual(null);

          arr.enable();
          expect(arr.errors).toEqual({'expected': true});
        });

        it('should re-populate array errors when enabled from a child', () => {
          const arr = new FormArray([new FormControl()], () => ({'expected': true}));
          arr.disable();
          expect(arr.errors).toEqual(null);

          arr.push(new FormControl());
          expect(arr.errors).toEqual({'expected': true});
        });

        it('should clear out async array errors when disabled', fakeAsync(() => {
             const arr = new FormArray([new FormControl()], null !, asyncValidator('expected'));
             tick();
             expect(arr.errors).toEqual({'async': true});

             arr.disable();
             expect(arr.errors).toEqual(null);

             arr.enable();
             tick();
             expect(arr.errors).toEqual({'async': true});
           }));

        it('should re-populate async array errors when enabled from a child', fakeAsync(() => {
             const arr = new FormArray([new FormControl()], null !, asyncValidator('expected'));
             tick();
             expect(arr.errors).toEqual({'async': true});

             arr.disable();
             expect(arr.errors).toEqual(null);

             arr.push(new FormControl());
             tick();
             expect(arr.errors).toEqual({'async': true});
           }));
      });

      describe('disabled events', () => {
        let logger: string[];
        let c: FormControl;
        let a: FormArray;
        let form: FormGroup;

        beforeEach(() => {
          logger = [];
          c = new FormControl('', Validators.required);
          a = new FormArray([c]);
          form = new FormGroup({a: a});
        });

        it('should emit value change events in the right order', () => {
          c.valueChanges.subscribe(() => logger.push('control'));
          a.valueChanges.subscribe(() => logger.push('array'));
          form.valueChanges.subscribe(() => logger.push('form'));

          a.disable();
          expect(logger).toEqual(['control', 'array', 'form']);
        });

        it('should emit status change events in the right order', () => {
          c.statusChanges.subscribe(() => logger.push('control'));
          a.statusChanges.subscribe(() => logger.push('array'));
          form.statusChanges.subscribe(() => logger.push('form'));

          a.disable();
          expect(logger).toEqual(['control', 'array', 'form']);
        });

        it('should not emit value change events when emitEvent = false', () => {
          c.valueChanges.subscribe(() => logger.push('control'));
          a.valueChanges.subscribe(() => logger.push('array'));
          form.valueChanges.subscribe(() => logger.push('form'));

          a.disable({emitEvent: false});
          expect(logger).toEqual([]);
          a.enable({emitEvent: false});
          expect(logger).toEqual([]);
        });

        it('should not emit status change events when emitEvent = false', () => {
          c.statusChanges.subscribe(() => logger.push('control'));
          a.statusChanges.subscribe(() => logger.push('array'));
          form.statusChanges.subscribe(() => logger.push('form'));

          a.disable({emitEvent: false});
          expect(logger).toEqual([]);
          a.enable({emitEvent: false});
          expect(logger).toEqual([]);
        });

      });

      describe('setControl()', () => {
        let c: FormControl;
        let a: FormArray;

        beforeEach(() => {
          c = new FormControl('one');
          a = new FormArray([c]);
        });

        it('should replace existing control with new control', () => {
          const c2 = new FormControl('new!', Validators.minLength(10));
          a.setControl(0, c2);

          expect(a.controls[0]).toEqual(c2);
          expect(a.value).toEqual(['new!']);
          expect(a.valid).toBe(false);
        });

        it('should add control if control did not exist before', () => {
          const c2 = new FormControl('new!', Validators.minLength(10));
          a.setControl(1, c2);

          expect(a.controls[1]).toEqual(c2);
          expect(a.value).toEqual(['one', 'new!']);
          expect(a.valid).toBe(false);
        });

        it('should remove control if new control is null', () => {
          a.setControl(0, null !);
          expect(a.controls[0]).not.toBeDefined();
          expect(a.value).toEqual([]);
        });

        it('should only emit value change event once', () => {
          const logger: string[] = [];
          const c2 = new FormControl('new!');
          a.valueChanges.subscribe(() => logger.push('change!'));
          a.setControl(0, c2);
          expect(logger).toEqual(['change!']);
        });

      });

    });
  });
Ejemplo n.º 6
0
export function main() {
  /**
   * Tests the PostMessageBus
   */
  describe('MessageBus', () => {
    let bus: MessageBus;

    beforeEach(() => { bus = createConnectedMessageBus(); });

    it('should pass messages in the same channel from sink to source',
       inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
         const CHANNEL = 'CHANNEL 1';
         const MESSAGE = 'Test message';
         bus.initChannel(CHANNEL, false);

         const fromEmitter = bus.from(CHANNEL);
         fromEmitter.subscribe({
           next: (message: any) => {
             expect(message).toEqual(MESSAGE);
             async.done();
           }
         });
         const toEmitter = bus.to(CHANNEL);
         toEmitter.emit(MESSAGE);
       }));

    it('should broadcast', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
         const CHANNEL = 'CHANNEL 1';
         const MESSAGE = 'TESTING';
         const NUM_LISTENERS = 2;
         bus.initChannel(CHANNEL, false);

         let callCount = 0;
         const emitHandler = (message: any) => {
           expect(message).toEqual(MESSAGE);
           callCount++;
           if (callCount == NUM_LISTENERS) {
             async.done();
           }
         };

         for (let i = 0; i < NUM_LISTENERS; i++) {
           const emitter = bus.from(CHANNEL);
           emitter.subscribe({next: emitHandler});
         }

         const toEmitter = bus.to(CHANNEL);
         toEmitter.emit(MESSAGE);
       }));

    it('should keep channels independent',
       inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
         const CHANNEL_ONE = 'CHANNEL 1';
         const CHANNEL_TWO = 'CHANNEL 2';
         const MESSAGE_ONE = 'This is a message on CHANNEL 1';
         const MESSAGE_TWO = 'This is a message on CHANNEL 2';
         let callCount = 0;
         bus.initChannel(CHANNEL_ONE, false);
         bus.initChannel(CHANNEL_TWO, false);

         const firstFromEmitter = bus.from(CHANNEL_ONE);
         firstFromEmitter.subscribe({
           next: (message: any) => {
             expect(message).toEqual(MESSAGE_ONE);
             callCount++;
             if (callCount == 2) {
               async.done();
             }
           }
         });
         const secondFromEmitter = bus.from(CHANNEL_TWO);
         secondFromEmitter.subscribe({
           next: (message: any) => {
             expect(message).toEqual(MESSAGE_TWO);
             callCount++;
             if (callCount == 2) {
               async.done();
             }
           }
         });

         const firstToEmitter = bus.to(CHANNEL_ONE);
         firstToEmitter.emit(MESSAGE_ONE);

         const secondToEmitter = bus.to(CHANNEL_TWO);
         secondToEmitter.emit(MESSAGE_TWO);
       }));
  });

  describe('PostMessageBusSink', () => {
    let bus: MessageBus;
    const CHANNEL = 'Test Channel';

    function setup(runInZone: boolean, zone: NgZone) {
      bus.attachToZone(zone);
      bus.initChannel(CHANNEL, runInZone);
    }

    /**
     * Flushes pending messages and then runs the given function.
     */
    // TODO(mlaval): timeout is fragile, test to be rewritten
    function flushMessages(fn: () => void) { setTimeout(fn, 50); }

    it('should buffer messages and wait for the zone to exit before sending',
       withModule({providers: [{provide: NgZone, useClass: MockNgZone}]})
           .inject(
               [AsyncTestCompleter, NgZone],
               (async: AsyncTestCompleter, zone: MockNgZone) => {
                 bus = createConnectedMessageBus();
                 setup(true, zone);

                 let wasCalled = false;
                 bus.from(CHANNEL).subscribe({next: (message: any) => { wasCalled = true; }});
                 bus.to(CHANNEL).emit('hi');


                 flushMessages(() => {
                   expect(wasCalled).toBeFalsy();

                   zone.simulateZoneExit();
                   flushMessages(() => {
                     expect(wasCalled).toBeTruthy();
                     async.done();
                   });
                 });
               }),
       500);

    it('should send messages immediately when run outside the zone',
       inject([AsyncTestCompleter, NgZone], (async: AsyncTestCompleter, zone: MockNgZone) => {
         bus = createConnectedMessageBus();
         setup(false, zone);

         let wasCalled = false;
         bus.from(CHANNEL).subscribe({next: (message: any) => { wasCalled = true; }});
         bus.to(CHANNEL).emit('hi');

         flushMessages(() => {
           expect(wasCalled).toBeTruthy();
           async.done();
         });
       }), 10000);
  });
}
    describe('readPerfLog (common)', () => {

      it('should execute a dummy script before reading them',
         inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
           // TODO(tbosch): This seems to be a bug in ChromeDriver:
           // Sometimes it does not report the newest events of the performance log
           // to the WebDriver client unless a script is executed...
           createExtension([]).readPerfLog().then((_) => {
             expect(log).toEqual([['executeScript', '1+1'], ['logs', 'performance']]);
             async.done();
           });
         }));

      ['Rasterize', 'CompositeLayers'].forEach((recordType) => {
        it(`should report ${recordType} as "render"`,
           inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
             createExtension(
                 [
                   chromeTimelineEvents.start(recordType, 1234),
                   chromeTimelineEvents.end(recordType, 2345)
                 ], )
                 .readPerfLog()
                 .then((events) => {
                   expect(events).toEqual([
                     normEvents.start('render', 1.234),
                     normEvents.end('render', 2.345),
                   ]);
                   async.done();
                 });
           }));
      });

      describe('frame metrics', () => {
        it('should report ImplThreadRenderingStats as frame event',
           inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
             createExtension([benchmarkEvents.instant(
                                 'BenchmarkInstrumentation::ImplThreadRenderingStats', 1100,
                                 {'data': {'frame_count': 1}})])
                 .readPerfLog()
                 .then((events) => {
                   expect(events).toEqual([
                     normEvents.instant('frame', 1.1),
                   ]);
                   async.done();
                 });
           }));

        it('should not report ImplThreadRenderingStats with zero frames',
           inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
             createExtension([benchmarkEvents.instant(
                                 'BenchmarkInstrumentation::ImplThreadRenderingStats', 1100,
                                 {'data': {'frame_count': 0}})])
                 .readPerfLog()
                 .then((events) => {
                   expect(events).toEqual([]);
                   async.done();
                 });
           }));

        it('should throw when ImplThreadRenderingStats contains more than one frame',
           inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {

             createExtension([benchmarkEvents.instant(
                                 'BenchmarkInstrumentation::ImplThreadRenderingStats', 1100,
                                 {'data': {'frame_count': 2}})])
                 .readPerfLog()
                 .catch((err): any => {
                   expect(() => {
                     throw err;
                   }).toThrowError('multi-frame render stats not supported');
                   async.done();
                 });
           }));

      });

      it('should report begin timestamps',
         inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
           createExtension([blinkEvents.create('S', 'someName', 1000)])
               .readPerfLog()
               .then((events) => {
                 expect(events).toEqual([normEvents.markStart('someName', 1.0)]);
                 async.done();
               });
         }));

      it('should report end timestamps',
         inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
           createExtension([blinkEvents.create('F', 'someName', 1000)])
               .readPerfLog()
               .then((events) => {
                 expect(events).toEqual([normEvents.markEnd('someName', 1.0)]);
                 async.done();
               });
         }));

      it('should throw an error on buffer overflow',
         inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {

           createExtension(
               [
                 chromeTimelineEvents.start('FunctionCall', 1234),
               ],
               CHROME45_USER_AGENT, 'Tracing.bufferUsage')
               .readPerfLog()
               .catch((err): any => {
                 expect(() => {
                   throw err;
                 }).toThrowError('The DevTools trace buffer filled during the test!');
                 async.done();
               });
         }));

      it('should match chrome browsers', () => {
        expect(createExtension().supports({'browserName': 'chrome'})).toBe(true);

        expect(createExtension().supports({'browserName': 'Chrome'})).toBe(true);
      });

    });
Ejemplo n.º 8
0
  describe('Validators', () => {
    describe('required', () => {
      it('should error on an empty string',
         () => { expect(Validators.required(new FormControl(''))).toEqual({'required': true}); });

      it('should error on null',
         () => { expect(Validators.required(new FormControl(null))).toEqual({'required': true}); });

      it('should not error on undefined', () => {
        expect(Validators.required(new FormControl(undefined))).toEqual({'required': true});
      });

      it('should not error on a non-empty string',
         () => { expect(Validators.required(new FormControl('not empty'))).toBeNull(); });

      it('should accept zero as valid',
         () => { expect(Validators.required(new FormControl(0))).toBeNull(); });

      it('should error on an empty array',
         () => expect(Validators.required(new FormControl([]))).toEqual({'required': true}));

      it('should not error on a non-empty array',
         () => expect(Validators.required(new FormControl([1, 2]))).toBeNull());
    });

    describe('requiredTrue', () => {
      it('should error on false',
         () => expect(Validators.requiredTrue(new FormControl(false))).toEqual({'required': true}));

      it('should not error on true',
         () => expect(Validators.requiredTrue(new FormControl(true))).toBeNull());
    });

    describe('email', () => {
      it('should error on invalid email',
         () => expect(Validators.email(new FormControl('some text'))).toEqual({'email': true}));

      it('should not error on valid email',
         () => expect(Validators.email(new FormControl('*****@*****.**'))).toBeNull());
    });

    describe('minLength', () => {
      it('should not error on an empty string',
         () => { expect(Validators.minLength(2)(new FormControl(''))).toBeNull(); });

      it('should not error on null',
         () => { expect(Validators.minLength(2)(new FormControl(null))).toBeNull(); });

      it('should not error on undefined',
         () => { expect(Validators.minLength(2)(new FormControl(undefined))).toBeNull(); });

      it('should not error on valid strings',
         () => { expect(Validators.minLength(2)(new FormControl('aa'))).toBeNull(); });

      it('should error on short strings', () => {
        expect(Validators.minLength(2)(new FormControl('a'))).toEqual({
          'minlength': {'requiredLength': 2, 'actualLength': 1}
        });
      });

      it('should not error when FormArray has valid length', () => {
        const fa = new FormArray([new FormControl(''), new FormControl('')]);
        expect(Validators.minLength(2)(fa)).toBeNull();
      });

      it('should error when FormArray has invalid length', () => {
        const fa = new FormArray([new FormControl('')]);
        expect(Validators.minLength(2)(fa)).toEqual({
          'minlength': {'requiredLength': 2, 'actualLength': 1}
        });
      });
    });

    describe('maxLength', () => {
      it('should not error on an empty string',
         () => { expect(Validators.maxLength(2)(new FormControl(''))).toBeNull(); });

      it('should not error on null',
         () => { expect(Validators.maxLength(2)(new FormControl(null))).toBeNull(); });

      it('should not error on undefined',
         () => { expect(Validators.maxLength(2)(new FormControl(undefined))).toBeNull(); });

      it('should not error on valid strings',
         () => { expect(Validators.maxLength(2)(new FormControl('aa'))).toBeNull(); });

      it('should error on long strings', () => {
        expect(Validators.maxLength(2)(new FormControl('aaa'))).toEqual({
          'maxlength': {'requiredLength': 2, 'actualLength': 3}
        });
      });

      it('should not error when FormArray has valid length', () => {
        const fa = new FormArray([new FormControl(''), new FormControl('')]);
        expect(Validators.maxLength(2)(fa)).toBeNull();
      });

      it('should error when FormArray has invalid length', () => {
        const fa = new FormArray([new FormControl(''), new FormControl('')]);
        expect(Validators.maxLength(1)(fa)).toEqual({
          'maxlength': {'requiredLength': 1, 'actualLength': 2}
        });
      });
    });

    describe('pattern', () => {
      it('should not error on an empty string',
         () => { expect(Validators.pattern('[a-zA-Z ]+')(new FormControl(''))).toBeNull(); });

      it('should not error on null',
         () => { expect(Validators.pattern('[a-zA-Z ]+')(new FormControl(null))).toBeNull(); });

      it('should not error on undefined', () => {
        expect(Validators.pattern('[a-zA-Z ]+')(new FormControl(undefined))).toBeNull();
      });

      it('should not error on null value and "null" pattern',
         () => { expect(Validators.pattern('null')(new FormControl(null))).toBeNull(); });

      it('should not error on valid strings',
         () => expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaAA'))).toBeNull());

      it('should error on failure to match string', () => {
        expect(Validators.pattern('[a-zA-Z ]*')(new FormControl('aaa0'))).toEqual({
          'pattern': {'requiredPattern': '^[a-zA-Z ]*$', 'actualValue': 'aaa0'}
        });
      });

      it('should accept RegExp object', () => {
        const pattern: RegExp = new RegExp('[a-zA-Z ]+');
        expect(Validators.pattern(pattern)(new FormControl('aaAA'))).toBeNull();
      });

      it('should error on failure to match RegExp object', () => {
        const pattern: RegExp = new RegExp('^[a-zA-Z ]*$');
        expect(Validators.pattern(pattern)(new FormControl('aaa0'))).toEqual({
          'pattern': {'requiredPattern': '/^[a-zA-Z ]*$/', 'actualValue': 'aaa0'}
        });
      });

      it('should not error on "null" pattern',
         () => expect(Validators.pattern(null !)(new FormControl('aaAA'))).toBeNull());

      it('should not error on "undefined" pattern',
         () => expect(Validators.pattern(undefined !)(new FormControl('aaAA'))).toBeNull());
    });

    describe('compose', () => {
      it('should return null when given null',
         () => { expect(Validators.compose(null !)).toBe(null); });

      it('should collect errors from all the validators', () => {
        const c = Validators.compose([validator('a', true), validator('b', true)]) !;
        expect(c(new FormControl(''))).toEqual({'a': true, 'b': true});
      });

      it('should run validators left to right', () => {
        const c = Validators.compose([validator('a', 1), validator('a', 2)]) !;
        expect(c(new FormControl(''))).toEqual({'a': 2});
      });

      it('should return null when no errors', () => {
        const c = Validators.compose([Validators.nullValidator, Validators.nullValidator]) !;
        expect(c(new FormControl(''))).toBeNull();
      });

      it('should ignore nulls', () => {
        const c = Validators.compose([null !, Validators.required]) !;
        expect(c(new FormControl(''))).toEqual({'required': true});
      });
    });

    describe('composeAsync', () => {

      describe('promises', () => {
        function promiseValidator(response: {[key: string]: any}): AsyncValidatorFn {
          return (c: AbstractControl) => {
            const res = c.value != 'expected' ? response : null;
            return Promise.resolve(res);
          };
        }

        it('should return null when given null',
           () => { expect(Validators.composeAsync(null !)).toBeNull(); });

        it('should collect errors from all the validators', fakeAsync(() => {
             const v = Validators.composeAsync(
                 [promiseValidator({'one': true}), promiseValidator({'two': true})]) !;

             let errorMap: {[key: string]: any} = undefined !;
             first.call(v(new FormControl('invalid')))
                 .subscribe((errors: {[key: string]: any}) => errorMap = errors);
             tick();

             expect(errorMap).toEqual({'one': true, 'two': true});
           }));

        it('should normalize and evaluate async validator-directives correctly', fakeAsync(() => {
             const v = Validators.composeAsync([normalizeAsyncValidator(
                 new AsyncValidatorDirective('expected', {'one': true}))]) !;

             let errorMap: {[key: string]: any} = undefined !;
             first.call(v(new FormControl('invalid')))
                 .subscribe((errors: {[key: string]: any}) => errorMap = errors);
             tick();

             expect(errorMap).toEqual({'one': true});
           }));

        it('should return null when no errors', fakeAsync(() => {
             const v = Validators.composeAsync([promiseValidator({'one': true})]) !;

             let errorMap: {[key: string]: any} = undefined !;
             first.call(v(new FormControl('expected')))
                 .subscribe((errors: {[key: string]: any}) => errorMap = errors);
             tick();

             expect(errorMap).toBeNull();
           }));

        it('should ignore nulls', fakeAsync(() => {
             const v = Validators.composeAsync([promiseValidator({'one': true}), null !]) !;

             let errorMap: {[key: string]: any} = undefined !;
             first.call(v(new FormControl('invalid')))
                 .subscribe((errors: {[key: string]: any}) => errorMap = errors);
             tick();

             expect(errorMap).toEqual({'one': true});
           }));
      });

      describe('observables', () => {
        function observableValidator(response: {[key: string]: any}): AsyncValidatorFn {
          return (c: AbstractControl) => {
            const res = c.value != 'expected' ? response : null;
            return of (res);
          };
        }

        it('should return null when given null',
           () => { expect(Validators.composeAsync(null !)).toBeNull(); });

        it('should collect errors from all the validators', () => {
          const v = Validators.composeAsync(
              [observableValidator({'one': true}), observableValidator({'two': true})]) !;

          let errorMap: {[key: string]: any} = undefined !;
          first.call(v(new FormControl('invalid')))
              .subscribe((errors: {[key: string]: any}) => errorMap = errors);

          expect(errorMap).toEqual({'one': true, 'two': true});
        });

        it('should normalize and evaluate async validator-directives correctly', () => {
          const v = Validators.composeAsync(
              [normalizeAsyncValidator(new AsyncValidatorDirective('expected', {'one': true}))]) !;

          let errorMap: {[key: string]: any} = undefined !;
          first.call(v(new FormControl('invalid')))
              .subscribe((errors: {[key: string]: any}) => errorMap = errors) !;

          expect(errorMap).toEqual({'one': true});
        });

        it('should return null when no errors', () => {
          const v = Validators.composeAsync([observableValidator({'one': true})]) !;

          let errorMap: {[key: string]: any} = undefined !;
          first.call(v(new FormControl('expected')))
              .subscribe((errors: {[key: string]: any}) => errorMap = errors);

          expect(errorMap).toBeNull();
        });

        it('should ignore nulls', () => {
          const v = Validators.composeAsync([observableValidator({'one': true}), null !]) !;

          let errorMap: {[key: string]: any} = undefined !;
          first.call(v(new FormControl('invalid')))
              .subscribe((errors: {[key: string]: any}) => errorMap = errors);

          expect(errorMap).toEqual({'one': true});
        });

        it('should wait for all validators before setting errors', fakeAsync(() => {
             function getTimerObs(time: number, errorMap: {[key: string]: any}): AsyncValidatorFn {
               return (c: AbstractControl) => { return map.call(timer(time), () => errorMap); };
             }

             const v = Validators.composeAsync(
                 [getTimerObs(100, {one: true}), getTimerObs(200, {two: true})]) !;

             let errorMap: {[key: string]: any} = undefined !;
             first.call(v(new FormControl('invalid')))
                 .subscribe((errors: {[key: string]: any}) => errorMap = errors);

             tick(100);
             expect(errorMap).not.toBeDefined(
                 `Expected errors not to be set until all validators came back.`);

             tick(100);
             expect(errorMap).toEqual(
                 {one: true, two: true}, `Expected errors to merge once all validators resolved.`);
           }));

      });

    });
  });
export function main() {
  describe('MockResourceLoader', () => {
    let resourceLoader: MockResourceLoader;

    beforeEach(() => { resourceLoader = new MockResourceLoader(); });

    function expectResponse(
        request: Promise<string>, url: string, response: string, done: () => void = null) {
      function onResponse(text: string): string {
        if (response === null) {
          throw `Unexpected response ${url} -> ${text}`;
        } else {
          expect(text).toEqual(response);
          if (done != null) done();
        }
        return text;
      }

      function onError(error: string): string {
        if (response !== null) {
          throw `Unexpected error ${url}`;
        } else {
          expect(error).toEqual(`Failed to load ${url}`);
          if (done != null) done();
        }
        return error;
      }

      request.then(onResponse, onError);
    }

    it('should return a response from the definitions',
       inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
         const url = '/foo';
         const response = 'bar';
         resourceLoader.when(url, response);
         expectResponse(resourceLoader.get(url), url, response, () => async.done());
         resourceLoader.flush();
       }));

    it('should return an error from the definitions',
       inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
         const url = '/foo';
         const response: string = null;
         resourceLoader.when(url, response);
         expectResponse(resourceLoader.get(url), url, response, () => async.done());
         resourceLoader.flush();
       }));

    it('should return a response from the expectations',
       inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
         const url = '/foo';
         const response = 'bar';
         resourceLoader.expect(url, response);
         expectResponse(resourceLoader.get(url), url, response, () => async.done());
         resourceLoader.flush();
       }));

    it('should return an error from the expectations',
       inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
         const url = '/foo';
         const response: string = null;
         resourceLoader.expect(url, response);
         expectResponse(resourceLoader.get(url), url, response, () => async.done());
         resourceLoader.flush();
       }));

    it('should not reuse expectations', () => {
      const url = '/foo';
      const response = 'bar';
      resourceLoader.expect(url, response);
      resourceLoader.get(url);
      resourceLoader.get(url);
      expect(() => { resourceLoader.flush(); }).toThrowError('Unexpected request /foo');
    });

    it('should return expectations before definitions',
       inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
         const url = '/foo';
         resourceLoader.when(url, 'when');
         resourceLoader.expect(url, 'expect');
         expectResponse(resourceLoader.get(url), url, 'expect');
         expectResponse(resourceLoader.get(url), url, 'when', () => async.done());
         resourceLoader.flush();
       }));

    it('should throw when there is no definitions or expectations', () => {
      resourceLoader.get('/foo');
      expect(() => { resourceLoader.flush(); }).toThrowError('Unexpected request /foo');
    });

    it('should throw when flush is called without any pending requests', () => {
      expect(() => { resourceLoader.flush(); }).toThrowError('No pending requests to flush');
    });

    it('should throw on unsatisfied expectations', () => {
      resourceLoader.expect('/foo', 'bar');
      resourceLoader.when('/bar', 'foo');
      resourceLoader.get('/bar');
      expect(() => { resourceLoader.flush(); }).toThrowError('Unsatisfied requests: /foo');
    });
  });
}
Ejemplo n.º 10
0
  t.describe('URL sanitizer', () => {
    let logMsgs: string[];
    let originalLog: (msg: any) => any;

    t.beforeEach(() => {
      logMsgs = [];
      originalLog = console.warn;  // Monkey patch DOM.log.
      console.warn = (msg: any) => logMsgs.push(msg);
    });

    afterEach(() => { console.warn = originalLog; });

    t.it('reports unsafe URLs', () => {
      t.expect(_sanitizeUrl('javascript:evil()')).toBe('unsafe:javascript:evil()');
      t.expect(logMsgs.join('\n')).toMatch(/sanitizing unsafe URL value/);
    });

    t.describe('valid URLs', () => {
      const validUrls = [
        '',
        'http://abc',
        'HTTP://abc',
        'https://abc',
        'HTTPS://abc',
        'ftp://abc',
        'FTP://abc',
        'mailto:me@example.com',
        'MAILTO:me@example.com',
        'tel:123-123-1234',
        'TEL:123-123-1234',
        '#anchor',
        '/page1.md',
        'http://JavaScript/my.js',
        'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/',  // Truncated.
        'data:video/webm;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/',
        'data:audio/opus;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/',
      ];
      for (const url of validUrls) {
        t.it(`valid ${url}`, () => t.expect(_sanitizeUrl(url)).toEqual(url));
      }
    });

    t.describe('invalid URLs', () => {
      const invalidUrls = [
        'javascript:evil()',
        'JavaScript:abc',
        'evilNewProtocol:abc',
        ' \n Java\n Script:abc',
        '&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;',
        '&#106&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;',
        '&#106 &#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;',
        '&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058',
        '&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A;',
        'jav&#x09;ascript:alert();',
        'jav\u0000ascript:alert();',
        'data:;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/',
        'data:,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/',
        'data:iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/',
        'data:text/javascript;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/',
        'data:application/x-msdownload;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/',
      ];
      for (const url of invalidUrls) {
        t.it(`valid ${url}`, () => t.expect(_sanitizeUrl(url)).toMatch(/^unsafe:/));
      }
    });

    t.describe('valid srcsets', () => {
      const validSrcsets = [
        '',
        'http://angular.io/images/test.png',
        'http://angular.io/images/test.png, http://angular.io/images/test.png',
        'http://angular.io/images/test.png, http://angular.io/images/test.png, http://angular.io/images/test.png',
        'http://angular.io/images/test.png 2x',
        'http://angular.io/images/test.png 2x, http://angular.io/images/test.png 3x',
        'http://angular.io/images/test.png 1.5x',
        'http://angular.io/images/test.png 1.25x',
        'http://angular.io/images/test.png 200w, http://angular.io/images/test.png 300w',
        'https://angular.io/images/test.png, http://angular.io/images/test.png',
        'http://angular.io:80/images/test.png, http://angular.io:8080/images/test.png',
        'http://www.angular.io:80/images/test.png, http://www.angular.io:8080/images/test.png',
        'https://angular.io/images/test.png, https://angular.io/images/test.png',
        '//angular.io/images/test.png, //angular.io/images/test.png',
        '/images/test.png, /images/test.png',
        'images/test.png, images/test.png',
        'http://angular.io/images/test.png?12345, http://angular.io/images/test.png?12345',
        'http://angular.io/images/test.png?maxage, http://angular.io/images/test.png?maxage',
        'http://angular.io/images/test.png?maxage=234, http://angular.io/images/test.png?maxage=234',
      ];
      for (const srcset of validSrcsets) {
        t.it(`valid ${srcset}`, () => t.expect(sanitizeSrcset(srcset)).toEqual(srcset));
      }
    });

    t.describe('invalid srcsets', () => {
      const invalidSrcsets = [
        'ht:tp://angular.io/images/test.png',
        'http://angular.io/images/test.png, ht:tp://angular.io/images/test.png',
      ];
      for (const srcset of invalidSrcsets) {
        t.it(`valid ${srcset}`, () => t.expect(sanitizeSrcset(srcset)).toMatch(/unsafe:/));
      }
    });

  });