Skip to content

Commit

Permalink
feat(editors): add missing Date Editor
Browse files Browse the repository at this point in the history
  • Loading branch information
ghiscoding-SE committed Mar 30, 2020
1 parent 29c9d89 commit c897c7c
Show file tree
Hide file tree
Showing 5 changed files with 819 additions and 8 deletions.
371 changes: 371 additions & 0 deletions packages/common/src/editors/__tests__/dateEditor.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,371 @@
import * as moment from 'moment';

import { Editors } from '../index';
import { DateEditor } from '../dateEditor';
import { FieldType } from '../../enums/index';
import { Column, EditorArgs, EditorArguments, GridOption } from '../../interfaces/index';
import { TranslateServiceStub } from '../../../../../test/translateServiceStub';

const containerId = 'demo-container';

// define a <div> container to simulate the grid container
const template = `<div id="${containerId}"></div>`;

const dataViewStub = {
refresh: jest.fn(),
};

const gridOptionMock = {
autoCommitEdit: false,
editable: true,
i18n: null,
} as GridOption;

const getEditorLockMock = {
commitCurrentEdit: jest.fn(),
};

const gridStub = {
getOptions: () => gridOptionMock,
getColumns: jest.fn(),
getEditorLock: () => getEditorLockMock,
getHeaderRowColumn: jest.fn(),
navigateNext: jest.fn(),
navigatePrev: jest.fn(),
render: jest.fn(),
};

