538 lines
17 KiB
JavaScript
538 lines
17 KiB
JavaScript
'use strict';
|
|
|
|
/* eslint-env mocha, jasmine */
|
|
|
|
|
|
describe('Dataset', function() {
|
|
require('../../src/common/dom.js').element = require('jquery');
|
|
require('../../src/jquery/plugin.js');
|
|
|
|
var $ = require('jquery');
|
|
require('jasmine-jquery');
|
|
|
|
var Dataset = require('../../src/autocomplete/dataset.js');
|
|
|
|
beforeEach(function() {
|
|
this.dataset = new Dataset({
|
|
name: 'test',
|
|
source: this.source = jasmine.createSpy('source')
|
|
});
|
|
});
|
|
|
|
it('should throw an error if source is missing', function() {
|
|
expect(noSource).toThrow();
|
|
|
|
function noSource() {
|
|
new Dataset();
|
|
}
|
|
});
|
|
|
|
it('should throw an error if the name is not a valid class name', function() {
|
|
expect(fn).toThrow();
|
|
|
|
function fn() {
|
|
var d = new Dataset({name: 'a space', source: $.noop});
|
|
}
|
|
});
|
|
|
|
describe('#getRoot', function() {
|
|
it('should return the root element', function() {
|
|
expect(this.dataset.getRoot()).toBeMatchedBy('div.aa-dataset-test');
|
|
});
|
|
});
|
|
|
|
describe('#update', function() {
|
|
it('should render suggestions', function() {
|
|
this.source.and.callFake(fakeGetWithSyncResults);
|
|
this.dataset.update('woah');
|
|
|
|
expect(this.dataset.getRoot()).toContainText('one');
|
|
expect(this.dataset.getRoot()).toContainText('two');
|
|
expect(this.dataset.getRoot()).toContainText('three');
|
|
});
|
|
|
|
it('should allow custom display functions', function() {
|
|
this.dataset = new Dataset({
|
|
name: 'test',
|
|
display: function(o) { return o.display; },
|
|
source: this.source = jasmine.createSpy('source')
|
|
});
|
|
|
|
this.source.and.callFake(fakeGetForDisplayFn);
|
|
this.dataset.update('woah');
|
|
|
|
expect(this.dataset.getRoot()).toContainText('4');
|
|
expect(this.dataset.getRoot()).toContainText('5');
|
|
expect(this.dataset.getRoot()).toContainText('6');
|
|
});
|
|
|
|
it('should render empty when no suggestions are available', function() {
|
|
this.dataset = new Dataset({
|
|
source: this.source,
|
|
templates: {
|
|
empty: '<h2>empty</h2>'
|
|
}
|
|
});
|
|
|
|
this.source.and.callFake(fakeGetWithSyncEmptyResults);
|
|
this.dataset.update('woah');
|
|
|
|
expect(this.dataset.getRoot()).toContainText('empty');
|
|
});
|
|
|
|
it('should throw an error if suggestions is not an array', function() {
|
|
this.source.and.callFake(fakeGetWithSyncNonArrayResults);
|
|
expect(this.dataset.update.bind(this.dataset, 'woah'))
|
|
.toThrowError(TypeError, 'suggestions must be an array');
|
|
});
|
|
|
|
it('should set the aa-without class when no suggestions are available', function() {
|
|
var $menu = $('<div />');
|
|
this.dataset = new Dataset({
|
|
$menu: $menu,
|
|
source: this.source,
|
|
templates: {
|
|
empty: '<h2>empty</h2>'
|
|
}
|
|
});
|
|
|
|
this.source.and.callFake(fakeGetWithSyncEmptyResults);
|
|
this.dataset.update('woah');
|
|
|
|
expect($menu).toHaveClass('aa-without-1');
|
|
expect($menu).not.toHaveClass('aa-with-1');
|
|
});
|
|
|
|
it('should set the aa-with class when suggestions are available', function() {
|
|
var $menu = $('<div />');
|
|
this.dataset = new Dataset({
|
|
$menu: $menu,
|
|
name: 'fake',
|
|
source: this.source,
|
|
templates: {
|
|
empty: '<h2>empty</h2>'
|
|
}
|
|
});
|
|
|
|
this.source.and.callFake(fakeGetWithSyncResults);
|
|
this.dataset.update('woah');
|
|
|
|
expect($menu).not.toHaveClass('aa-without-fake');
|
|
expect($menu).toHaveClass('aa-with-fake');
|
|
});
|
|
|
|
it('should allow dataset name=0 and use the provided div', function() {
|
|
var $menu = $('<div><div class="predefined aa-dataset-0"></div></div>');
|
|
this.dataset = new Dataset({
|
|
$menu: $menu,
|
|
name: 0,
|
|
source: this.source
|
|
});
|
|
expect(this.dataset.$el).toHaveClass('predefined');
|
|
});
|
|
|
|
it('should render isEmpty with extra params', function() {
|
|
var spy = jasmine.createSpy('empty with extra params');
|
|
this.dataset = new Dataset({
|
|
source: this.source,
|
|
templates: {
|
|
empty: spy
|
|
}
|
|
});
|
|
|
|
this.source.and.callFake(fakeGetWithSyncEmptyResultsAndExtraParams);
|
|
this.dataset.update('woah');
|
|
|
|
expect(spy).toHaveBeenCalled();
|
|
expect(spy.calls.argsFor(0).length).toEqual(4);
|
|
expect(spy.calls.argsFor(0)[0]).toEqual({query: 'woah', isEmpty: true});
|
|
expect(spy.calls.argsFor(0)[1]).toEqual(42);
|
|
expect(spy.calls.argsFor(0)[2]).toEqual(true);
|
|
expect(spy.calls.argsFor(0)[3]).toEqual(false);
|
|
});
|
|
|
|
it('should render with extra params', function() {
|
|
var headerSpy = jasmine.createSpy('header with extra params');
|
|
var footerSpy = jasmine.createSpy('footer with extra params');
|
|
var suggestionSpy = jasmine.createSpy('suggestion with extra params');
|
|
this.dataset = new Dataset({
|
|
source: this.source,
|
|
templates: {
|
|
header: headerSpy,
|
|
footer: footerSpy,
|
|
suggestion: suggestionSpy
|
|
}
|
|
});
|
|
|
|
var suggestions;
|
|
fakeGetWithSyncResultsAndExtraParams('woah', function(all) {
|
|
suggestions = all;
|
|
});
|
|
|
|
this.source.and.callFake(fakeGetWithSyncResultsAndExtraParams);
|
|
this.dataset.update('woah');
|
|
|
|
expect(headerSpy).toHaveBeenCalled();
|
|
expect(headerSpy.calls.argsFor(0).length).toEqual(4);
|
|
expect(headerSpy.calls.argsFor(0)[0]).toEqual({query: 'woah', isEmpty: false});
|
|
expect(headerSpy.calls.argsFor(0)[1]).toEqual(42);
|
|
expect(headerSpy.calls.argsFor(0)[2]).toEqual(true);
|
|
expect(headerSpy.calls.argsFor(0)[3]).toEqual(false);
|
|
|
|
expect(footerSpy).toHaveBeenCalled();
|
|
expect(footerSpy.calls.argsFor(0).length).toEqual(4);
|
|
expect(footerSpy.calls.argsFor(0)[0]).toEqual({query: 'woah', isEmpty: false});
|
|
expect(footerSpy.calls.argsFor(0)[1]).toEqual(42);
|
|
expect(footerSpy.calls.argsFor(0)[2]).toEqual(true);
|
|
expect(footerSpy.calls.argsFor(0)[3]).toEqual(false);
|
|
|
|
expect(suggestionSpy).toHaveBeenCalled();
|
|
for (var i = 0; i < 2; ++i) {
|
|
expect(suggestionSpy.calls.argsFor(i).length).toEqual(4);
|
|
expect(suggestionSpy.calls.argsFor(i)[0]).toEqual(suggestions[i]);
|
|
expect(suggestionSpy.calls.argsFor(i)[1]).toEqual(42);
|
|
expect(suggestionSpy.calls.argsFor(i)[2]).toEqual(true);
|
|
expect(suggestionSpy.calls.argsFor(i)[3]).toEqual(false);
|
|
}
|
|
});
|
|
|
|
it('should render header', function() {
|
|
this.dataset = new Dataset({
|
|
source: this.source,
|
|
templates: {
|
|
header: '<h2>header</h2>'
|
|
}
|
|
});
|
|
|
|
this.source.and.callFake(fakeGetWithSyncResults);
|
|
this.dataset.update('woah');
|
|
|
|
expect(this.dataset.getRoot()).toContainText('header');
|
|
});
|
|
|
|
it('should render footer', function() {
|
|
this.dataset = new Dataset({
|
|
source: this.source,
|
|
templates: {
|
|
footer: function(c) { return '<p>' + c.query + '</p>'; }
|
|
}
|
|
});
|
|
|
|
this.source.and.callFake(fakeGetWithSyncResults);
|
|
this.dataset.update('woah');
|
|
|
|
expect(this.dataset.getRoot()).toContainText('woah');
|
|
});
|
|
|
|
it('should not render header/footer if there is no content', function() {
|
|
this.dataset = new Dataset({
|
|
source: this.source,
|
|
templates: {
|
|
header: '<h2>header</h2>',
|
|
footer: '<h2>footer</h2>'
|
|
}
|
|
});
|
|
|
|
this.source.and.callFake(fakeGetWithSyncEmptyResults);
|
|
this.dataset.update('woah');
|
|
|
|
expect(this.dataset.getRoot()).not.toContainText('header');
|
|
expect(this.dataset.getRoot()).not.toContainText('footer');
|
|
});
|
|
|
|
it('should not render stale suggestions', function(done) {
|
|
this.source.and.callFake(fakeGetWithAsyncResults);
|
|
this.dataset.update('woah');
|
|
|
|
this.source.and.callFake(fakeGetWithSyncResults);
|
|
this.dataset.update('nelly');
|
|
|
|
var that = this;
|
|
setTimeout(function() {
|
|
expect(that.dataset.getRoot()).toContainText('one');
|
|
expect(that.dataset.getRoot()).toContainText('two');
|
|
expect(that.dataset.getRoot()).toContainText('three');
|
|
expect(that.dataset.getRoot()).not.toContainText('four');
|
|
expect(that.dataset.getRoot()).not.toContainText('five');
|
|
done();
|
|
}, 100);
|
|
});
|
|
|
|
it('should not render suggestions if update was canceled', function(done) {
|
|
this.source.and.callFake(fakeGetWithAsyncResults);
|
|
this.dataset.update('woah');
|
|
this.dataset.cancel();
|
|
|
|
var that = this;
|
|
setTimeout(function() {
|
|
expect(that.dataset.getRoot()).toBeEmpty();
|
|
done();
|
|
}, 100);
|
|
});
|
|
|
|
it('should trigger rendered after suggestions are rendered', function(done) {
|
|
var spy;
|
|
|
|
this.dataset.onSync('rendered', spy = jasmine.createSpy());
|
|
|
|
this.source.and.callFake(fakeGetWithSyncResults);
|
|
this.dataset.update('woah');
|
|
|
|
setTimeout(function() {
|
|
expect(spy.calls.count()).toBe(1);
|
|
done();
|
|
}, 100);
|
|
});
|
|
|
|
it('should cache latest query, suggestions and extra render arguments', function() {
|
|
this.source.and.callFake(fakeGetWithSyncResultsAndExtraParams);
|
|
this.dataset.update('woah');
|
|
|
|
expect(this.dataset.cachedQuery).toEqual('woah');
|
|
expect(this.dataset.cachedSuggestions).toEqual([
|
|
{value: 'one', raw: {value: 'one'}},
|
|
{value: 'two', raw: {value: 'two'}},
|
|
{value: 'three', raw: {value: 'three'}}
|
|
]);
|
|
expect(this.dataset.cachedRenderExtraArgs).toEqual([42, true, false]);
|
|
});
|
|
|
|
it('should retrieve cached results for subsequent identical queries', function() {
|
|
this.source.and.callFake(fakeGetWithSyncResults);
|
|
|
|
this.dataset.update('woah');
|
|
expect(this.source.calls.count()).toBe(1);
|
|
expect(this.dataset.getRoot()).toContainText('one');
|
|
expect(this.dataset.getRoot()).toContainText('two');
|
|
expect(this.dataset.getRoot()).toContainText('three');
|
|
|
|
this.dataset.clear();
|
|
this.dataset.update('woah');
|
|
expect(this.source.calls.count()).toBe(1);
|
|
expect(this.dataset.getRoot()).toContainText('one');
|
|
expect(this.dataset.getRoot()).toContainText('two');
|
|
expect(this.dataset.getRoot()).toContainText('three');
|
|
});
|
|
|
|
it('should not retrieve cached results for subsequent identical queries if cache is disabled', function() {
|
|
this.dataset = new Dataset({
|
|
name: 'test',
|
|
source: this.source = jasmine.createSpy('source'),
|
|
cache: false,
|
|
});
|
|
|
|
this.source.and.callFake(fakeGetWithSyncResultsAndExtraParams);
|
|
|
|
this.dataset.update('woah');
|
|
expect(this.source.calls.count()).toBe(1);
|
|
expect(this.dataset.getRoot()).toContainText('one');
|
|
expect(this.dataset.getRoot()).toContainText('two');
|
|
expect(this.dataset.getRoot()).toContainText('three');
|
|
|
|
this.dataset.clear();
|
|
this.dataset.update('woah');
|
|
expect(this.source.calls.count()).toBe(2);
|
|
expect(this.dataset.getRoot()).toContainText('one');
|
|
expect(this.dataset.getRoot()).toContainText('two');
|
|
expect(this.dataset.getRoot()).toContainText('three');
|
|
});
|
|
|
|
it('should reuse render function extra params for subsequent identical queries', function() {
|
|
var spy = spyOn(this.dataset, '_render');
|
|
this.source.and.callFake(fakeGetWithSyncResultsAndExtraParams);
|
|
|
|
this.dataset.update('woah');
|
|
expect(this.source.calls.count()).toBe(1);
|
|
expect(spy.calls.argsFor(0)).toEqual([
|
|
'woah', [
|
|
{value: 'one', raw: {value: 'one'}},
|
|
{value: 'two', raw: {value: 'two'}},
|
|
{value: 'three', raw: {value: 'three'}}
|
|
], 42, true, false]);
|
|
|
|
this.dataset.clear();
|
|
this.dataset.update('woah');
|
|
expect(this.source.calls.count()).toBe(1);
|
|
expect(spy.calls.argsFor(1)).toEqual([
|
|
'woah', [
|
|
{value: 'one', raw: {value: 'one'}},
|
|
{value: 'two', raw: {value: 'two'}},
|
|
{value: 'three', raw: {value: 'three'}}
|
|
], 42, true, false]);
|
|
});
|
|
|
|
it('should not retrieved cached results for subsequent different queries', function() {
|
|
this.source.and.callFake(fakeGetWithSyncResultsAndExtraParams);
|
|
|
|
this.dataset.update('woah');
|
|
expect(this.source.calls.count()).toBe(1);
|
|
|
|
this.dataset.clear();
|
|
this.dataset.update('woah 2');
|
|
expect(this.source.calls.count()).toBe(2);
|
|
});
|
|
|
|
it('should wait before calling the source if debounce is provided', function(done) {
|
|
var that = this;
|
|
|
|
this.dataset = new Dataset({
|
|
source: this.source,
|
|
debounce: 250
|
|
});
|
|
|
|
this.source.and.callFake(fakeGetWithSyncResultsAndExtraParams);
|
|
|
|
this.dataset.update('woah');
|
|
expect(this.source.calls.count()).toBe(0);
|
|
|
|
this.dataset.update('woah 2');
|
|
expect(this.source.calls.count()).toBe(0);
|
|
|
|
setTimeout(function() {
|
|
expect(that.source.calls.count()).toBe(1);
|
|
done();
|
|
}, 500);
|
|
});
|
|
|
|
it('should not call the source if update was canceled', function(done) {
|
|
var that = this;
|
|
|
|
this.dataset = new Dataset({
|
|
source: this.source,
|
|
debounce: 250
|
|
});
|
|
|
|
this.source.and.callFake(fakeGetWithSyncResultsAndExtraParams);
|
|
|
|
this.dataset.update('woah');
|
|
expect(this.source.calls.count()).toBe(0);
|
|
|
|
this.dataset.update('woah 2');
|
|
expect(this.source.calls.count()).toBe(0);
|
|
|
|
this.dataset.clear();
|
|
expect(this.source.calls.count()).toBe(0);
|
|
|
|
setTimeout(function() {
|
|
expect(that.source.calls.count()).toBe(0);
|
|
done();
|
|
}, 500);
|
|
});
|
|
});
|
|
|
|
describe('#cacheSuggestions', function() {
|
|
it('should assign cachedQuery, cachedSuggestions and cachedRenderArgs properties', function() {
|
|
this.dataset.cacheSuggestions('woah', ['one', 'two'], 42);
|
|
expect(this.dataset.cachedQuery).toEqual('woah');
|
|
expect(this.dataset.cachedSuggestions).toEqual(['one', 'two']);
|
|
expect(this.dataset.cachedRenderExtraArgs).toEqual(42);
|
|
});
|
|
});
|
|
|
|
describe('#clearCachedSuggestions', function() {
|
|
it('should delete cachedQuery and cachedSuggestions properties', function() {
|
|
this.dataset.cachedQuery = 'one';
|
|
this.dataset.cachedSuggestions = ['one', 'two'];
|
|
this.dataset.cachedRenderExtraArgs = 42;
|
|
|
|
this.dataset.clearCachedSuggestions();
|
|
|
|
expect(this.dataset.cachedQuery).toBeUndefined();
|
|
expect(this.dataset.cachedSuggestions).toBeUndefined();
|
|
expect(this.dataset.cachedRenderExtraArgs).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('#clear', function() {
|
|
it('should clear suggestions', function() {
|
|
this.source.and.callFake(fakeGetWithSyncResults);
|
|
this.dataset.update('woah');
|
|
|
|
this.dataset.clear();
|
|
expect(this.dataset.getRoot()).toBeEmpty();
|
|
});
|
|
|
|
it('should cancel pending updates', function() {
|
|
var spy = spyOn(this.dataset, 'cancel');
|
|
|
|
this.source.and.callFake(fakeGetWithSyncResults);
|
|
this.dataset.update('woah');
|
|
expect(this.dataset.canceled).toBe(false);
|
|
|
|
this.dataset.clear();
|
|
expect(spy).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('#isEmpty', function() {
|
|
it('should return true when empty', function() {
|
|
expect(this.dataset.isEmpty()).toBe(true);
|
|
});
|
|
|
|
it('should return false when not empty', function() {
|
|
this.source.and.callFake(fakeGetWithSyncResults);
|
|
this.dataset.update('woah');
|
|
|
|
expect(this.dataset.isEmpty()).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('#destroy', function() {
|
|
it('should null out the reference to the dataset element', function() {
|
|
this.dataset.destroy();
|
|
|
|
expect(this.dataset.$el).toBeNull();
|
|
});
|
|
|
|
it('should clear suggestion cache', function() {
|
|
var spy = spyOn(this.dataset, 'clearCachedSuggestions');
|
|
this.dataset.destroy();
|
|
expect(spy).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
// helper functions
|
|
// ----------------
|
|
|
|
function fakeGetWithSyncResults(query, cb) {
|
|
cb([
|
|
{value: 'one', raw: {value: 'one'}},
|
|
{value: 'two', raw: {value: 'two'}},
|
|
{value: 'three', raw: {value: 'three'}}
|
|
]);
|
|
}
|
|
|
|
function fakeGetForDisplayFn(query, cb) {
|
|
cb([{display: '4'}, {display: '5'}, {display: '6'}]);
|
|
}
|
|
|
|
function fakeGetWithSyncNonArrayResults(query, cb) {
|
|
cb({});
|
|
}
|
|
|
|
function fakeGetWithSyncEmptyResults(query, cb) {
|
|
cb();
|
|
}
|
|
|
|
function fakeGetWithSyncEmptyResultsAndExtraParams(query, cb) {
|
|
cb([], 42, true, false);
|
|
}
|
|
|
|
function fakeGetWithSyncResultsAndExtraParams(query, cb) {
|
|
cb([
|
|
{value: 'one', raw: {value: 'one'}},
|
|
{value: 'two', raw: {value: 'two'}},
|
|
{value: 'three', raw: {value: 'three'}}
|
|
], 42, true, false);
|
|
}
|
|
|
|
function fakeGetWithAsyncResults(query, cb) {
|
|
setTimeout(function() {
|
|
cb([
|
|
{value: 'four', raw: {value: 'four'}},
|
|
{value: 'five', raw: {value: 'five'}}
|
|
]);
|
|
}, 0);
|
|
}
|
|
});
|