You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gristlabs_grist-core/test/client/lib/koForm.js

276 lines
8.7 KiB

var assert = require('chai').assert;
var ko = require('knockout');
var kf = require('app/client/lib/koForm');
var koArray = require('app/client/lib/koArray');
var clientUtil = require('../clientUtil');
var G = require('app/client/lib/browserGlobals').get('$');
describe('koForm', function() {
clientUtil.setTmpMochaGlobals();
function triggerInput(input, property, value) {
input[property] = value;
G.$(input).trigger('input');
}
function triggerChange(input, property, value) {
input[property] = value;
G.$(input).trigger('change');
}
function triggerClick(elem) {
G.$(elem).trigger('click');
}
describe("button", function() {
it("should call a function", function() {
var calls = 0;
var btn = kf.button(function() { calls++; }, 'Test');
triggerClick(btn);
triggerClick(btn);
triggerClick(btn);
assert.equal(calls, 3);
});
});
describe("checkButton", function() {
it("should bind an observable", function() {
var obs = ko.observable(false);
// Test observable->widget binding.
var btn = kf.checkButton(obs, "Test");
assert(!btn.classList.contains('active'));
obs(true);
assert(btn.classList.contains('active'));
btn = kf.checkButton(obs, "Test2");
assert(btn.classList.contains('active'));
obs(false);
assert(!btn.classList.contains('active'));
// Test widget->observable binding.
assert.equal(obs(), false);
triggerClick(btn);
assert.equal(obs(), true);
triggerClick(btn);
assert.equal(obs(), false);
});
});
describe("buttonSelect", function() {
it("should bind an observable", function() {
var obs = ko.observable('b');
var a, b, c;
kf.buttonSelect(obs,
a = kf.optionButton('a', 'Test A'),
b = kf.optionButton('b', 'Test B'),
c = kf.optionButton('c', 'Test C')
);
// Test observable->widget binding.
assert(!a.classList.contains('active'));
assert(b.classList.contains('active'));
assert(!c.classList.contains('active'));
obs('a');
assert(a.classList.contains('active'));
assert(!b.classList.contains('active'));
obs('c');
assert(!a.classList.contains('active'));
assert(!b.classList.contains('active'));
assert(c.classList.contains('active'));
// Test widget->observable binding.
assert.equal(obs(), 'c');
triggerClick(b);
assert.equal(obs(), 'b');
});
});
describe("checkbox", function() {
it("should bind an observable", function() {
var obs = ko.observable(false);
var check = kf.checkbox(obs, "Foo").querySelector('input');
// Test observable->widget binding.
assert.equal(check.checked, false);
obs(true);
assert.equal(check.checked, true);
check = kf.checkbox(obs, "Foo").querySelector('input');
assert.equal(check.checked, true);
obs(false);
assert.equal(check.checked, false);
// Test widget->observable binding.
triggerChange(check, 'checked', true);
assert.equal(obs(), true);
assert.equal(check.checked, true);
triggerChange(check, 'checked', false);
assert.equal(obs(), false);
assert.equal(check.checked, false);
});
});
describe("text", function() {
it("should bind an observable", function() {
var obs = ko.observable('hello');
var input = kf.text(obs).querySelector('input');
// Test observable->widget binding.
assert.equal(input.value, 'hello');
obs('world');
assert.equal(input.value, 'world');
// Test widget->observable binding.
triggerChange(input, 'value', 'foo');
assert.equal(obs(), 'foo');
});
});
describe("text debounce", function() {
it("should bind an observable", function() {
var obs = ko.observable('hello');
var input = kf.text(obs, {delay: 300}).querySelector('input');
// Test observable->widget binding.
assert.equal(input.value, 'hello');
obs('world');
assert.equal(input.value, 'world');
// Test widget->observable binding using interrupted by 'Enter' or loosing focus debounce.
triggerInput(input, 'value', 'bar');
assert.equal(input.value, 'bar');
// Ensure that observable value wasn't changed immediately
assert.equal(obs(), 'world');
// Simulate 'change' event (hitting 'Enter' or loosing focus)
triggerChange(input, 'value', 'bar');
// Ensure that observable value was changed on 'change' event
assert.equal(obs(), 'bar');
// Test widget->observable binding using debounce.
triggerInput(input, 'value', 'helloworld');
input.selectionStart = 3;
input.selectionEnd = 7;
assert.equal(input.value.substring(input.selectionStart, input.selectionEnd), 'lowo');
assert.equal(input.value, 'helloworld');
// Ensure that observable value wasn't changed immediately, needs to wait 300 ms
assert.equal(obs(), 'bar');
// Ensure that after delay value were changed
return clientUtil.waitForChange(obs, 350)
.then(() => {
assert.equal(obs(), 'helloworld');
assert.equal(input.value, 'helloworld');
// Ensure that selection is the same and cursor didn't jump to the end
assert.equal(input.value.substring(input.selectionStart, input.selectionEnd), 'lowo');
});
});
});
describe("numText", function() {
it("should bind an observable", function() {
var obs = ko.observable(1234);
var input = kf.numText(obs).querySelector('input');
// Test observable->widget binding.
assert.equal(input.value, '1234');
obs('-987.654');
assert.equal(input.value, '-987.654');
// Test widget->observable binding.
triggerInput(input, 'value', '-1.2');
assert.strictEqual(obs(), -1.2);
});
});
describe("select", function() {
it("should bind an observable", function() {
var obs = ko.observable("b");
var input = kf.select(obs, ["a", "b", "c"]).querySelector('select');
var options = Array.prototype.slice.call(input.querySelectorAll('option'), 0);
function selected() {
return options.map(function(option) { return option.selected; });
}
// Test observable->widget binding.
assert.deepEqual(selected(), [false, true, false]);
obs("a");
assert.deepEqual(selected(), [true, false, false]);
obs("c");
assert.deepEqual(selected(), [false, false, true]);
// Test widget->observable binding.
triggerChange(options[0], 'selected', true);
assert.deepEqual(selected(), [true, false, false]);
assert.equal(obs(), "a");
triggerChange(options[1], 'selected', true);
assert.deepEqual(selected(), [false, true, false]);
assert.equal(obs(), "b");
});
it("should work with option array of objects", function() {
var obs = ko.observable();
var foo = ko.observable('foo');
var bar = ko.observable('bar');
var values = koArray([
{ label: foo, value: 'a1' },
{ label: bar, value: 'b1' },
]);
var select = kf.select(obs, values);
var options = Array.from(select.querySelectorAll('option'));
assert.deepEqual(options.map(el => el.textContent), ['foo', 'bar']);
triggerChange(options[0], 'selected', true);
assert.equal(obs(), 'a1');
foo('foo2');
bar('bar2');
options = Array.from(select.querySelectorAll('option'));
assert.deepEqual(options.map(el => el.textContent), ['foo2', 'bar2']);
triggerChange(options[1], 'selected', true);
assert.equal(obs(), 'b1');
});
it("should store actual, non-stringified values", function() {
let obs = ko.observable();
let values = [
{ label: 'a', value: 1 },
{ label: 'b', value: '2' },
{ label: 'c', value: true },
{ label: 'd', value: { hello: 'world' } },
{ label: 'e', value: new Date() },
];
let options = Array.from(kf.select(obs, values).querySelectorAll('option'));
for (let i = 0; i < values.length; i++) {
triggerChange(options[i], 'selected', true);
assert.strictEqual(obs(), values[i].value);
}
});
it("should allow multi-select and save sorted values", function() {
let obs = ko.observable();
let foo = { foo: 'bar' };
let values = [{ label: 'a', value: foo }, 'd', { label: 'c', value: 1 }, 'b'];
let options = Array.from(
kf.select(obs, values, { multiple: true}).querySelectorAll('option'));
triggerChange(options[0], 'selected', true);
triggerChange(options[2], 'selected', true);
triggerChange(options[3], 'selected', true);
assert.deepEqual(obs(), [1, foo, 'b']);
});
});
});