2021-02-11 21:31:41 +08:00

495 lines
13 KiB
JavaScript

'use strict';
/* eslint-env mocha, jasmine */
describe('Dropdown', function() {
require('../../src/common/dom.js').element = require('jquery');
require('../../src/jquery/plugin.js');
var $ = require('jquery');
require('jasmine-jquery');
var Dropdown = require('../../src/autocomplete/dropdown.js');
var fixtures = require('../fixtures.js');
var mocks = require('../helpers/mocks.js');
Dropdown.Dataset = mocks(Dropdown.Dataset);
beforeEach(function() {
var $fixture;
setFixtures(fixtures.html.menu);
$fixture = $('#jasmine-fixtures');
this.$menu = $fixture.find('.aa-dropdown-menu');
this.$menu.html(fixtures.html.dataset);
this.view = new Dropdown({menu: this.$menu, datasets: [{}]});
this.dataset = this.view.datasets[0];
});
it('should throw an error if menu and/or datasets is missing', function() {
expect(noMenu).toThrow();
expect(noDatasets).toThrow();
function noMenu() {
new Dropdown({menu: '.menu'});
}
function noDatasets() {
new Dropdown({datasets: true});
}
});
describe('when click event is triggered on a suggestion', function() {
it('should trigger suggestionClicked', function() {
var spy;
this.view.onSync('suggestionClicked', spy = jasmine.createSpy());
this.$menu.find('.aa-suggestion').first().click();
expect(spy).toHaveBeenCalled();
});
});
describe('when instantiated with a custom header or footer', function() {
beforeEach(function() {
this.view.destroy();
this.view = new Dropdown({
menu: this.$menu,
datasets: [{}],
templates: {
header: function() { return '<h2 class="header">Header</h2>'; },
footer: '<h2 class="footer">Footer</h2>'
}
});
});
it('should include the header', function() {
expect(this.$menu.find('h2.header').length).toEqual(1);
});
it('should include the footer', function() {
expect(this.$menu.find('h2.footer').length).toEqual(1);
});
});
describe('when instantiated with a custom empty template', function() {
beforeEach(function() {
this.view.destroy();
this.view = new Dropdown({
menu: this.$menu,
datasets: [{}],
templates: {
empty: '<h3 class="empty">this is empty</h3>'
},
minLength: 4
});
});
it('should include the empty', function() {
expect(this.$menu.find('.aa-empty').length).toEqual(1);
expect(this.$menu.find('.aa-empty').children().length).toEqual(0);
expect(this.$menu.find('.aa-empty').css('display')).toEqual('none');
});
it('should not hide the dropdown if empty', function() {
this.view.datasets[0].isEmpty.and.returnValue(true);
this.view.open();
this.view._onRendered('rendered', 'a query');
expect(this.$menu.find('.aa-empty').length).toEqual(1);
expect(this.$menu.find('.aa-empty').children().length).toEqual(1);
expect(this.$menu.find('.aa-empty').find('h3.empty').length).toEqual(1);
expect(this.$menu.find('.aa-empty').css('display')).not.toEqual('none');
});
it('should trigger empty', function() {
var spy;
this.view.datasets[0].isEmpty.and.returnValue(true);
this.view.onSync('empty', spy = jasmine.createSpy());
this.view.open();
this.view._onRendered('rendered', 'a query');
expect(spy).toHaveBeenCalled();
});
it('should not trigger empty if minLength is no satisfied', function() {
var spy;
this.view.datasets[0].isEmpty.and.returnValue(true);
this.view.onSync('empty', spy = jasmine.createSpy());
this.view.open();
this.view._onRendered('rendered', 'te');
expect(spy).not.toHaveBeenCalled();
expect(this.$menu.find('.aa-empty').css('display')).toEqual('none');
});
});
describe('when mouseenter is triggered on a suggestion', function() {
it('should remove pre-existing cursor', function(done) {
var $first;
var $last;
$first = this.$menu.find('.aa-suggestion').first();
$last = this.$menu.find('.aa-suggestion').last();
$first.addClass('aa-cursor');
$last.mouseenter();
// see implementation on why we need this setTimeout
setTimeout(function() {
expect($first).not.toHaveClass('aa-cursor');
expect($last).toHaveClass('aa-cursor');
done();
}, 0);
});
it('should set the cursor', function(done) {
var $suggestion;
$suggestion = this.$menu.find('.aa-suggestion').first();
$suggestion.mouseenter();
// see implementation on why we need this setTimeout
setTimeout(function() {
expect($suggestion).toHaveClass('aa-cursor');
done();
}, 0);
});
it('should trigger cursorMoved', function(done) {
var spy;
var $suggestion;
this.view.onSync('cursorMoved', spy = jasmine.createSpy());
$suggestion = this.$menu.find('.aa-suggestion').first();
$suggestion.mouseenter();
setTimeout(function() {
expect(spy).toHaveBeenCalled();
done();
}, 0);
});
});
describe('when mouseleave is triggered on a suggestion', function() {
it('should remove the cursor', function() {
var $suggestion;
$suggestion = this.$menu.find('.aa-suggestion').first();
$suggestion.mouseenter().mouseleave();
expect($suggestion).not.toHaveClass('aa-cursor');
});
});
describe('when rendered is triggered on a dataset', function() {
it('should hide the dropdown if empty', function() {
this.dataset.isEmpty.and.returnValue(true);
this.view.open();
this.view._show();
this.dataset.trigger('rendered', 'the query');
expect(this.$menu).not.toBeVisible();
});
it('should show the dropdown if not empty', function() {
this.dataset.isEmpty.and.returnValue(false);
this.view.open();
this.dataset.trigger('rendered', 'the query');
expect(this.$menu).toBeVisible();
});
it('should trigger datasetRendered', function() {
var spy;
this.view.onSync('datasetRendered', spy = jasmine.createSpy());
this.dataset.trigger('rendered');
expect(spy).toHaveBeenCalled();
});
});
describe('#open', function() {
it('should display the menu if not empty', function() {
this.view.isOpen = true;
this.view.close();
expect(this.$menu).not.toBeVisible();
this.view.isEmpty = false;
this.view.open();
expect(this.$menu).toBeVisible();
});
it('should not display the menu if empty', function() {
this.view.isOpen = true;
this.view.close();
expect(this.$menu).not.toBeVisible();
this.view.isEmpty = true;
this.view.open();
expect(this.$menu).not.toBeVisible();
});
it('should trigger opened', function() {
var spy;
this.view.onSync('opened', spy = jasmine.createSpy());
this.view.close();
this.view.open();
expect(spy).toHaveBeenCalled();
});
it('should trigger shown', function() {
var spy;
this.view.onSync('shown', spy = jasmine.createSpy());
this.view.close();
this.view.isEmpty = false;
this.view.open();
expect(spy).toHaveBeenCalled();
});
it('should trigger redrawn', function() {
var spy;
this.view.onSync('redrawn', spy = jasmine.createSpy());
this.view.close();
this.view.isEmpty = false;
this.view.appendTo = 'body';
this.view.open();
expect(spy).toHaveBeenCalled();
});
});
describe('#close', function() {
it('should hide the menu', function() {
this.view.open();
this.view.close();
expect(this.$menu).not.toBeVisible();
});
it('should trigger closed', function() {
var spy;
this.view.onSync('closed', spy = jasmine.createSpy());
this.view.open();
this.view.close();
expect(spy).toHaveBeenCalled();
});
});
describe('#setLanguageDirection', function() {
it('should update css for given language direction', function() {
// TODO: eh, the toHaveCss matcher doesn't seem to work very well
/*
this.view.setLanguageDirection('rtl');
expect(this.$menu).toHaveCss({ left: 'auto', right: '0px' });
this.view.setLanguageDirection('ltr');
expect(this.$menu).toHaveCss({ left: '0px', right: 'auto' });
*/
});
});
describe('#moveCursorUp', function() {
beforeEach(function() {
this.view.open();
});
it('should move the cursor up', function() {
var $first;
var $second;
$first = this.view._getSuggestions().eq(0);
$second = this.view._getSuggestions().eq(1);
this.view._setCursor($second);
this.view.moveCursorUp();
expect(this.view._getCursor()).toContainText($first.text());
});
it('should move cursor to bottom if cursor is not present', function() {
var $bottom;
$bottom = this.view._getSuggestions().eq(-1);
this.view.moveCursorUp();
expect(this.view._getCursor()).toContainText($bottom.text());
});
it('should remove cursor if already at top', function() {
var $first;
$first = this.view._getSuggestions().eq(0);
this.view._setCursor($first);
this.view.moveCursorUp();
expect(this.view._getCursor().length).toBe(0);
});
});
describe('#moveCursorDown', function() {
beforeEach(function() {
this.view.open();
});
it('should move the cursor down', function() {
var $first;
var $second;
$first = this.view._getSuggestions().eq(0);
$second = this.view._getSuggestions().eq(1);
this.view._setCursor($first);
this.view.moveCursorDown();
expect(this.view._getCursor()).toContainText($second.text());
});
it('should move cursor to top if cursor is not present', function() {
var $first;
$first = this.view._getSuggestions().eq(0);
this.view.moveCursorDown();
expect(this.view._getCursor()).toContainText($first.text());
});
it('should remove cursor if already at bottom', function() {
var $bottom;
$bottom = this.view._getSuggestions().eq(-1);
this.view._setCursor($bottom);
this.view.moveCursorDown();
expect(this.view._getCursor().length).toBe(0);
});
});
describe('#getDatumForSuggestion', function() {
it('should extract the datum from the suggestion element', function() {
var $suggestion;
var datum;
$suggestion = $('<div>').data({aaValue: 'one', aaDatum: JSON.stringify('two')});
datum = this.view.getDatumForSuggestion($suggestion);
expect(datum).toEqual({value: 'one', raw: 'two', datasetName: undefined});
});
it('should return null if no element is given', function() {
expect(this.view.getDatumForSuggestion($('notreal'))).toBeNull();
});
});
describe('#getDatumForCursor', function() {
it('should return the datum for the cursor', function() {
var $first;
$first = this.view._getSuggestions().eq(0);
$first.data({aaValue: 'one', aaDatum: JSON.stringify('two')});
this.view._setCursor($first);
expect(this.view.getDatumForCursor())
.toEqual({value: 'one', raw: 'two', datasetName: undefined});
});
});
describe('#getDatumForTopSuggestion', function() {
it('should return the datum for top suggestion', function() {
var $first;
$first = this.view._getSuggestions().eq(0);
$first.data({aaValue: 'one', aaDatum: JSON.stringify('two')});
expect(this.view.getDatumForTopSuggestion())
.toEqual({value: 'one', raw: 'two', datasetName: undefined});
});
});
describe('#update', function() {
it('should invoke update on each dataset', function() {
this.view.update();
expect(this.dataset.update).toHaveBeenCalled();
});
});
describe('#empty', function() {
it('should invoke clear on each dataset', function() {
this.view.empty();
expect(this.dataset.clear).toHaveBeenCalled();
});
});
describe('#isVisible', function() {
it('should return true if open and not empty', function() {
this.view.isOpen = true;
this.view.isEmpty = false;
expect(this.view.isVisible()).toBe(true);
this.view.isOpen = false;
this.view.isEmpty = false;
expect(this.view.isVisible()).toBe(false);
this.view.isOpen = true;
this.view.isEmpty = true;
expect(this.view.isVisible()).toBe(false);
this.view.isOpen = false;
this.view.isEmpty = false;
expect(this.view.isVisible()).toBe(false);
});
});
describe('#destroy', function() {
it('should remove event handlers', function() {
var $menu = this.view.$menu;
spyOn($menu, 'off');
this.view.destroy();
expect($menu.off).toHaveBeenCalledWith('.aa');
});
it('should destroy its datasets', function() {
this.view.destroy();
expect(this.dataset.destroy).toHaveBeenCalled();
});
it('should null out its reference to the menu element', function() {
this.view.destroy();
expect(this.view.$menu).toBeNull();
});
});
});