Skip to content

Commit

Permalink
Merge pull request #296 from ghiscoding/feat/tests-input-editors
Browse files Browse the repository at this point in the history
fix(editors): complex objects should work with all editors
  • Loading branch information
ghiscoding committed Oct 1, 2019
2 parents 29a5b55 + 07171d8 commit 0b67fea
Show file tree
Hide file tree
Showing 34 changed files with 5,252 additions and 656 deletions.
4 changes: 2 additions & 2 deletions .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ coverage:
default:
target: auto
# Fail the status if coverage drops by >= 3%
threshold: 3
threshold: 3%
branches: null
patch:
default:
threshold: 3
threshold: 5%
25 changes: 17 additions & 8 deletions src/app/examples/grid-editor.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,33 +180,36 @@ export class GridEditorComponent implements OnInit {
}, {
id: 'duration',
name: 'Duration (days)',
field: 'duration',
field: 'task.duration',
minWidth: 100,
filterable: true,
sortable: true,
formatter: Formatters.complexObject,
type: FieldType.number,
exportWithFormatter: true,
filter: { model: Filters.slider, params: { hideSliderNumber: false } },
/*
editor: {
model: Editors.slider,
minValue: 0,
maxValue: 100,
// params: { hideSliderNumber: true },
},
/*
*/
editor: {
// default is 0 decimals, if no decimals is passed it will accept 0 or more decimals
// however if you pass the "decimalPlaces", it will validate with that maximum
alwaysSaveOnEnterKey: true, // defaults to False, when set to true and user presses ENTER it will always call a Save even if value is empty
model: Editors.float,
placeholder: 'enter number',
title: 'Your number must be bigger than 5', title: 'show a custom title', // add a custom title, to see it as a real tooltip you'll need to implement something like tipsy jquery lib
title: 'Your number must be bigger than 5', // add a custom title, to see it as a real tooltip you'll need to implement something like tipsy jquery lib
minValue: 5,
maxValue: 365,
// the default validation error message is in English but you can override it by using "errorMessage"
// errorMessage: this.i18n.tr('INVALID_FLOAT', { maxDecimal: 2 }),
params: { decimalPlaces: 2 },
},
*/

}, {
id: 'complete',
name: '% Complete',
Expand Down Expand Up @@ -247,11 +250,14 @@ export class GridEditorComponent implements OnInit {
}, {
id: 'start',
name: 'Start',
field: 'start',
field: 'task.start',
minWidth: 100,
filterable: true,
filter: { model: Filters.compoundDate },
formatter: Formatters.dateIso,
formatter: Formatters.multiple,
params: {
formatters: [Formatters.complexObject, Formatters.dateIso,]
},
exportWithFormatter: true,
sortable: true,
type: FieldType.date,
Expand Down Expand Up @@ -383,6 +389,7 @@ export class GridEditorComponent implements OnInit {
sortable: true,
type: FieldType.string,
editor: {
placeholder: 'choose option',
collectionAsync: this.http.get<{ value: string; label: string; }[]>(URL_SAMPLE_COLLECTION_DATA),
// OR a regular collection load
// collection: Array.from(Array(100).keys()).map(k => ({ value: k, prefix: 'Task', label: k })),
Expand Down Expand Up @@ -530,10 +537,12 @@ export class GridEditorComponent implements OnInit {
tempDataset.push({
id: i,
title: 'Task ' + i,
duration: (i % 33 === 0) ? null : Math.round(Math.random() * 100) + '',
task: {
duration: (i % 33 === 0) ? null : Math.round(Math.random() * 100) + '',
start: new Date(randomYear, randomMonth, randomDay),
},
percentComplete: randomPercent,
percentCompleteNumber: randomPercent,
start: new Date(randomYear, randomMonth, randomDay),
finish: randomFinish < new Date() ? '' : randomFinish, // make sure the random date is earlier than today
effortDriven: (i % 5 === 0),
prerequisites: (i % 2 === 0) && i !== 0 && i < 12 ? [i, i - 1] : [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,21 +143,17 @@ describe('AutoCompleteEditor', () => {
it('should define an item datacontext containing a string as cell value and expect this value to be loaded in the editor when calling "loadValue"', () => {
editor = new AutoCompleteEditor(editorArguments);
editor.loadValue(mockItemData);
const editorElm = editor.editorDomElement;

expect(editor.getValue()).toBe('male');
expect(editorElm[0].defaultValue).toBe('male');
});

it('should define an item datacontext containing a complex object as cell value and expect this value to be loaded in the editor when calling "loadValue"', () => {
mockItemData = { id: 123, gender: { value: 'male', label: 'Male' }, isActive: true };
mockColumn.field = 'gender.value';
editor = new AutoCompleteEditor(editorArguments);
editor.loadValue(mockItemData);
const editorElm = editor.editorDomElement;

expect(editor.getValue()).toBe('Male');
expect(editorElm[0].defaultValue).toBe('Male');
expect(editor.getValue()).toBe('male');
});

it('should dispatch a keyboard event and expect "stopImmediatePropagation()" to have been called when using Left Arrow key', () => {
Expand Down Expand Up @@ -238,7 +234,7 @@ describe('AutoCompleteEditor', () => {
expect(editor.isValueChanged()).toBe(true);
});

it('should call "focus()" method and expect the DOM element to be focused and selected', async () => {
it('should call "focus()" method and expect the DOM element to be focused and selected', () => {
editor = new AutoCompleteEditor(editorArguments);
const editorElm = editor.editorDomElement;
const spy = jest.spyOn(editorElm, 'focus');
Expand All @@ -247,136 +243,157 @@ describe('AutoCompleteEditor', () => {
expect(spy).toHaveBeenCalled();
});

it('should return override the item data as an object found from the collection when calling "applyValue" that passes validation', () => {
mockColumn.internalColumnEditor.validator = null;
mockItemData = { id: 123, gender: 'female', isActive: true };
describe('applyValue method', () => {
it('should apply the value to the gender property when it passes validation', () => {
mockColumn.internalColumnEditor.validator = null;
mockItemData = { id: 123, gender: 'female', isActive: true };

editor = new AutoCompleteEditor(editorArguments);
editor.applyValue(mockItemData, { value: 'female', label: 'female' });
editor = new AutoCompleteEditor(editorArguments);
editor.applyValue(mockItemData, { value: 'female', label: 'female' });

expect(mockItemData).toEqual({ id: 123, gender: { value: 'female', label: 'female' }, isActive: true });
});
expect(mockItemData).toEqual({ id: 123, gender: { value: 'female', label: 'female' }, isActive: true });
});

it('should return override the item data as a string found from the collection when calling "applyValue" that passes validation', () => {
mockColumn.internalColumnEditor.validator = null;
mockColumn.internalColumnEditor.collection = ['male', 'female'];
mockItemData = { id: 123, gender: 'female', isActive: true };
it('should apply the value to the gender property with a field having dot notation (complex object) that passes validation', () => {
mockColumn.internalColumnEditor.validator = null;
mockColumn.field = 'user.gender';
mockItemData = { id: 1, user: { gender: 'female' }, isActive: true };

editor = new AutoCompleteEditor(editorArguments);
editor.applyValue(mockItemData, 'female');
editor = new AutoCompleteEditor(editorArguments);
editor.applyValue(mockItemData, { value: 'female', label: 'female' });

expect(mockItemData).toEqual({ id: 123, gender: 'female', isActive: true });
});
expect(mockItemData).toEqual({ id: 1, user: { gender: { value: 'female', label: 'female' } }, isActive: true });
});

it('should return item data with an empty string in its value when calling "applyValue" which fails the custom validation', () => {
mockColumn.internalColumnEditor.validator = (value: any, args: EditorArgs) => {
if (value.label.length < 10) {
return { valid: false, msg: 'Must be at least 10 chars long.' };
}
return { valid: true, msg: '' };
};
mockItemData = { id: 123, gender: 'female', isActive: true };
it('should return override the item data as a string found from the collection that passes validation', () => {
mockColumn.internalColumnEditor.validator = null;
mockColumn.internalColumnEditor.collection = ['male', 'female'];
mockItemData = { id: 123, gender: 'female', isActive: true };

editor = new AutoCompleteEditor(editorArguments);
editor.applyValue(mockItemData, 'female');
editor = new AutoCompleteEditor(editorArguments);
editor.applyValue(mockItemData, 'female');

expect(mockItemData).toEqual({ id: 123, gender: '', isActive: true });
});
expect(mockItemData).toEqual({ id: 123, gender: 'female', isActive: true });
});

it('should return DOM element value when "forceUserInput" is enabled and loaded value length is greater then minLength defined when calling "serializeValue"', () => {
mockColumn.internalColumnEditor.editorOptions = { forceUserInput: true, };
mockItemData = { id: 123, gender: { value: 'male', label: 'Male' }, isActive: true };
it('should return item data with an empty string in its value when calling "applyValue" which fails the custom validation', () => {
mockColumn.internalColumnEditor.validator = (value: any, args: EditorArgs) => {
if (value.label.length < 10) {
return { valid: false, msg: 'Must be at least 10 chars long.' };
}
return { valid: true, msg: '' };
};
mockItemData = { id: 123, gender: 'female', isActive: true };

editor = new AutoCompleteEditor(editorArguments);
editor.loadValue(mockItemData);
editor.setValue('Female');
const output = editor.serializeValue();
editor = new AutoCompleteEditor(editorArguments);
editor.applyValue(mockItemData, 'female');

expect(output).toBe('Female');
expect(mockItemData).toEqual({ id: 123, gender: '', isActive: true });
});
});

it('should return DOM element value when "forceUserInput" is enabled and loaded value length is greater then custom minLength defined when calling "serializeValue"', () => {
mockColumn.internalColumnEditor.editorOptions = { forceUserInput: true, minLength: 2 } as AutocompleteOption;
mockItemData = { id: 123, gender: { value: 'male', label: 'Male' }, isActive: true };
describe('forceUserInput flag', () => {
it('should return DOM element value when "forceUserInput" is enabled and loaded value length is greater then minLength defined when calling "serializeValue"', () => {
mockColumn.internalColumnEditor.editorOptions = { forceUserInput: true, };
mockItemData = { id: 123, gender: { value: 'male', label: 'Male' }, isActive: true };

editor = new AutoCompleteEditor(editorArguments);
editor.loadValue(mockItemData);
editor.setValue('Female');
const output = editor.serializeValue();
editor = new AutoCompleteEditor(editorArguments);
editor.loadValue(mockItemData);
editor.setValue('Female');
const output = editor.serializeValue();

expect(output).toBe('Female');
});
expect(output).toBe('Female');
});

it('should return loaded value when "forceUserInput" is enabled and loaded value length is lower than minLength defined when calling "serializeValue"', () => {
mockColumn.internalColumnEditor.editorOptions = { forceUserInput: true, };
mockItemData = { id: 123, gender: { value: 'male', label: 'Male' }, isActive: true };
it('should return DOM element value when "forceUserInput" is enabled and loaded value length is greater then custom minLength defined when calling "serializeValue"', () => {
mockColumn.internalColumnEditor.editorOptions = { forceUserInput: true, minLength: 2 } as AutocompleteOption;
mockItemData = { id: 123, gender: { value: 'male', label: 'Male' }, isActive: true };

editor = new AutoCompleteEditor(editorArguments);
editor.loadValue(mockItemData);
editor.setValue('F');
const output = editor.serializeValue();
editor = new AutoCompleteEditor(editorArguments);
editor.loadValue(mockItemData);
editor.setValue('Female');
const output = editor.serializeValue();

expect(output).toBe('Male');
});
expect(output).toBe('Female');
});

it('should return correct object value even when defining a "customStructure" when calling "serializeValue"', () => {
mockColumn.internalColumnEditor.collection = [{ option: 'male', text: 'Male' }, { option: 'female', text: 'Female' }];
mockColumn.internalColumnEditor.customStructure = { value: 'option', label: 'text' };
mockItemData = { id: 123, gender: { option: 'female', text: 'Female' }, isActive: true };
it('should return loaded value when "forceUserInput" is enabled and loaded value length is lower than minLength defined when calling "serializeValue"', () => {
mockColumn.internalColumnEditor.editorOptions = { forceUserInput: true, };
mockItemData = { id: 123, gender: { value: 'male', label: 'Male' }, isActive: true };

editor = new AutoCompleteEditor(editorArguments);
editor.loadValue(mockItemData);
const output = editor.serializeValue();
editor = new AutoCompleteEditor(editorArguments);
editor.loadValue(mockItemData);
editor.setValue('F');
const output = editor.serializeValue();

expect(output).toBe('Female');
expect(output).toBe('Male');
});
});

it('should return an object output when calling "serializeValue" with its column definition set to "FieldType.object"', () => {
mockColumn.type = FieldType.object;
mockColumn.internalColumnEditor.collection = [{ value: 'm', label: 'Male' }, { value: 'f', label: 'Female' }];
mockItemData = { id: 123, gender: { value: 'f', label: 'Female' }, isActive: true };
describe('serializeValue method', () => {
it('should return correct object value even when defining a "customStructure" when calling "serializeValue"', () => {
mockColumn.internalColumnEditor.collection = [{ option: 'male', text: 'Male' }, { option: 'female', text: 'Female' }];
mockColumn.internalColumnEditor.customStructure = { value: 'option', label: 'text' };
mockItemData = { id: 123, gender: { option: 'female', text: 'Female' }, isActive: true };

editor = new AutoCompleteEditor(editorArguments);
editor.loadValue(mockItemData);
const output = editor.serializeValue();
editor = new AutoCompleteEditor(editorArguments);
editor.loadValue(mockItemData);
const output = editor.serializeValue();

expect(output).toEqual({ value: 'f', label: 'Female' });
});
expect(output).toBe('Female');
});

it('should call "getEditorLock" when "hasAutoCommitEdit" is enabled after calling "save()" method', async () => {
gridOptionMock.autoCommitEdit = true;
const spy = jest.spyOn(gridStub.getEditorLock(), 'commitCurrentEdit');
it('should return an object output when calling "serializeValue" with its column definition set to "FieldType.object"', () => {
mockColumn.type = FieldType.object;
mockColumn.internalColumnEditor.collection = [{ value: 'm', label: 'Male' }, { value: 'f', label: 'Female' }];
mockItemData = { id: 123, gender: { value: 'f', label: 'Female' }, isActive: true };

editor = new AutoCompleteEditor(editorArguments);
editor.save();
editor = new AutoCompleteEditor(editorArguments);
editor.loadValue(mockItemData);
const output = editor.serializeValue();

expect(spy).toHaveBeenCalled();
expect(output).toEqual({ value: 'f', label: 'Female' });
});
});

it('should call "commitChanges" when "hasAutoCommitEdit" is disabled after calling "save()" method', async () => {
gridOptionMock.autoCommitEdit = false;
const spy = jest.spyOn(editorArguments, 'commitChanges');
describe('save method', () => {
it('should call "getEditorLock" when "hasAutoCommitEdit" is enabled after calling "save()" method', () => {
gridOptionMock.autoCommitEdit = true;
const spy = jest.spyOn(gridStub.getEditorLock(), 'commitCurrentEdit');

editor = new AutoCompleteEditor(editorArguments);
editor.save();
editor = new AutoCompleteEditor(editorArguments);
editor.save();

expect(spy).toHaveBeenCalled();
});
expect(spy).toHaveBeenCalled();
});

it('should validate and return False when field is required and field is an empty string', () => {
mockColumn.internalColumnEditor.required = true;
editor = new AutoCompleteEditor(editorArguments);
const validation = editor.validate('');
it('should call "commitChanges" when "hasAutoCommitEdit" is disabled after calling "save()" method', () => {
gridOptionMock.autoCommitEdit = false;
const spy = jest.spyOn(editorArguments, 'commitChanges');

editor = new AutoCompleteEditor(editorArguments);
editor.save();

expect(validation).toEqual({ valid: false, msg: 'Field is required' });
expect(spy).toHaveBeenCalled();
});
});

it('should validate and return True when field is required and field a valid object', () => {
mockColumn.internalColumnEditor.required = true;
editor = new AutoCompleteEditor(editorArguments);
const validation = editor.validate(mockItemData);
describe('validate method', () => {
it('should validate and return False when field is required and field is an empty string', () => {
mockColumn.internalColumnEditor.required = true;
editor = new AutoCompleteEditor(editorArguments);
const validation = editor.validate('');

expect(validation).toEqual({ valid: false, msg: 'Field is required' });
});

it('should validate and return True when field is required and field is a valid input value', () => {
mockColumn.internalColumnEditor.required = true;
editor = new AutoCompleteEditor(editorArguments);
const validation = editor.validate('gender');

expect(validation).toEqual({ valid: true, msg: null });
expect(validation).toEqual({ valid: true, msg: null });
});
});

describe('onSelect method', () => {
Expand Down

0 comments on commit 0b67fea

Please sign in to comment.