describe(`${name} shadow dom strategy`, () => { it('should support simple components', inject([AsyncTestCompleter, DomTestbed], (async, tb) => { tb.compileAll([ mainDir, new ViewDefinition({ componentId: 'main', template: '<simple>' + '<div>A</div>' + '</simple>', directives: [simple] }), simpleTemplate ]) .then((protoViews) => { tb.createRootViews(protoViews); expect(tb.rootEl).toHaveText('SIMPLE(A)'); async.done(); }); })); it('should not show the light dom even if there is not content tag', inject([AsyncTestCompleter, DomTestbed], (async, tb) => { tb.compileAll([ mainDir, new ViewDefinition({ componentId: 'main', template: '<empty>' + '<div>A</div>' + '</empty>', directives: [empty] }), emptyTemplate ]) .then((protoViews) => { tb.createRootViews(protoViews); expect(tb.rootEl).toHaveText(''); async.done(); }); })); it('should support dynamic components', inject([AsyncTestCompleter, DomTestbed], (async, tb) => { tb.compileAll([ mainDir, new ViewDefinition({ componentId: 'main', template: '<dynamic>' + '<div>A</div>' + '</dynamic>', directives: [dynamicComponent] }), simpleTemplate ]) .then((protoViews) => { var views = tb.createRootViews(ListWrapper.slice(protoViews, 0, 2)); tb.createComponentView(views[1].viewRef, 0, protoViews[2]); expect(tb.rootEl).toHaveText('SIMPLE(A)'); async.done(); }); })); it('should support multiple content tags', inject([AsyncTestCompleter, DomTestbed], (async, tb) => { tb.compileAll([ mainDir, new ViewDefinition({ componentId: 'main', template: '<multiple-content-tags>' + '<div>B</div>' + '<div>C</div>' + '<div class="left">A</div>' + '</multiple-content-tags>', directives: [multipleContentTagsComponent] }), multipleContentTagsTemplate ]) .then((protoViews) => { tb.createRootViews(protoViews); expect(tb.rootEl).toHaveText('(A, BC)'); async.done(); }); })); it('should redistribute only direct children', inject([AsyncTestCompleter, DomTestbed], (async, tb) => { tb.compileAll([ mainDir, new ViewDefinition({ componentId: 'main', template: '<multiple-content-tags>' + '<div>B<div class="left">A</div></div>' + '<div>C</div>' + '</multiple-content-tags>', directives: [multipleContentTagsComponent] }), multipleContentTagsTemplate ]) .then((protoViews) => { tb.createRootViews(protoViews); expect(tb.rootEl).toHaveText('(, BAC)'); async.done(); }); })); it("should redistribute direct child viewcontainers when the light dom changes", inject([AsyncTestCompleter, DomTestbed], (async, tb) => { tb.compileAll([ mainDir, new ViewDefinition({ componentId: 'main', template: '<multiple-content-tags>' + '<div><div template="manual" class="left">A</div></div>' + '<div>B</div>' + '</multiple-content-tags>', directives: [multipleContentTagsComponent, manualViewportDirective] }), multipleContentTagsTemplate ]) .then((protoViews) => { var views = tb.createRootViews(protoViews); var childProtoView = protoViews[1].elementBinders[1].nestedProtoView; expect(tb.rootEl).toHaveText('(, B)'); var childView = tb.createViewInContainer(views[1].viewRef, 1, 0, childProtoView); expect(tb.rootEl).toHaveText('(, AB)'); tb.destroyViewInContainer(views[1].viewRef, 1, 0, childView.viewRef); expect(tb.rootEl).toHaveText('(, B)'); async.done(); }); })); it("should redistribute when the light dom changes", inject([AsyncTestCompleter, DomTestbed], (async, tb) => { tb.compileAll([ mainDir, new ViewDefinition({ componentId: 'main', template: '<multiple-content-tags>' + '<div template="manual" class="left">A</div>' + '<div>B</div>' + '</multiple-content-tags>', directives: [multipleContentTagsComponent, manualViewportDirective] }), multipleContentTagsTemplate ]) .then((protoViews) => { var views = tb.createRootViews(protoViews); var childProtoView = protoViews[1].elementBinders[1].nestedProtoView; expect(tb.rootEl).toHaveText('(, B)'); var childView = tb.createViewInContainer(views[1].viewRef, 1, 0, childProtoView); expect(tb.rootEl).toHaveText('(A, B)'); tb.destroyViewInContainer(views[1].viewRef, 1, 0, childView.viewRef); expect(tb.rootEl).toHaveText('(, B)'); async.done(); }); })); it("should support nested components", inject([AsyncTestCompleter, DomTestbed], (async, tb) => { tb.compileAll([ mainDir, new ViewDefinition({ componentId: 'main', template: '<outer-with-indirect-nested>' + '<div>A</div>' + '<div>B</div>' + '</outer-with-indirect-nested>', directives: [outerWithIndirectNestedComponent] }), outerWithIndirectNestedTemplate, simpleTemplate ]) .then((protoViews) => { tb.createRootViews(protoViews); expect(tb.rootEl).toHaveText('OUTER(SIMPLE(AB))'); async.done(); }); })); it("should support nesting with content being direct child of a nested component", inject([AsyncTestCompleter, DomTestbed], (async, tb) => { tb.compileAll([ mainDir, new ViewDefinition({ componentId: 'main', template: '<outer>' + '<div template="manual" class="left">A</div>' + '<div>B</div>' + '<div>C</div>' + '</outer>', directives: [outerComponent, manualViewportDirective] }), outerTemplate, innerTemplate, innerInnerTemplate ]) .then((protoViews) => { var views = tb.createRootViews(protoViews); var childProtoView = protoViews[1].elementBinders[1].nestedProtoView; expect(tb.rootEl).toHaveText('OUTER(INNER(INNERINNER(,BC)))'); tb.createViewInContainer(views[1].viewRef, 1, 0, childProtoView); expect(tb.rootEl).toHaveText('OUTER(INNER(INNERINNER(A,BC)))'); async.done(); }); })); it('should redistribute when the shadow dom changes', inject([AsyncTestCompleter, DomTestbed], (async, tb) => { tb.compileAll([ mainDir, new ViewDefinition({ componentId: 'main', template: '<conditional-content>' + '<div class="left">A</div>' + '<div>B</div>' + '<div>C</div>' + '</conditional-content>', directives: [conditionalContentComponent] }), conditionalContentTemplate ]) .then((protoViews) => { var views = tb.createRootViews(protoViews); var childProtoView = protoViews[2].elementBinders[0].nestedProtoView; expect(tb.rootEl).toHaveText('(, ABC)'); var childView = tb.createViewInContainer(views[2].viewRef, 0, 0, childProtoView); expect(tb.rootEl).toHaveText('(A, BC)'); tb.destroyViewInContainer(views[2].viewRef, 0, 0, childView.viewRef); expect(tb.rootEl).toHaveText('(, ABC)'); async.done(); }); })); it("should support tabs with view caching", inject([AsyncTestCompleter, DomTestbed], (async, tb) => { tb.compileAll([ mainDir, new ViewDefinition({ componentId: 'main', template: '(<tab><span>0</span></tab>' + '<tab><span>1</span></tab>' + '<tab><span>2</span></tab>)', directives: [tabComponent] }), tabTemplate ]) .then((protoViews) => { var views = tb.createRootViews(ListWrapper.slice(protoViews, 0, 2)); var tabProtoView = protoViews[2]; var tabChildProtoView = tabProtoView.elementBinders[0].nestedProtoView; var tab1View = tb.createComponentView(views[1].viewRef, 0, tabProtoView); var tab2View = tb.createComponentView(views[1].viewRef, 1, tabProtoView); var tab3View = tb.createComponentView(views[1].viewRef, 2, tabProtoView); expect(tb.rootEl).toHaveText('()'); var tabChildView = tb.createViewInContainer(tab1View.viewRef, 0, 0, tabChildProtoView); expect(tb.rootEl).toHaveText('(TAB(0))'); tb.renderer.dehydrateView(tabChildView.viewRef); tb.renderer.detachViewInContainer(tab1View.viewRef, 0, 0, tabChildView.viewRef); tb.renderer.attachViewInContainer(tab2View.viewRef, 0, 0, tabChildView.viewRef); tb.renderer.hydrateView(tabChildView.viewRef); expect(tb.rootEl).toHaveText('(TAB(1))'); tb.renderer.dehydrateView(tabChildView.viewRef); tb.renderer.detachViewInContainer(tab2View.viewRef, 0, 0, tabChildView.viewRef); tb.renderer.attachViewInContainer(tab3View.viewRef, 0, 0, tabChildView.viewRef); tb.renderer.hydrateView(tabChildView.viewRef); expect(tb.rootEl).toHaveText('(TAB(2))'); async.done(); }); })); // Implement once ElementRef support changing a class // it("should redistribute when a class has been added or removed"); // it('should not lose focus', () => { // var temp = `<simple>aaa<input type="text" id="focused-input" ng-class="{'aClass' : // showClass}"> bbb</simple>`; // // compile(temp, (view, lc) => { // var input = view.rootNodes[1]; // input.focus(); // // expect(document.activeElement.id).toEqual("focused-input"); // // // update class of input // // expect(document.activeElement.id).toEqual("focused-input"); // }); //}); });
describe('compiler', function() { var mockStepFactory; function createCompiler(processClosure, urlData = null) { if (isBlank(urlData)) { urlData = MapWrapper.create(); } var tplLoader = new FakeTemplateLoader(urlData); mockStepFactory = new MockStepFactory([new MockStep(processClosure)]); return new Compiler(mockStepFactory, tplLoader); } it('should run the steps and build the ProtoView of the root element', inject([AsyncTestCompleter], (async) => { var compiler = createCompiler((parent, current, control) => { current.inheritedProtoView.bindVariable('b', 'a'); }); compiler.compile(new Template({ componentId: 'someComponent', inline: '<div></div>' })).then((protoView) => { expect(protoView.variableBindings).toEqual(MapWrapper.createFromStringMap({'a': 'b'})); async.done(); }); })); it('should use the inline template and compile in sync', inject([AsyncTestCompleter], (async) => { var compiler = createCompiler(EMPTY_STEP); compiler.compile(new Template({ componentId: 'someId', inline: 'inline component' })).then((protoView) => { expect(DOM.getInnerHTML(protoView.render.delegate.element)).toEqual('inline component'); async.done(); }); })); it('should load url templates', inject([AsyncTestCompleter], (async) => { var urlData = MapWrapper.createFromStringMap({'someUrl': 'url component'}); var compiler = createCompiler(EMPTY_STEP, urlData); compiler.compile(new Template({ componentId: 'someId', absUrl: 'someUrl' })).then((protoView) => { expect(DOM.getInnerHTML(protoView.render.delegate.element)).toEqual('url component'); async.done(); }); })); it('should report loading errors', inject([AsyncTestCompleter], (async) => { var compiler = createCompiler(EMPTY_STEP, MapWrapper.create()); PromiseWrapper.catchError(compiler.compile(new Template({ componentId: 'someId', absUrl: 'someUrl' })), (e) => { expect(e.message).toContain(`Failed to load the template "someId"`); async.done(); }); })); it('should wait for async subtasks to be resolved', inject([AsyncTestCompleter], (async) => { var subTasksCompleted = false; var completer = PromiseWrapper.completer(); var compiler = createCompiler((parent, current, control) => { ListWrapper.push(mockStepFactory.subTaskPromises, completer.promise.then((_) => { subTasksCompleted = true; })); }); var pvPromise = compiler.compile(new Template({ componentId: 'someId', inline: 'some component' })); expect(pvPromise).toBePromise(); expect(subTasksCompleted).toEqual(false); completer.resolve(null); pvPromise.then((protoView) => { expect(subTasksCompleted).toEqual(true); async.done(); }); })); });
describe('runner', () => { var injector: Injector; var runner; function createRunner(defaultBindings = null) { if (isBlank(defaultBindings)) { defaultBindings = []; } runner = new Runner([ defaultBindings, bind(Sampler).toFactory( (_injector) => { injector = _injector; return new MockSampler(); }, [Injector]), bind(Metric).toFactory(() => new MockMetric(), []), bind(Validator).toFactory(() => new MockValidator(), []), bind(WebDriverAdapter).toFactory(() => new MockWebDriverAdapter(), []) ]); return runner; } it('should set SampleDescription.id', inject([AsyncTestCompleter], (async) => { createRunner() .sample({id: 'someId'}) .then((_) => injector.get(SampleDescription)) .then((desc) => { expect(desc.id).toBe('someId'); async.done(); }); })); it('should merge SampleDescription.description', inject([AsyncTestCompleter], (async) => { createRunner([bind(Options.DEFAULT_DESCRIPTION).toValue({'a': 1})]) .sample({id: 'someId', bindings: [bind(Options.SAMPLE_DESCRIPTION).toValue({'b': 2})]}) .then((_) => injector.get(SampleDescription)) .then((desc) => { expect(desc.description) .toEqual( {'forceGc': false, 'userAgent': 'someUserAgent', 'a': 1, 'b': 2, 'v': 11}); async.done(); }); })); it('should fill SampleDescription.metrics from the Metric', inject([AsyncTestCompleter], (async) => { createRunner() .sample({id: 'someId'}) .then((_) => injector.get(SampleDescription)) .then((desc) => { expect(desc.metrics).toEqual({'m1': 'some metric'}); async.done(); }); })); it('should bind Options.EXECUTE', inject([AsyncTestCompleter], (async) => { var execute = () => {}; createRunner() .sample({id: 'someId', execute: execute}) .then((_) => { expect(injector.get(Options.EXECUTE)).toEqual(execute); async.done(); }); })); it('should bind Options.PREPARE', inject([AsyncTestCompleter], (async) => { var prepare = () => {}; createRunner() .sample({id: 'someId', prepare: prepare}) .then((_) => { expect(injector.get(Options.PREPARE)).toEqual(prepare); async.done(); }); })); it('should bind Options.MICRO_METRICS', inject([AsyncTestCompleter], (async) => { createRunner() .sample({id: 'someId', microMetrics: {'a': 'b'}}) .then((_) => { expect(injector.get(Options.MICRO_METRICS)).toEqual({'a': 'b'}); async.done(); }); })); it('should overwrite bindings per sample call', inject([AsyncTestCompleter], (async) => { createRunner([ bind(Options.DEFAULT_DESCRIPTION) .toValue({'a': 1}), ]) .sample({ id: 'someId', bindings: [ bind(Options.DEFAULT_DESCRIPTION) .toValue({'a': 2}), ] }) .then((_) => injector.get(SampleDescription)) .then((desc) => { expect(desc.description['a']).toBe(2); async.done(); }); })); });
describe('StyleUrlResolver', () => { let styleUrlResolver; beforeEach(() => { styleUrlResolver = new StyleUrlResolver(new UrlResolver()); }); it('should resolve "url()" urls', () => { var css = ` .foo { background-image: url("double.jpg"); background-image: url('simple.jpg'); background-image: url(noquote.jpg); }`; var expectedCss = ` .foo { background-image: url('http://ng.io/double.jpg'); background-image: url('http://ng.io/simple.jpg'); background-image: url('http://ng.io/noquote.jpg'); }`; var resolvedCss = styleUrlResolver.resolveUrls(css, 'http://ng.io'); expect(resolvedCss).toEqual(expectedCss); }); it('should resolve "@import" urls', () => { var css = ` @import '1.css'; @import "2.css"; `; var expectedCss = ` @import 'http://ng.io/1.css'; @import 'http://ng.io/2.css'; `; var resolvedCss = styleUrlResolver.resolveUrls(css, 'http://ng.io'); expect(resolvedCss).toEqual(expectedCss); }); it('should resolve "@import url()" urls', () => { var css = ` @import url('3.css'); @import url("4.css"); @import url(5.css); `; var expectedCss = ` @import url('http://ng.io/3.css'); @import url('http://ng.io/4.css'); @import url('http://ng.io/5.css'); `; var resolvedCss = styleUrlResolver.resolveUrls(css, 'http://ng.io'); expect(resolvedCss).toEqual(expectedCss); }); it('should support media query in "@import"', () => { var css = ` @import 'print.css' print; @import url(print.css) print; `; var expectedCss = ` @import 'http://ng.io/print.css' print; @import url('http://ng.io/print.css') print; `; var resolvedCss = styleUrlResolver.resolveUrls(css, 'http://ng.io'); expect(resolvedCss).toEqual(expectedCss); }); });
describe('CssSelector.parse', () => { it('should detect element names', () => { var cssSelector = CssSelector.parse('sometag')[0]; expect(cssSelector.element).toEqual('sometag'); expect(cssSelector.toString()).toEqual('sometag'); }); it('should detect class names', () => { var cssSelector = CssSelector.parse('.someClass')[0]; expect(cssSelector.classNames).toEqual(['someclass']); expect(cssSelector.toString()).toEqual('.someclass'); }); it('should detect attr names', () => { var cssSelector = CssSelector.parse('[attrname]')[0]; expect(cssSelector.attrs).toEqual(['attrname', '']); expect(cssSelector.toString()).toEqual('[attrname]'); }); it('should detect attr values', () => { var cssSelector = CssSelector.parse('[attrname=attrvalue]')[0]; expect(cssSelector.attrs).toEqual(['attrname', 'attrvalue']); expect(cssSelector.toString()).toEqual('[attrname=attrvalue]'); }); it('should detect multiple parts', () => { var cssSelector = CssSelector.parse('sometag[attrname=attrvalue].someclass')[0]; expect(cssSelector.element).toEqual('sometag'); expect(cssSelector.attrs).toEqual(['attrname', 'attrvalue']); expect(cssSelector.classNames).toEqual(['someclass']); expect(cssSelector.toString()).toEqual('sometag.someclass[attrname=attrvalue]'); }); it('should detect multiple attributes', () => { var cssSelector = CssSelector.parse('input[type=text][control]')[0]; expect(cssSelector.element).toEqual('input'); expect(cssSelector.attrs).toEqual(['type', 'text', 'control', '']); expect(cssSelector.toString()).toEqual('input[type=text][control]'); }); it('should detect :not', () => { var cssSelector = CssSelector.parse('sometag:not([attrname=attrvalue].someclass)')[0]; expect(cssSelector.element).toEqual('sometag'); expect(cssSelector.attrs.length).toEqual(0); expect(cssSelector.classNames.length).toEqual(0); var notSelector = cssSelector.notSelectors[0]; expect(notSelector.element).toEqual(null); expect(notSelector.attrs).toEqual(['attrname', 'attrvalue']); expect(notSelector.classNames).toEqual(['someclass']); expect(cssSelector.toString()).toEqual('sometag:not(.someclass[attrname=attrvalue])'); }); it('should detect :not without truthy', () => { var cssSelector = CssSelector.parse(':not([attrname=attrvalue].someclass)')[0]; expect(cssSelector.element).toEqual("*"); var notSelector = cssSelector.notSelectors[0]; expect(notSelector.attrs).toEqual(['attrname', 'attrvalue']); expect(notSelector.classNames).toEqual(['someclass']); expect(cssSelector.toString()).toEqual('*:not(.someclass[attrname=attrvalue])'); }); it('should throw when nested :not', () => { expect(() => { CssSelector.parse('sometag:not(:not([attrname=attrvalue].someclass))')[0]; }) .toThrowError('Nesting :not is not allowed in a selector'); }); it('should throw when multiple selectors in :not', () => { expect(() => { CssSelector.parse('sometag:not(a,b)'); }) .toThrowError('Multiple selectors in :not are not supported'); }); it('should detect lists of selectors', () => { var cssSelectors = CssSelector.parse('.someclass,[attrname=attrvalue], sometag'); expect(cssSelectors.length).toEqual(3); expect(cssSelectors[0].classNames).toEqual(['someclass']); expect(cssSelectors[1].attrs).toEqual(['attrname', 'attrvalue']); expect(cssSelectors[2].element).toEqual('sometag'); }); it('should detect lists of selectors with :not', () => { var cssSelectors = CssSelector.parse('input[type=text], :not(textarea), textbox:not(.special)'); expect(cssSelectors.length).toEqual(3); expect(cssSelectors[0].element).toEqual('input'); expect(cssSelectors[0].attrs).toEqual(['type', 'text']); expect(cssSelectors[1].element).toEqual('*'); expect(cssSelectors[1].notSelectors[0].element).toEqual('textarea'); expect(cssSelectors[2].element).toEqual('textbox'); expect(cssSelectors[2].notSelectors[0].classNames).toEqual(['special']); }); });
describe('exceptions', () => { it('should call the on error callback when it is defined', inject([AsyncTestCompleter], (async) => { macroTask(() => { _zone.overrideOnErrorHandler(logError); var exception = new BaseException('sync'); _zone.run(() => { throw exception; }); expect(_errors.length).toBe(1); expect(_errors[0]).toBe(exception); async.done(); }); })); it('should call onError for errors from microtasks', inject([AsyncTestCompleter], (async) => { _zone.overrideOnErrorHandler(logError); var exception = new BaseException('async'); macroTask(() => { _zone.run(() => { microTask(() => { throw exception; }); }); }); macroTask(() => { expect(_errors.length).toBe(1); expect(_errors[0]).toEqual(exception); async.done(); }, 80); })); it('should call onError when onTurnDone throws and the zone is sync', inject([AsyncTestCompleter], (async) => { var exception = new BaseException('fromOnTurnDone'); _zone.overrideOnErrorHandler(logError); _zone.overrideOnTurnDone(() => { throw exception; }); macroTask(() => { _zone.run(() => {}); }); macroTask(() => { expect(_errors.length).toBe(1); expect(_errors[0]).toEqual(exception); async.done(); }, 80); })); it('should call onError when onTurnDone throws and the zone is async', inject([AsyncTestCompleter], (async) => { var asyncRan = false; var exception = new BaseException('fromOnTurnDone'); _zone.overrideOnErrorHandler(logError); _zone.overrideOnTurnDone(() => { throw exception; }); macroTask(() => { _zone.run(() => { microTask(() => { asyncRan = true; }); }); }); macroTask(() => { expect(asyncRan).toBe(true); expect(_errors.length).toBe(1); expect(_errors[0]).toEqual(exception); async.done(); }, 80); })); });
describe('injector', function() { it('should instantiate a class without dependencies', function() { var injector = new Injector([Engine]); var engine = injector.get(Engine); expect(engine).toBeAnInstanceOf(Engine); }); it('should resolve dependencies based on type information', function() { var injector = new Injector([Engine, Car]); var car = injector.get(Car); expect(car).toBeAnInstanceOf(Car); expect(car.engine).toBeAnInstanceOf(Engine); }); it('should resolve dependencies based on @Inject annotation', function() { var injector = new Injector([TurboEngine, Engine, CarWithInject]); var car = injector.get(CarWithInject); expect(car).toBeAnInstanceOf(CarWithInject); expect(car.engine).toBeAnInstanceOf(TurboEngine); }); it('should throw when no type and not @Inject', function() { expect(() => new Injector([NoAnnotations])).toThrowError('Cannot resolve all parameters for NoAnnotations'); }); it('should cache instances', function() { var injector = new Injector([Engine]); var e1 = injector.get(Engine); var e2 = injector.get(Engine); expect(e1).toBe(e2); }); it('should bind to a value', function() { var injector = new Injector([bind(Engine).toValue("fake engine")]); var engine = injector.get(Engine); expect(engine).toEqual("fake engine"); }); it('should bind to a factory', function() { function sportsCarFactory(e) { return new SportsCar(e); } Object.defineProperty(sportsCarFactory, "parameters", {get: function() { return [[Engine]]; }}); var injector = new Injector([Engine, bind(Car).toFactory(sportsCarFactory)]); var car = injector.get(Car); expect(car).toBeAnInstanceOf(SportsCar); expect(car.engine).toBeAnInstanceOf(Engine); }); it('should bind to an alias', function() { var injector = new Injector([Engine, bind(SportsCar).toClass(SportsCar), bind(Car).toAlias(SportsCar)]); var car = injector.get(Car); var sportsCar = injector.get(SportsCar); expect(car).toBeAnInstanceOf(SportsCar); expect(car).toBe(sportsCar); }); it('should throw when the aliased binding does not exist', function() { var injector = new Injector([bind('car').toAlias(SportsCar)]); expect(() => injector.get('car')).toThrowError('No provider for SportsCar! (car -> SportsCar)'); }); it('should support overriding factory dependencies', function() { var injector = new Injector([Engine, bind(Car).toFactory((e) => new SportsCar(e), [Engine])]); var car = injector.get(Car); expect(car).toBeAnInstanceOf(SportsCar); expect(car.engine).toBeAnInstanceOf(Engine); }); it('should support optional dependencies', function() { var injector = new Injector([CarWithOptionalEngine]); var car = injector.get(CarWithOptionalEngine); expect(car.engine).toEqual(null); }); it("should flatten passed-in bindings", function() { var injector = new Injector([[[Engine, Car]]]); var car = injector.get(Car); expect(car).toBeAnInstanceOf(Car); }); it("should use the last binding " + "when there are mutliple bindings for same token", function() { var injector = new Injector([bind(Engine).toClass(Engine), bind(Engine).toClass(TurboEngine)]); expect(injector.get(Engine)).toBeAnInstanceOf(TurboEngine); }); it('should use non-type tokens', function() { var injector = new Injector([bind('token').toValue('value')]); expect(injector.get('token')).toEqual('value'); }); it('should throw when given invalid bindings', function() { expect(() => new Injector(["blah"])).toThrowError('Invalid binding blah'); expect(() => new Injector([bind("blah")])).toThrowError('Invalid binding blah'); }); it('should provide itself', function() { var parent = new Injector([]); var child = parent.createChild([]); expect(child.get(Injector)).toBe(child); }); it('should throw when no provider defined', function() { var injector = new Injector([]); expect(() => injector.get('NonExisting')).toThrowError('No provider for NonExisting!'); }); it('should show the full path when no provider', function() { var injector = new Injector([CarWithDashboard, Engine, Dashboard]); expect(() => injector.get(CarWithDashboard)).toThrowError('No provider for DashboardSoftware! (CarWithDashboard -> Dashboard -> DashboardSoftware)'); }); it('should throw when trying to instantiate a cyclic dependency', function() { var injector = new Injector([Car, bind(Engine).toClass(CyclicEngine)]); expect(() => injector.get(Car)).toThrowError('Cannot instantiate cyclic dependency! (Car -> Engine -> Car)'); expect(() => injector.asyncGet(Car)).toThrowError('Cannot instantiate cyclic dependency! (Car -> Engine -> Car)'); }); it('should show the full path when error happens in a constructor', function() { var injector = new Injector([Car, bind(Engine).toClass(BrokenEngine)]); try { injector.get(Car); throw "Must throw"; } catch (e) { expect(e.message).toContain("Error during instantiation of Engine! (Car -> Engine)"); } }); it('should instantiate an object after a failed attempt', function() { var isBroken = true; var injector = new Injector([Car, bind(Engine).toFactory(() => isBroken ? new BrokenEngine() : new Engine())]); expect(() => injector.get(Car)).toThrowError(new RegExp("Error")); isBroken = false; expect(injector.get(Car)).toBeAnInstanceOf(Car); }); it('should support null values', () => { var injector = new Injector([bind('null').toValue(null)]); expect(injector.get('null')).toBe(null); }); describe("default bindings", function() { it("should be used when no matching binding found", function() { var injector = new Injector([], {defaultBindings: true}); var car = injector.get(Car); expect(car).toBeAnInstanceOf(Car); }); it("should use the matching binding when it is available", function() { var injector = new Injector([bind(Car).toClass(SportsCar)], {defaultBindings: true}); var car = injector.get(Car); expect(car).toBeAnInstanceOf(SportsCar); }); }); describe("child", function() { it('should load instances from parent injector', function() { var parent = new Injector([Engine]); var child = parent.createChild([]); var engineFromParent = parent.get(Engine); var engineFromChild = child.get(Engine); expect(engineFromChild).toBe(engineFromParent); }); it("should not use the child bindings when resolving the dependencies of a parent binding", function() { var parent = new Injector([Car, Engine]); var child = parent.createChild([bind(Engine).toClass(TurboEngine)]); var carFromChild = child.get(Car); expect(carFromChild.engine).toBeAnInstanceOf(Engine); }); it('should create new instance in a child injector', function() { var parent = new Injector([Engine]); var child = parent.createChild([bind(Engine).toClass(TurboEngine)]); var engineFromParent = parent.get(Engine); var engineFromChild = child.get(Engine); expect(engineFromParent).not.toBe(engineFromChild); expect(engineFromChild).toBeAnInstanceOf(TurboEngine); }); it("should create child injectors without default bindings", function() { var parent = new Injector([], {defaultBindings: true}); var child = parent.createChild([]); var childCar = child.get(Car); var parentCar = parent.get(Car); expect(childCar).toBe(parentCar); }); }); describe("lazy", function() { it("should create dependencies lazily", function() { var injector = new Injector([Engine, CarWithLazyEngine]); var car = injector.get(CarWithLazyEngine); expect(car.engineFactory()).toBeAnInstanceOf(Engine); }); it("should cache instance created lazily", function() { var injector = new Injector([Engine, CarWithLazyEngine]); var car = injector.get(CarWithLazyEngine); var e1 = car.engineFactory(); var e2 = car.engineFactory(); expect(e1).toBe(e2); }); }); });
describe("optional components", () => { describe("contains", () => { var group; beforeEach(() => { group = new ControlGroup( { "required": new Control("requiredValue"), "optional": new Control("optionalValue") }, {"optional": false}); }); // rename contains into has it("should return false when the component is not included", () => { expect(group.contains("optional")).toEqual(false); }) it("should return false when there is no component with the given name", () => { expect(group.contains("something else")).toEqual(false); }); it("should return true when the component is included", () => { expect(group.contains("required")).toEqual(true); group.include("optional"); expect(group.contains("optional")).toEqual(true); }); }); it("should not include an inactive component into the group value", () => { var group = new ControlGroup( {"required": new Control("requiredValue"), "optional": new Control("optionalValue")}, {"optional": false}); expect(group.value).toEqual({"required": "requiredValue"}); group.include("optional"); expect(group.value).toEqual({"required": "requiredValue", "optional": "optionalValue"}); }); it("should not run Validators on an inactive component", () => { var group = new ControlGroup( { "required": new Control("requiredValue", Validators.required), "optional": new Control("", Validators.required) }, {"optional": false}); expect(group.valid).toEqual(true); group.include("optional"); expect(group.valid).toEqual(false); }); describe("valueChanges", () => { var g, c1, c2; beforeEach(() => { c1 = new Control("old1"); c2 = new Control("old2"); g = new ControlGroup({"one": c1, "two": c2}, {"two": true}); }); it("should fire an event after the value has been updated", inject([AsyncTestCompleter], (async) => { ObservableWrapper.subscribe(g.valueChanges, (value) => { expect(g.value).toEqual({'one': 'new1', 'two': 'old2'}); expect(value).toEqual({'one': 'new1', 'two': 'old2'}); async.done(); }); c1.updateValue("new1"); })); it("should fire an event after the control's observable fired an event", inject([AsyncTestCompleter], (async) => { var controlCallbackIsCalled = false; ObservableWrapper.subscribe(c1.valueChanges, (value) => { controlCallbackIsCalled = true; }); ObservableWrapper.subscribe(g.valueChanges, (value) => { expect(controlCallbackIsCalled).toBe(true); async.done(); }); c1.updateValue("new1"); })); it("should fire an event when a control is excluded", inject([AsyncTestCompleter], (async) => { ObservableWrapper.subscribe(g.valueChanges, (value) => { expect(value).toEqual({'one': 'old1'}); async.done(); }); g.exclude("two"); })); it("should fire an event when a control is included", inject([AsyncTestCompleter], (async) => { g.exclude("two"); ObservableWrapper.subscribe(g.valueChanges, (value) => { expect(value).toEqual({'one': 'old1', 'two': 'old2'}); async.done(); }); g.include("two"); })); it("should fire an event every time a control is updated", inject([AsyncTestCompleter], (async) => { var loggedValues = []; ObservableWrapper.subscribe(g.valueChanges, (value) => { ListWrapper.push(loggedValues, value); if (loggedValues.length == 2) { expect(loggedValues) .toEqual([{"one": "new1", "two": "old2"}, {"one": "new1", "two": "new2"}]) async.done(); } }); c1.updateValue("new1"); c2.updateValue("new2"); })); xit("should not fire an event when an excluded control is updated", inject([AsyncTestCompleter], (async) => { // hard to test without hacking zones })); }); });
describe("valueChanges", () => { var g, c1, c2; beforeEach(() => { c1 = new Control("old1"); c2 = new Control("old2"); g = new ControlGroup({"one": c1, "two": c2}, {"two": true}); }); it("should fire an event after the value has been updated", inject([AsyncTestCompleter], (async) => { ObservableWrapper.subscribe(g.valueChanges, (value) => { expect(g.value).toEqual({'one': 'new1', 'two': 'old2'}); expect(value).toEqual({'one': 'new1', 'two': 'old2'}); async.done(); }); c1.updateValue("new1"); })); it("should fire an event after the control's observable fired an event", inject([AsyncTestCompleter], (async) => { var controlCallbackIsCalled = false; ObservableWrapper.subscribe(c1.valueChanges, (value) => { controlCallbackIsCalled = true; }); ObservableWrapper.subscribe(g.valueChanges, (value) => { expect(controlCallbackIsCalled).toBe(true); async.done(); }); c1.updateValue("new1"); })); it("should fire an event when a control is excluded", inject([AsyncTestCompleter], (async) => { ObservableWrapper.subscribe(g.valueChanges, (value) => { expect(value).toEqual({'one': 'old1'}); async.done(); }); g.exclude("two"); })); it("should fire an event when a control is included", inject([AsyncTestCompleter], (async) => { g.exclude("two"); ObservableWrapper.subscribe(g.valueChanges, (value) => { expect(value).toEqual({'one': 'old1', 'two': 'old2'}); async.done(); }); g.include("two"); })); it("should fire an event every time a control is updated", inject([AsyncTestCompleter], (async) => { var loggedValues = []; ObservableWrapper.subscribe(g.valueChanges, (value) => { ListWrapper.push(loggedValues, value); if (loggedValues.length == 2) { expect(loggedValues) .toEqual([{"one": "new1", "two": "old2"}, {"one": "new1", "two": "new2"}]) async.done(); } }); c1.updateValue("new1"); c2.updateValue("new2"); })); xit("should not fire an event when an excluded control is updated", inject([AsyncTestCompleter], (async) => { // hard to test without hacking zones })); });
describe('URLSearchParams', () => { it('should conform to spec', () => { var paramsString = "q=URLUtils.searchParams&topic=api"; var searchParams = new URLSearchParams(paramsString); // Tests borrowed from example at // https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams // Compliant with spec described at https://url.spec.whatwg.org/#urlsearchparams expect(searchParams.has("topic")).toBe(true); expect(searchParams.has("foo")).toBe(false); expect(searchParams.get("topic")).toEqual("api"); expect(searchParams.getAll("topic")).toEqual(["api"]); expect(searchParams.get("foo")).toBe(null); searchParams.append("topic", "webdev"); expect(searchParams.getAll("topic")).toEqual(["api", "webdev"]); expect(searchParams.toString()).toEqual("q=URLUtils.searchParams&topic=api&topic=webdev"); searchParams.delete("topic"); expect(searchParams.toString()).toEqual("q=URLUtils.searchParams"); // Test default constructor expect(new URLSearchParams().toString()).toBe(""); }); it('should support map-like merging operation via setAll()', () => { var mapA = new URLSearchParams('a=1&a=2&a=3&c=8'); var mapB = new URLSearchParams('a=4&a=5&a=6&b=7'); mapA.setAll(mapB); expect(mapA.has('a')).toBe(true); expect(mapA.has('b')).toBe(true); expect(mapA.has('c')).toBe(true); expect(mapA.getAll('a')).toEqual(['4']); expect(mapA.getAll('b')).toEqual(['7']); expect(mapA.getAll('c')).toEqual(['8']); expect(mapA.toString()).toEqual('a=4&c=8&b=7'); }); it('should support multimap-like merging operation via appendAll()', () => { var mapA = new URLSearchParams('a=1&a=2&a=3&c=8'); var mapB = new URLSearchParams('a=4&a=5&a=6&b=7'); mapA.appendAll(mapB); expect(mapA.has('a')).toBe(true); expect(mapA.has('b')).toBe(true); expect(mapA.has('c')).toBe(true); expect(mapA.getAll('a')).toEqual(['1', '2', '3', '4', '5', '6']); expect(mapA.getAll('b')).toEqual(['7']); expect(mapA.getAll('c')).toEqual(['8']); expect(mapA.toString()).toEqual('a=1&a=2&a=3&a=4&a=5&a=6&c=8&b=7'); }); it('should support multimap-like merging operation via replaceAll()', () => { var mapA = new URLSearchParams('a=1&a=2&a=3&c=8'); var mapB = new URLSearchParams('a=4&a=5&a=6&b=7'); mapA.replaceAll(mapB); expect(mapA.has('a')).toBe(true); expect(mapA.has('b')).toBe(true); expect(mapA.has('c')).toBe(true); expect(mapA.getAll('a')).toEqual(['4', '5', '6']); expect(mapA.getAll('b')).toEqual(['7']); expect(mapA.getAll('c')).toEqual(['8']); expect(mapA.toString()).toEqual('a=4&a=5&a=6&c=8&b=7'); }); });