import {describe, xit, it, expect, beforeEach, ddescribe, iit, el} from 'angular2/test_lib'; import {DOM} from 'angular2/src/facade/dom'; import {Map, MapWrapper, ListWrapper} from 'angular2/src/facade/collection'; import {Type, isPresent} from 'angular2/src/facade/lang'; import {Injector} from 'angular2/di'; import {Lexer, Parser, ChangeDetector, dynamicChangeDetection} from 'angular2/change_detection'; import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler'; import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader'; import {NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy'; import {TemplateLoader} from 'angular2/src/core/compiler/template_loader'; import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver'; import {Decorator, Component, Viewport} from 'angular2/src/core/annotations/annotations'; import {Template} from 'angular2/src/core/annotations/template'; import {ViewContainer} from 'angular2/src/core/compiler/view_container'; import {Foreach} from 'angular2/src/directives/foreach'; export function main() { describe('foreach', () => { var view, cd, compiler, component, tplResolver; beforeEach(() => { tplResolver = new FakeTemplateResolver(); compiler = new Compiler(dynamicChangeDetection, new TemplateLoader(null), new DirectiveMetadataReader(), new Parser(new Lexer()), new CompilerCache(), new NativeShadowDomStrategy(), tplResolver); }); function createView(pv) { component = new TestComponent(); view = pv.instantiate(null, null); view.hydrate(new Injector([]), null, component); cd = view.changeDetector; } function compileWithTemplate(html) { var template = new Template({ inline: html, directives: [Foreach] }); tplResolver.setTemplate(TestComponent, template); return compiler.compile(TestComponent); } var TEMPLATE = '
{{item.toString()}};
'; it('should reflect initial elements', (done) => { compileWithTemplate(TEMPLATE).then((pv) => { createView(pv); cd.detectChanges(); expect(DOM.getText(view.nodes[0])).toEqual('1;2;'); done(); }); }); it('should reflect added elements', (done) => { compileWithTemplate(TEMPLATE).then((pv) => { createView(pv); cd.detectChanges(); ListWrapper.push(component.items, 3); cd.detectChanges(); expect(DOM.getText(view.nodes[0])).toEqual('1;2;3;'); done(); }); }); it('should reflect removed elements', (done) => { compileWithTemplate(TEMPLATE).then((pv) => { createView(pv); cd.detectChanges(); ListWrapper.removeAt(component.items, 1); cd.detectChanges(); expect(DOM.getText(view.nodes[0])).toEqual('1;'); done(); }); }); it('should reflect moved elements', (done) => { compileWithTemplate(TEMPLATE).then((pv) => { createView(pv); cd.detectChanges(); ListWrapper.removeAt(component.items, 0); ListWrapper.push(component.items, 1); cd.detectChanges(); expect(DOM.getText(view.nodes[0])).toEqual('2;1;'); done(); }); }); it('should reflect a mix of all changes (additions/removals/moves)', (done) => { compileWithTemplate(TEMPLATE).then((pv) => { createView(pv); component.items = [0, 1, 2, 3, 4, 5]; cd.detectChanges(); component.items = [6, 2, 7, 0, 4, 8]; cd.detectChanges(); expect(DOM.getText(view.nodes[0])).toEqual('6;2;7;0;4;8;'); done(); }); }); it('should iterate over an array of objects', () => { compileWithTemplate('').then((pv) => { createView(pv); // INIT component.items = [{'name': 'misko'}, {'name':'shyam'}]; cd.detectChanges(); expect(DOM.getText(view.nodes[0])).toEqual('misko;shyam;'); // GROW ListWrapper.push(component.items, {'name': 'adam'}); cd.detectChanges(); expect(DOM.getText(view.nodes[0])).toEqual('misko;shyam;adam;'); // SHRINK ListWrapper.removeAt(component.items, 2); ListWrapper.removeAt(component.items, 0); cd.detectChanges(); expect(DOM.getText(view.nodes[0])).toEqual('shyam;'); }); }); it('should gracefully handle nulls', (done) => { compileWithTemplate('').then((pv) => { createView(pv); cd.detectChanges(); expect(DOM.getText(view.nodes[0])).toEqual(''); done(); }); }); it('should gracefully handle ref changing to null and back', (done) => { compileWithTemplate(TEMPLATE).then((pv) => { createView(pv); cd.detectChanges(); expect(DOM.getText(view.nodes[0])).toEqual('1;2;'); component.items = null; cd.detectChanges(); expect(DOM.getText(view.nodes[0])).toEqual(''); component.items = [1, 2, 3]; cd.detectChanges(); expect(DOM.getText(view.nodes[0])).toEqual('1;2;3;'); done(); }); }); it('should throw on ref changing to string', (done) => { compileWithTemplate(TEMPLATE).then((pv) => { createView(pv); cd.detectChanges(); expect(DOM.getText(view.nodes[0])).toEqual('1;2;'); component.items = 'whaaa'; expect(() => cd.detectChanges()).toThrowError(); done(); }); }); it('should works with duplicates', (done) => { compileWithTemplate(TEMPLATE).then((pv) => { createView(pv); var a = new Foo(); component.items = [a, a]; cd.detectChanges(); expect(DOM.getText(view.nodes[0])).toEqual('foo;foo;'); done(); }); }); it('should repeat over nested arrays', (done) => { compileWithTemplate( '
' + '
' + '{{subitem}};' + '
|
' ).then((pv) => { createView(pv); component.items = [['a', 'b'], ['c','d']]; cd.detectChanges(); cd.detectChanges(); cd.detectChanges(); expect(DOM.getText(view.nodes[0])).toEqual('a;b;|c;d;|'); done(); }); }); it('should display indices correctly', (done) => { var INDEX_TEMPLATE = '
{{i.toString()}}
'; compileWithTemplate(INDEX_TEMPLATE).then((pv) => { createView(pv); component.items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; cd.detectChanges(); expect(DOM.getText(view.nodes[0])).toEqual('0123456789'); component.items = [1, 2, 6, 7, 4, 3, 5, 8, 9, 0]; cd.detectChanges(); expect(DOM.getText(view.nodes[0])).toEqual('0123456789'); done(); }); }); }); } class Foo { toString() { return 'foo'; } } @Component({selector: 'test-cmp'}) class TestComponent { items: any; item: any; constructor() { this.items = [1, 2]; } } class FakeTemplateResolver extends TemplateResolver { _cmpTemplates: Map; constructor() { super(); this._cmpTemplates = MapWrapper.create(); } setTemplate(component: Type, template: Template) { MapWrapper.set(this._cmpTemplates, component, template); } resolve(component: Type): Template { var override = MapWrapper.get(this._cmpTemplates, component); if (isPresent(override)) { return override; } return super.resolve(component); } }