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