describe('DateEditor', () => {
let translateService: TranslateServiceStub;
let divContainer: HTMLDivElement;
let editor: DateEditor;
let editorArguments: EditorArguments;
let mockColumn: Column;
let mockItemData: any;

beforeEach(() => {
translateService = new TranslateServiceStub();

divContainer = document.createElement('div');
divContainer.innerHTML = template;
document.body.appendChild(divContainer);

mockColumn = { id: 'startDate', field: 'startDate', editable: true, editor: { model: Editors.date }, internalColumnEditor: {} } as Column;

editorArguments = {
grid: gridStub,
column: mockColumn,
item: mockItemData,
event: null,
cancelChanges: jest.fn(),
commitChanges: jest.fn(),
container: divContainer,
columnMetaData: null,
dataView: dataViewStub,
gridPosition: { top: 0, left: 0, bottom: 10, right: 10, height: 100, width: 100, visible: true },
position: { top: 0, left: 0, bottom: 10, right: 10, height: 100, width: 100, visible: true },
};
});

describe('with invalid Editor instance', () => {
it('should throw an error when trying to call init without any arguments', (done) => {
try {
editor = new DateEditor(null);
} catch (e) {
expect(e.toString()).toContain(`[Slickgrid-Universal] Something is wrong with this grid, an Editor must always have valid arguments.`);
done();
}
});
});

describe('with valid Editor instance', () => {
beforeEach(() => {
mockItemData = { id: 1, startDate: '2001-01-02T11:02:02.000Z', isActive: true };
mockColumn = { id: 'startDate', field: 'startDate', editable: true, editor: { model: Editors.date }, internalColumnEditor: {} } as Column;

editorArguments.column = mockColumn;
editorArguments.item = mockItemData;
});

afterEach(() => {
editor.destroy();
});

it('should initialize the editor', () => {
gridOptionMock.i18n = translateService;
editor = new DateEditor(editorArguments);
const editorCount = divContainer.querySelectorAll('input.editor-text.editor-startDate').length;
expect(editorCount).toBe(1);
});

it('should have a placeholder when defined in its column definition', () => {
const testValue = 'test placeholder';
mockColumn.internalColumnEditor.placeholder = testValue;

editor = new DateEditor(editorArguments);
const editorElm = divContainer.querySelector<HTMLTextAreaElement>('input.editor-text.editor-startDate');

expect(editorElm.placeholder).toBe(testValue);
});

it('should have a title (tooltip) when defined in its column definition', () => {
const testValue = 'test title';
mockColumn.internalColumnEditor.title = testValue;

editor = new DateEditor(editorArguments);
const editorElm = divContainer.querySelector<HTMLTextAreaElement>('input.editor-text.editor-startDate');

expect(editorElm.title).toBe(testValue);
});

it('should call "columnEditor" GETTER and expect to equal the editor settings we provided', () => {
mockColumn.internalColumnEditor = {
placeholder: 'test placeholder',
title: 'test title',
};

editor = new DateEditor(editorArguments);

expect(editor.columnEditor).toEqual(mockColumn.internalColumnEditor);
});

it('should call "setValue" and expect the DOM element value to be the same string when calling "getValue"', () => {
editor = new DateEditor(editorArguments);
editor.setValue('2001-01-02T11:02:02.000Z');

expect(editor.getValue()).toBe('2001-01-02T11:02:02.000Z');
});

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"', () => {
mockItemData = { id: 1, startDate: '2001-01-02T11:02:02.000Z', isActive: true };
editor = new DateEditor(editorArguments);
editor.loadValue(mockItemData);
const editorElm = editor.editorDomElement;

expect(editor.getValue()).toBe('2001-01-02T11:02:02.000Z');
expect(editorElm[0].defaultValue).toBe('2001-01-02T11:02:02.000Z');
});

it('should hide the DOM element when the "hide" method is called', () => {
editor = new DateEditor(editorArguments);
const spy = jest.spyOn(editor.flatInstance, 'close');
const calendarElm = document.body.querySelector<HTMLDivElement>('.flatpickr-calendar');
editor.hide();

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

it('should show the DOM element when the "show" method is called', () => {
editor = new DateEditor(editorArguments);
const spy = jest.spyOn(editor.flatInstance, 'open');
const calendarElm = document.body.querySelector<HTMLDivElement>('.flatpickr-calendar');
editor.show();
editor.focus();

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

describe('isValueChanged method', () => {
it('should return True when date is changed in the picker', () => {
// change to allow input value only for testing purposes & use the regular flatpickr input to test that one too
mockColumn.internalColumnEditor.editorOptions = { allowInput: true, altInput: false };
mockItemData = { id: 1, startDate: '2001-01-02T11:02:02.000Z', isActive: true };

editor = new DateEditor(editorArguments);
editor.loadValue(mockItemData);
editor.focus();
const editorInputElm = divContainer.querySelector<HTMLInputElement>('input.flatpickr');
editorInputElm.value = '2024-04-02T16:02:02.239Z';
editorInputElm.dispatchEvent(new (window.window as any).KeyboardEvent('keydown', { keyCode: 13, bubbles: true, cancelable: true }));

expect(editor.isValueChanged()).toBe(true);
});

it('should return False when date in the picker is the same as the current date', () => {
mockItemData = { id: 1, startDate: '2001-01-02T11:02:02.000Z', isActive: true };
mockColumn.internalColumnEditor.editorOptions = { allowInput: true }; // change to allow input value only for testing purposes

editor = new DateEditor(editorArguments);
editor.loadValue(mockItemData);
const editorInputElm = divContainer.querySelector<HTMLInputElement>('input.flatpickr-alt-input');
editorInputElm.value = '2001-01-02T11:02:02.000Z';
editorInputElm.dispatchEvent(new (window.window as any).KeyboardEvent('keydown', { keyCode: 13, bubbles: true, cancelable: true }));

expect(editor.isValueChanged()).toBe(false);
});
});

describe('applyValue method', () => {
it('should apply the value to the startDate property when it passes validation', () => {
mockColumn.internalColumnEditor.validator = null;
mockColumn.type = FieldType.dateTimeIsoAmPm;
mockItemData = { id: 1, startDate: '2001-04-05T11:33:42.000Z', isActive: true };

const newDate = '2001-01-02T16:02:02.000+05:00';
editor = new DateEditor(editorArguments);
editor.applyValue(mockItemData, newDate);

expect(mockItemData).toEqual({ id: 1, startDate: moment(newDate, 'YYYY-MM-DD hh:mm:ss a').toDate(), isActive: true });
});

it('should apply the value to the startDate property with a field having dot notation (complex object) that passes validation', () => {
mockColumn.internalColumnEditor.validator = null;
mockColumn.type = FieldType.dateTimeIsoAmPm;
mockColumn.field = 'employee.startDate';
mockItemData = { id: 1, employee: { startDate: '2001-04-05T11:33:42.000Z' }, isActive: true };

const newDate = '2001-01-02T16:02:02.000+05:00';
editor = new DateEditor(editorArguments);
editor.applyValue(mockItemData, newDate);

expect(mockItemData).toEqual({ id: 1, employee: { startDate: moment(newDate, 'YYYY-MM-DD hh:mm:ss a').toDate() }, isActive: true });
});

it('should return item data with an empty string in its value when it fails the custom validation', () => {
mockColumn.internalColumnEditor.validator = (value: any, args: EditorArgs) => {
if (value.length > 10) {
return { valid: false, msg: 'Must be at least 10 chars long.' };
}
return { valid: true, msg: '' };
};
mockItemData = { id: 1, startDate: '2001-04-05T11:33:42.000Z', isActive: true };

editor = new DateEditor(editorArguments);
editor.applyValue(mockItemData, '2001-01-02T16:02:02.000+05:00');

expect(mockItemData).toEqual({ id: 1, startDate: '', isActive: true });
});
});

describe('serializeValue method', () => {
it('should return serialized value as a date string', () => {
mockColumn.type = FieldType.dateIso;
mockItemData = { id: 1, startDate: '2001-01-02T16:02:02.000+05:00', isActive: true };

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

expect(output).toBe('2001-01-02');
});

it('should return serialized value as an empty string when item value is also an empty string', () => {
mockItemData = { id: 1, startDate: '', isActive: true };

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

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

it('should return serialized value as an empty string when item value is null', () => {
mockItemData = { id: 1, startDate: null, isActive: true };

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

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

it('should return serialized value as a date string when using a dot (.) notation for complex object', () => {
mockColumn.type = FieldType.dateIso;
mockColumn.field = 'employee.startDate';
mockItemData = { id: 1, employee: { startDate: '2001-01-02T16:02:02.000+05:00' }, isActive: true };

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

expect(output).toBe('2001-01-02');
});
});

describe('save method', () => {
afterEach(() => {
jest.clearAllMocks();
});

it('should call "getEditorLock" method when "hasAutoCommitEdit" is enabled', () => {
mockItemData = { id: 1, startDate: '2001-01-02T16:02:02.000+05:00', isActive: true };
gridOptionMock.autoCommitEdit = true;
const spy = jest.spyOn(gridStub.getEditorLock(), 'commitCurrentEdit');

editor = new DateEditor(editorArguments);
editor.loadValue(mockItemData);
editor.setValue('2022-03-02T16:02:02.000+05:00');
editor.save();

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

it('should call "commitChanges" method when "hasAutoCommitEdit" is disabled', () => {
mockItemData = { id: 1, startDate: '2001-01-02T16:02:02.000+05:00', isActive: true };
gridOptionMock.autoCommitEdit = false;
const spy = jest.spyOn(editorArguments, 'commitChanges');

editor = new DateEditor(editorArguments);
editor.loadValue(mockItemData);
editor.setValue('2022-03-02T16:02:02.000+05:00');
editor.save();

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

it('should not call anything when the input value is empty but is required', () => {
mockItemData = { id: 1, startDate: '', isActive: true };
mockColumn.internalColumnEditor.required = true;
gridOptionMock.autoCommitEdit = true;
const spy = jest.spyOn(gridStub.getEditorLock(), 'commitCurrentEdit');

editor = new DateEditor(editorArguments);
editor.loadValue(mockItemData);
editor.save();

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

describe('validate method', () => {
it('should return False when field is required and field is empty', () => {
mockColumn.internalColumnEditor.required = true;
editor = new DateEditor(editorArguments);
const validation = editor.validate('');

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

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

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

describe('with different locale', () => {
it('should display text in new locale', (done) => {
gridOptionMock.i18n = translateService;

translateService.setLocale('fr-CA'); // will be trimmed to "fr"
editor = new DateEditor(editorArguments);

const spy = jest.spyOn(editor.flatInstance, 'open');
const calendarElm = document.body.querySelector<HTMLDivElement>('.flatpickr-calendar');
const selectonOptionElms = calendarElm.querySelectorAll<HTMLSelectElement>(' .flatpickr-monthDropdown-months option');

editor.show();

expect(calendarElm).toBeTruthy();
expect(selectonOptionElms.length).toBe(12);
expect(selectonOptionElms[0].textContent).toBe('janvier');
expect(spy).toHaveBeenCalled();
done();
});
});
});
});
Loading

0 comments on commit c897c7c

Please sign in to comment.