diff --git a/demos/src/alert/pages/page-one/page-one.ts b/demos/src/alert/pages/page-one/page-one.ts index 4d03164731..b25f5b177c 100644 --- a/demos/src/alert/pages/page-one/page-one.ts +++ b/demos/src/alert/pages/page-one/page-one.ts @@ -58,13 +58,13 @@ export class PageOne { buttons: [ { text: 'Cancel', - handler: (data: any) => { + handler: (data) => { console.log('Cancel clicked'); } }, { text: 'Save', - handler: (data: any) => { + handler: (data) => { console.log('Saved clicked'); } } diff --git a/src/components/alert/alert-options.ts b/src/components/alert/alert-options.ts index 39cd2e8030..454b895ca6 100644 --- a/src/components/alert/alert-options.ts +++ b/src/components/alert/alert-options.ts @@ -28,5 +28,5 @@ export interface AlertButton { text?: string; role?: string; cssClass?: string; - handler?: Function; + handler?: (value: any) => boolean|void; }; diff --git a/src/components/alert/test/basic/app/app.module.ts b/src/components/alert/test/basic/app/app.module.ts index 8b037e7d58..c2bb850b8f 100644 --- a/src/components/alert/test/basic/app/app.module.ts +++ b/src/components/alert/test/basic/app/app.module.ts @@ -11,9 +11,7 @@ import { PageOneModule } from '../pages/page-one/page-one.module'; ], imports: [ BrowserModule, - IonicModule.forRoot(AppComponent, { - mode: 'ios' - }), + IonicModule.forRoot(AppComponent, {}), PageOneModule ], bootstrap: [IonicApp] diff --git a/src/components/checkbox/checkbox.ts b/src/components/checkbox/checkbox.ts index b70f3ac96c..ee14c689de 100644 --- a/src/components/checkbox/checkbox.ts +++ b/src/components/checkbox/checkbox.ts @@ -94,8 +94,7 @@ export class Checkbox extends BaseInput implements IonicTapInput, After renderer: Renderer, private _cd: ChangeDetectorRef ) { - super(config, elementRef, renderer, 'checkbox', form, item, null); - this._value = false; + super(config, elementRef, renderer, 'checkbox', false, form, item, null); } /** @@ -110,7 +109,6 @@ export class Checkbox extends BaseInput implements IonicTapInput, After */ @HostListener('click', ['$event']) _click(ev: UIEvent) { - console.debug('checkbox, checked'); ev.preventDefault(); ev.stopPropagation(); this.value = !this.value; @@ -122,6 +120,7 @@ export class Checkbox extends BaseInput implements IonicTapInput, After _inputNormalize(val: any): boolean { return isTrueProperty(val); } + /** * @hidden */ @@ -129,11 +128,4 @@ export class Checkbox extends BaseInput implements IonicTapInput, After this._item && this._item.setElementClass('item-checkbox-checked', val); } - /** - * @hidden - */ - _inputUpdated() { - this._cd.detectChanges(); - } - } diff --git a/src/components/datetime/datetime.ts b/src/components/datetime/datetime.ts index d81913bcac..4351f3da84 100644 --- a/src/components/datetime/datetime.ts +++ b/src/components/datetime/datetime.ts @@ -273,12 +273,11 @@ export const DATETIME_VALUE_ACCESSOR: any = { providers: [DATETIME_VALUE_ACCESSOR], encapsulation: ViewEncapsulation.None, }) -export class DateTime extends BaseInput implements AfterContentInit, ControlValueAccessor, OnDestroy { +export class DateTime extends BaseInput implements AfterContentInit, ControlValueAccessor, OnDestroy { _text: string = ''; _min: DateTimeData; _max: DateTimeData; - _timeValue: DateTimeData = {}; _locale: LocaleData = {}; _picker: Picker; @@ -426,7 +425,7 @@ export class DateTime extends BaseInput implements AfterContentInit, Contro @Optional() item: Item, @Optional() private _pickerCtrl: PickerController ) { - super(config, elementRef, renderer, 'datetime', form, item, null); + super(config, elementRef, renderer, 'datetime', {}, form, item, null); } /** @@ -448,10 +447,17 @@ export class DateTime extends BaseInput implements AfterContentInit, Contro * @hidden */ _inputUpdated() { - updateDate(this._timeValue, this.value); this.updateText(); } + /** + * @hidden + */ + _inputNormalize(val: any): DateTimeData { + updateDate(this._value, val); + return this._value; + } + /** * @hidden */ @@ -743,7 +749,7 @@ export class DateTime extends BaseInput implements AfterContentInit, Contro * @hidden */ getValue(): DateTimeData { - return this._timeValue; + return this._value; } /** diff --git a/src/components/datetime/test/datetime.spec.ts b/src/components/datetime/test/datetime.spec.ts index 06edcaab34..b56c77b4ec 100644 --- a/src/components/datetime/test/datetime.spec.ts +++ b/src/components/datetime/test/datetime.spec.ts @@ -604,10 +604,6 @@ describe('DateTime', () => { datetime.setValue(null); expect(datetime.getValue()).toEqual({}); - datetime.setValue('1994-12-15T13:47:20.789Z'); - datetime.setValue(undefined); - expect(datetime.getValue()).toEqual({}); - datetime.setValue('1994-12-15T13:47:20.789Z'); datetime.setValue(''); expect(datetime.getValue()).toEqual({}); diff --git a/src/components/input/input.ts b/src/components/input/input.ts index f5253ab3c8..b6feec63df 100644 --- a/src/components/input/input.ts +++ b/src/components/input/input.ts @@ -132,7 +132,7 @@ export class TextInput extends BaseInput implements IonicFormInput { @Optional() public ngControl: NgControl, private _dom: DomController ) { - super(config, elementRef, renderer, 'input', form, item, ngControl); + super(config, elementRef, renderer, 'input', '', form, item, ngControl); this._nav = nav; diff --git a/src/components/range/range.ts b/src/components/range/range.ts index 0bc7c3f668..1d048e693f 100644 --- a/src/components/range/range.ts +++ b/src/components/range/range.ts @@ -10,7 +10,6 @@ import { BaseInput } from '../../util/base-input'; import { Item } from '../item/item'; import { Platform } from '../../platform/platform'; import { PointerCoordinates, pointerCoord } from '../../util/dom'; -import { TimeoutDebouncer } from '../../util/debouncer'; import { UIEventManager } from '../../gestures/ui-event-manager'; @@ -139,7 +138,6 @@ export class Range extends BaseInput implements AfterViewInit, ControlValue _barL: string; _barR: string; - _debouncer: TimeoutDebouncer = new TimeoutDebouncer(0); _events: UIEventManager; @ViewChild('slider') public _slider: ElementRef; @@ -268,9 +266,8 @@ export class Range extends BaseInput implements AfterViewInit, ControlValue private _dom: DomController, private _cd: ChangeDetectorRef ) { - super(config, elementRef, renderer, 'range', form, item, null); + super(config, elementRef, renderer, 'range', 0, form, item, null); this._events = new UIEventManager(_plt); - this._value = 0; } /** diff --git a/src/components/range/test/range.spec.ts b/src/components/range/test/range.spec.ts index d60e999a14..4e1855e6fa 100644 --- a/src/components/range/test/range.spec.ts +++ b/src/components/range/test/range.spec.ts @@ -7,6 +7,7 @@ import { commonInputTest, NUMBER_CORPUS } from '../../../util/input-tester'; describe('Range', () => { it('should pass common test', () => { + // TODO, validate range inside bounds const range = createRange(); range._slider = mockElementRef(); commonInputTest(range, { diff --git a/src/components/searchbar/searchbar.ts b/src/components/searchbar/searchbar.ts index 34b8ee9f82..f1e9fc3724 100644 --- a/src/components/searchbar/searchbar.ts +++ b/src/components/searchbar/searchbar.ts @@ -62,7 +62,6 @@ export class Searchbar extends BaseInput { _autocomplete: string = 'off'; _autocorrect: string = 'off'; _isActive: boolean = false; - _debouncer: TimeoutDebouncer = new TimeoutDebouncer(250); _showCancelButton: boolean = false; _animated: boolean = false; @@ -165,7 +164,8 @@ export class Searchbar extends BaseInput { renderer: Renderer, @Optional() ngControl: NgControl ) { - super(config, elementRef, renderer, 'searchbar', null, null, ngControl); + super(config, elementRef, renderer, 'searchbar', '', null, null, ngControl); + this.debounce = 250; } @ViewChild('searchbarInput') _searchbarInput: ElementRef; diff --git a/src/components/segment/segment.ts b/src/components/segment/segment.ts index b4ad72154c..0990f1bcb8 100644 --- a/src/components/segment/segment.ts +++ b/src/components/segment/segment.ts @@ -77,7 +77,7 @@ export class Segment extends BaseInput { renderer: Renderer, @Optional() ngControl: NgControl ) { - super(config, elementRef, renderer, 'segment', null, null, ngControl); + super(config, elementRef, renderer, 'segment', null, null, null, ngControl); } /** diff --git a/src/components/segment/test/segment.spec.ts b/src/components/segment/test/segment.spec.ts new file mode 100644 index 0000000000..ee0fbe57ae --- /dev/null +++ b/src/components/segment/test/segment.spec.ts @@ -0,0 +1,31 @@ + +import { QueryList } from '@angular/core'; +import { Segment } from '../segment'; +import { SegmentButton } from '../segment-button'; +import { mockConfig, mockElementRef, mockRenderer } from '../../../util/mock-providers'; +import { commonInputTest } from '../../../util/input-tester'; + +describe('Segment', () => { + + it('should pass common test', () => { + + const config = mockConfig(); + const elementRef = mockElementRef(); + const renderer = mockRenderer(); + const segment = new Segment(config, elementRef, renderer, null); + segment._buttons = new QueryList(); + + commonInputTest(segment, { + defaultValue: null, + corpus: [ + ['option1', 'option1'], + ['option2', 'option2'], + ['option3', 'option3'], + ['option4', 'option4'], + ['', ''], + ] + }); + + }); + +}); diff --git a/src/components/select/select.ts b/src/components/select/select.ts index e27f9770ee..046ff51205 100644 --- a/src/components/select/select.ts +++ b/src/components/select/select.ts @@ -7,7 +7,7 @@ import { App } from '../app/app'; import { Config } from '../../config/config'; import { Form } from '../../util/form'; import { BaseInput } from '../../util/base-input'; -import { isCheckedProperty, isTrueProperty, isBlank, deepCopy } from '../../util/util'; +import { isCheckedProperty, isTrueProperty, isBlank, deepCopy, deepEqual } from '../../util/util'; import { Item } from '../item/item'; import { NavController } from '../../navigation/nav-controller'; import { Option } from '../option/option'; @@ -195,8 +195,7 @@ export class Select extends BaseInput implements AfterViewInit, OnDest @Optional() item: Item, @Optional() private _nav: NavController ) { - super(config, elementRef, renderer, 'select', form, item, null); - this._value = []; + super(config, elementRef, renderer, 'select', [], form, item, null); } @@ -259,7 +258,7 @@ export class Select extends BaseInput implements AfterViewInit, OnDest this.interface = 'alert'; } - let overlay: any; + let overlay: ActionSheet | Alert; if (this.interface === 'action-sheet') { selectOptions.buttons = selectOptions.buttons.concat(options.map(input => { return { @@ -321,7 +320,7 @@ export class Select extends BaseInput implements AfterViewInit, OnDest overlay.addButton({ text: this.okText, - handler: (selectedValues: any) => this.value = selectedValues + handler: (selectedValues) => this.value = selectedValues }); } @@ -373,20 +372,17 @@ export class Select extends BaseInput implements AfterViewInit, OnDest } _inputNormalize(val: any): string[] { - if (val === null) { + if (isBlank(val)) { return []; } if (Array.isArray(val)) { return val; } - return isBlank(val) ? [] : [val]; + return [val + '']; } _inputShouldChange(val: string[]): boolean { - if (val.length === 0 && this._value.length === 0) { - return false; - } - return super._inputShouldChange(val); + return !deepEqual(this._value, val); } /** diff --git a/src/components/select/test/select.spec.ts b/src/components/select/test/select.spec.ts index fc166f3092..c289784c2b 100644 --- a/src/components/select/test/select.spec.ts +++ b/src/components/select/test/select.spec.ts @@ -1,7 +1,7 @@ import { Select } from '../select'; import { mockApp, mockConfig, mockElementRef, mockRenderer, mockItem, mockForm } from '../../../util/mock-providers'; -import { commonInputTest, BOOLEAN_CORPUS } from '../../../util/input-tester'; +import { commonInputTest } from '../../../util/input-tester'; describe('Select', () => { @@ -16,8 +16,13 @@ describe('Select', () => { const select = new Select(app, form, config, elementRef, renderer, item, null); commonInputTest(select, { - defaultValue: false, - corpus: BOOLEAN_CORPUS + defaultValue: [], + corpus: [ + [['hola'], ['hola']], + [null, []], + ['hola', ['hola']], + [['hola', 'adios'], ['hola', 'adios']] + ] }); }); diff --git a/src/components/toggle/test/toggle.spec.ts b/src/components/toggle/test/toggle.spec.ts index e62b1572f5..85387eb1df 100644 --- a/src/components/toggle/test/toggle.spec.ts +++ b/src/components/toggle/test/toggle.spec.ts @@ -20,7 +20,7 @@ describe('Toggle', () => { commonInputTest(toggle, { defaultValue: false, - corpus: BOOLEAN_CORPUS + corpus: BOOLEAN_CORPUS, }); }); diff --git a/src/components/toggle/toggle.ts b/src/components/toggle/toggle.ts index 9779e88527..d2410d44f2 100644 --- a/src/components/toggle/toggle.ts +++ b/src/components/toggle/toggle.ts @@ -109,8 +109,7 @@ export class Toggle extends BaseInput implements IonicTapInput, AfterVi private _domCtrl: DomController, private _cd: ChangeDetectorRef ) { - super(config, elementRef, renderer, 'toggle', form, item, null); - this._value = false; + super(config, elementRef, renderer, 'toggle', false, form, item, null); } /** diff --git a/src/util/base-input.ts b/src/util/base-input.ts index 8fe352f0e9..f1247ed2bf 100644 --- a/src/util/base-input.ts +++ b/src/util/base-input.ts @@ -2,7 +2,7 @@ import { ElementRef, EventEmitter, Input, Output, Renderer } from '@angular/core import { ControlValueAccessor } from '@angular/forms'; import { NgControl } from '@angular/forms'; -import { isPresent, isArray, isTrueProperty, assert } from './util'; +import { isPresent, isUndefined, isArray, isTrueProperty, deepCopy, assert } from './util'; import { Ion } from '../components/ion'; import { Config } from '../config/config'; import { Item } from '../components/item/item'; @@ -30,13 +30,13 @@ export interface CommonInput extends ControlValueAccessor { export class BaseInput extends Ion implements CommonInput { - _value: T = null; + _value: T; _onChanged: Function; _onTouched: Function; _isFocus: boolean = false; _labelId: string; _disabled: boolean = false; - _debouncer: TimeoutDebouncer; + _debouncer: TimeoutDebouncer = new TimeoutDebouncer(0); _init: boolean = false; id: string; @@ -66,18 +66,19 @@ export class BaseInput extends Ion implements CommonInput { this.setDisabledState(val); } - constructor( config: Config, elementRef: ElementRef, renderer: Renderer, name: string, + private _defaultValue: T, public _form: Form, public _item: Item, ngControl: NgControl ) { super(config, elementRef, renderer, name); _form && _form.register(this); + this._value = deepCopy(this._defaultValue); if (_item) { this.id = name + '-' + _item.registerInput(name); @@ -103,7 +104,7 @@ export class BaseInput extends Ion implements CommonInput { // 1. Updates the value // 2. Calls _inputUpdated() // 3. Dispatch onChange events - setValue(val: T) { + setValue(val: any) { this.value = val; } @@ -123,19 +124,26 @@ export class BaseInput extends Ion implements CommonInput { } _writeValue(val: any): boolean { - const normalized = this._inputNormalize(val); - const shouldUpdate = this._inputShouldChange(normalized); - if (shouldUpdate) { - console.debug('BaseInput: value changed:', normalized, this); - this._value = normalized; - this._inputCheckHasValue(normalized); - this._inputUpdated(); - if (this._init) { - this.ionChange.emit(this); - } - return true; + if (isUndefined(val)) { + return false; + } + const normalized = (val === null) + ? deepCopy(this._defaultValue) + : this._inputNormalize(val); + + const notUpdate = isUndefined(normalized) || !this._inputShouldChange(normalized); + if (notUpdate) { + return false; + } + + console.debug('BaseInput: value changed:', normalized, this); + this._value = normalized; + this._inputCheckHasValue(normalized); + this._inputUpdated(); + if (this._init) { + this._debouncer.debounce(() => this.ionChange.emit(this)); } - return false; + return true; } /** @@ -224,12 +232,11 @@ export class BaseInput extends Ion implements CommonInput { if (!this._item) { return; } - let hasValue: boolean; - if (isArray(val)) { - hasValue = val.length > 0; - } else { - hasValue = isPresent(val); - } + + const hasValue = isArray(val) + ? val.length > 0 + : isPresent(val); + this._item.setElementClass('input-has-value', hasValue); } @@ -249,7 +256,7 @@ export class BaseInput extends Ion implements CommonInput { * @hidden */ _inputShouldChange(val: T): boolean { - return (typeof val !== 'undefined') && this._value !== val; + return this._value !== val; } /** diff --git a/src/util/input-tester.ts b/src/util/input-tester.ts index 78ad89695d..94b41c3d2a 100644 --- a/src/util/input-tester.ts +++ b/src/util/input-tester.ts @@ -21,7 +21,7 @@ export const NUMBER_CORPUS: any[] = [ [123456789, 123456789], ['1.1234', 1.1234], ['123456789', 123456789], - ['-123456789', -123456789], + ['-123456789', -123456789] ]; export const BOOLEAN_CORPUS: any[] = [ @@ -30,13 +30,11 @@ export const BOOLEAN_CORPUS: any[] = [ ['', true], ['false', false], ['true', true], - ['hola', false] ]; export const ANY_CORPUS: any[] = [ [true, true], [false, false], - [null, null], [0, 0], ['', ''], [' ', ' '], @@ -68,16 +66,36 @@ function testInput(input: BaseInput, config: TestConfig, isInit: boolean) } function testState(input: BaseInput, config: TestConfig, isInit: boolean) { - assert(input._init === isInit, 'input must be init'); - assert(!input._isFocus && !input.isFocus(), 'should not be focus'); - assert(input.value === config.defaultValue, 'default value is wrong'); - + assertEqual(input._init, isInit, 'input must be init'); + assertEqual(input._isFocus, false, 'should not be focus'); + assertEqual(input.isFocus(), false, 'should not be focus'); + assertEqual(input.value, config.defaultValue, 'default value is wrong'); + + let blurCount = 0; + let focusCount = 0; + const subBlur = input.ionBlur.subscribe((ev: any) => { + assertEqual(ev, input, 'ionBlur argument is wrong'); + blurCount++; + }); + const subFocus = input.ionFocus.subscribe((ev: any) => { + assertEqual(ev, input, 'ionFocus argument is wrong'); + focusCount++; + }); input._setFocus(); - assert(input._isFocus && input.isFocus(), 'should be focus'); - input._setFocus(); // it should not crash + assertEqual(input._isFocus, true, 'should be focus'); + assertEqual(input.isFocus(), true, 'should be focus'); + input._setFocus(); + input._setBlur(); - assert(!input._isFocus && !input.isFocus(), 'should not be focus'); + assertEqual(input._isFocus, false, 'should be not focus'); + assertEqual(input.isFocus(), false, 'should be not focus'); input._setBlur(); // it should not crash + + assertEqual(focusCount, 1, 'ionFocus was not called correctly'); + assertEqual(blurCount, 1, 'ionBlur was not called correctly'); + + subBlur.unsubscribe(); + subFocus.unsubscribe(); } function testWriteValue(input: BaseInput, config: TestConfig, isInit: boolean) { @@ -87,27 +105,28 @@ function testWriteValue(input: BaseInput, config: TestConfig, isInit: bool let OnChangeCalled = 0; let OnTouchedCalled = 0; - input.value = config.defaultValue; - // Test ionChange let sub = input.ionChange.subscribe((ev: any) => { - assert(ionChangeCalled === 0, 'internal error'); - assert(ev === input, 'ev is not the input'); - assert(test[1] === ev.value, 'value does not match'); + assertEqual(ionChangeCalled, 0, 'ionChange: internal error'); + assertEqual(ev, input, 'ionChange: ev is not the input'); + assertEqual(ev.value, test[1], 'ionChange: value does not match'); + ionChangeCalled++; }); // Test registerOnChange input.registerOnChange((ev: any) => { - assert(OnChangeCalled === 0, 'internal error'); - assert(ev === input.value, 'ev output does not match'); - assert(test[1] === input.value, 'value does not match'); + assertEqual(OnChangeCalled, 0, 'registerOnChange: internal error'); + assertEqual(input.value, ev, 'registerOnChange: ev output does not match'); + assertEqual(input.value, test[1], 'registerOnChange: value does not match'); + OnChangeCalled++; }); // Test registerOnChange input.registerOnTouched(() => { - assert(OnTouchedCalled === 0, 'internal error'); + assertEqual(OnTouchedCalled, 0, 'registerOnTouched: internal error'); + OnTouchedCalled++; }); @@ -115,28 +134,45 @@ function testWriteValue(input: BaseInput, config: TestConfig, isInit: bool for (i = 0; i < config.corpus.length; i++) { test = config.corpus[i]; input.value = test[0]; - assert(input.value === test[1], 'input/output does not match'); + assertEqual(input.value, test[1], 'loop: input/output does not match'); if (isInit) { - assert(ionChangeCalled === 1, 'ionChange error'); + assertEqual(ionChangeCalled, 1, 'loop: ionChange error'); } else { - assert(ionChangeCalled === 0, 'ionChange error'); + assertEqual(ionChangeCalled, 0, 'loop: ionChange error'); } - assert(OnChangeCalled === 1, 'OnChangeCalled was not called'); - assert(OnTouchedCalled === 1, 'OnTouchedCalled was not called'); + assertEqual(OnChangeCalled, 1, 'loop: OnChangeCalled was not called'); + assertEqual(OnTouchedCalled, 1, 'loop: OnTouchedCalled was not called'); + OnTouchedCalled = OnChangeCalled = ionChangeCalled = 0; + console.log(test[0], input.value); // Set same value (it should not redispatch) input.value = test[0]; - assert(ionChangeCalled === 0, 'ionChange should not be called'); - assert(OnChangeCalled === 0, 'OnChangeCalled should not be called'); + assertEqual(ionChangeCalled, 0, 'loop: ionChange should not be called'); + assertEqual(OnChangeCalled, 0, 'loop: OnChangeCalled should not be called'); // TODO OnTouchedCalled? OnTouchedCalled = OnChangeCalled = ionChangeCalled = 0; } + // Test undefined + input.value = undefined; + assertEqual(input.value, test[1], 'undefined should not change the value'); + assertEqual(ionChangeCalled, 0, 'undefined: ionChange should not be called'); + assertEqual(OnChangeCalled, 0, 'undefined: OnChangeCalled should not be called'); + assertEqual(OnTouchedCalled, 0, 'undefined: OnTouchedCalled should not be called'); + + + // Test null (reset) + test = [null, config.defaultValue]; + input.value = null; + assertEqual(input.value, config.defaultValue, 'null: wrong default value'); + assertEqual(OnChangeCalled, 1, 'null: OnChangeCalled was not called'); + assertEqual(OnTouchedCalled, 1, 'null: OnTouchedCalled was not called'); + + input.registerOnChange(null); input.registerOnTouched(null); sub.unsubscribe(); - input.value = config.defaultValue; } function testNgModelChange(input: BaseInput, config: TestConfig, isInit: boolean) { @@ -148,9 +184,10 @@ function testNgModelChange(input: BaseInput, config: TestConfig, isInit: b // Test ionChange let sub = input.ionChange.subscribe((ev: any) => { - assert(ionChangeCalled === 0, 'internal error'); - assert(ev === input, 'ev output does not match'); - assert(test[1] === ev.value, 'value does not match'); + assertEqual(ionChangeCalled, 0, 'internal error'); + assertEqual(ev, input, 'ev output does not match'); + assertEqual(test[1], ev.value, 'value does not match'); + ionChangeCalled++; }); @@ -169,21 +206,21 @@ function testNgModelChange(input: BaseInput, config: TestConfig, isInit: b test = config.corpus[i]; input.writeValue(test[0]); - assert(input.value === test[1], 'input/output does not match'); + assertEqual(input.value, test[1], 'input/output does not match'); if (isInit) { - assert(ionChangeCalled === 1, 'ionChange error'); + assertEqual(ionChangeCalled, 1, 'ionChange error'); } else { - assert(ionChangeCalled === 0, 'ionChange error'); + assertEqual(ionChangeCalled, 0, 'ionChange error'); } - assert(OnChangeCalled === 0, 'OnChangeCalled should not be called'); - assert(OnTouchedCalled === 0, 'OnTouchedCalled should not be called'); + assertEqual(OnChangeCalled, 0, 'OnChangeCalled should not be called'); + assertEqual(OnTouchedCalled, 0, 'OnTouchedCalled should not be called'); OnTouchedCalled = OnChangeCalled = ionChangeCalled = 0; // Set same value (it should not redispatch) input.writeValue(test[0]); input.value = test[0]; - assert(ionChangeCalled === 0, 'ionChange should not be called'); - assert(OnChangeCalled === 0, 'OnChangeCalled should not be called'); + assertEqual(ionChangeCalled, 0, 'ionChange should not be called'); + assertEqual(OnChangeCalled, 0, 'OnChangeCalled should not be called'); // TODO OnTouchedCalled? OnTouchedCalled = OnChangeCalled = ionChangeCalled = 0; @@ -195,7 +232,19 @@ function testNgModelChange(input: BaseInput, config: TestConfig, isInit: b input.value = config.defaultValue; } +function assertEqual(a: any, b: any, message: string) { + if (!equal(a, b)) { + assert(false, a + ' != ' + b + ' ' + message); + } +} +function equal(a: any, b: any): boolean { + if (a === b) { + return true; + } + // return false; + return JSON.stringify(a) === JSON.stringify(b); +} diff --git a/src/util/test/base-input.spec.ts b/src/util/test/base-input.spec.ts index 97aa217840..5f6e83c6f7 100644 --- a/src/util/test/base-input.spec.ts +++ b/src/util/test/base-input.spec.ts @@ -51,5 +51,5 @@ function mockInput(form: any, item: any, ngControl: any): BaseInput { config = mockConfig(null, '/', platform); elementRef = mockElementRef(); renderer = mockRenderer(); - return new BaseInput(config, elementRef, renderer, 'input', form, item, ngControl); + return new BaseInput(config, elementRef, renderer, 'input', null, form, item, ngControl); } diff --git a/src/util/util.ts b/src/util/util.ts index d9334dce62..915fefffcf 100644 --- a/src/util/util.ts +++ b/src/util/util.ts @@ -16,6 +16,14 @@ export function deepCopy(obj: any) { return JSON.parse(JSON.stringify(obj)); } +/** @hidden */ +export function deepEqual(a: any, b: any) { + if (a === b) { + return true; + } + return JSON.stringify(a) === JSON.stringify(b); +} + /** @hidden */ export function debounce(fn: Function, wait: number, immediate: boolean = false): any { var timeout: number, args: any, context: any, timestamp: number, result: any;