diff options
author | Fatih Acet <acetfatih@gmail.com> | 2016-07-24 23:45:11 +0300 |
---|---|---|
committer | Fatih Acet <acetfatih@gmail.com> | 2016-07-24 23:45:11 +0300 |
commit | aaa9509d120524573085e94af9de5cdde83e3271 (patch) | |
tree | 3824cffd4cdd132ee9cf75a00a7624f5ccc0dabd /spec/javascripts | |
parent | 56b79181adc0bd6e9abef97ea075c14be971a01a (diff) | |
download | gitlab-ce-aaa9509d120524573085e94af9de5cdde83e3271.tar.gz |
ES6ify all the things!
Diffstat (limited to 'spec/javascripts')
50 files changed, 1905 insertions, 2581 deletions
diff --git a/spec/javascripts/application_spec.js b/spec/javascripts/application_spec.js new file mode 100644 index 00000000000..b48026c3b77 --- /dev/null +++ b/spec/javascripts/application_spec.js @@ -0,0 +1,32 @@ + +/*= require lib/utils/common_utils */ + +(function() { + describe('Application', function() { + return describe('disable buttons', function() { + fixture.preload('application.html'); + beforeEach(function() { + return fixture.load('application.html'); + }); + it('should prevent default action for disabled buttons', function() { + var $button, isClicked; + gl.utils.preventDisabledButtons(); + isClicked = false; + $button = $('#test-button'); + $button.click(function() { + return isClicked = true; + }); + $button.trigger('click'); + return expect(isClicked).toBe(false); + }); + return it('should be on the same page if a disabled link clicked', function() { + var locationBeforeLinkClick; + locationBeforeLinkClick = window.location.href; + gl.utils.preventDisabledButtons(); + $('#test-link').click(); + return expect(window.location.href).toBe(locationBeforeLinkClick); + }); + }); + }); + +}).call(this); diff --git a/spec/javascripts/application_spec.js.coffee b/spec/javascripts/application_spec.js.coffee deleted file mode 100644 index 4b6a2bb5440..00000000000 --- a/spec/javascripts/application_spec.js.coffee +++ /dev/null @@ -1,30 +0,0 @@ -#= require lib/utils/common_utils - -describe 'Application', -> - describe 'disable buttons', -> - fixture.preload('application.html') - - beforeEach -> - fixture.load('application.html') - - it 'should prevent default action for disabled buttons', -> - - gl.utils.preventDisabledButtons() - - isClicked = false - $button = $ '#test-button' - - $button.click -> isClicked = true - $button.trigger 'click' - - expect(isClicked).toBe false - - - it 'should be on the same page if a disabled link clicked', -> - - locationBeforeLinkClick = window.location.href - gl.utils.preventDisabledButtons() - - $('#test-link').click() - - expect(window.location.href).toBe locationBeforeLinkClick diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js new file mode 100644 index 00000000000..3ddc163033e --- /dev/null +++ b/spec/javascripts/awards_handler_spec.js @@ -0,0 +1,187 @@ + +/*= require awards_handler */ + + +/*= require jquery */ + + +/*= require jquery.cookie */ + + +/*= require ./fixtures/emoji_menu */ + +(function() { + var awardsHandler, lazyAssert; + + awardsHandler = null; + + window.gl || (window.gl = {}); + + window.gon || (window.gon = {}); + + gl.emojiAliases = function() { + return { + '+1': 'thumbsup', + '-1': 'thumbsdown' + }; + }; + + gon.award_menu_url = '/emojis'; + + lazyAssert = function(done, assertFn) { + return setTimeout(function() { + assertFn(); + return done(); + }, 333); + }; + + describe('AwardsHandler', function() { + fixture.preload('awards_handler.html'); + beforeEach(function() { + fixture.load('awards_handler.html'); + awardsHandler = new AwardsHandler; + spyOn(awardsHandler, 'postEmoji').and.callFake((function(_this) { + return function(url, emoji, cb) { + return cb(); + }; + })(this)); + return spyOn(jQuery, 'get').and.callFake(function(req, cb) { + return cb(window.emojiMenu); + }); + }); + describe('::showEmojiMenu', function() { + it('should show emoji menu when Add emoji button clicked', function(done) { + $('.js-add-award').eq(0).click(); + return lazyAssert(done, function() { + var $emojiMenu; + $emojiMenu = $('.emoji-menu'); + expect($emojiMenu.length).toBe(1); + expect($emojiMenu.hasClass('is-visible')).toBe(true); + expect($emojiMenu.find('#emoji_search').length).toBe(1); + return expect($('.js-awards-block.current').length).toBe(1); + }); + }); + it('should also show emoji menu for the smiley icon in notes', function(done) { + $('.note-action-button').click(); + return lazyAssert(done, function() { + var $emojiMenu; + $emojiMenu = $('.emoji-menu'); + return expect($emojiMenu.length).toBe(1); + }); + }); + return it('should remove emoji menu when body is clicked', function(done) { + $('.js-add-award').eq(0).click(); + return lazyAssert(done, function() { + var $emojiMenu; + $emojiMenu = $('.emoji-menu'); + $('body').click(); + expect($emojiMenu.length).toBe(1); + expect($emojiMenu.hasClass('is-visible')).toBe(false); + return expect($('.js-awards-block.current').length).toBe(0); + }); + }); + }); + describe('::addAwardToEmojiBar', function() { + it('should add emoji to votes block', function() { + var $emojiButton, $votesBlock; + $votesBlock = $('.js-awards-block').eq(0); + awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false); + $emojiButton = $votesBlock.find('[data-emoji=heart]'); + expect($emojiButton.length).toBe(1); + expect($emojiButton.next('.js-counter').text()).toBe('1'); + return expect($votesBlock.hasClass('hidden')).toBe(false); + }); + it('should remove the emoji when we click again', function() { + var $emojiButton, $votesBlock; + $votesBlock = $('.js-awards-block').eq(0); + awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false); + awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false); + $emojiButton = $votesBlock.find('[data-emoji=heart]'); + return expect($emojiButton.length).toBe(0); + }); + return it('should decrement the emoji counter', function() { + var $emojiButton, $votesBlock; + $votesBlock = $('.js-awards-block').eq(0); + awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false); + $emojiButton = $votesBlock.find('[data-emoji=heart]'); + $emojiButton.next('.js-counter').text(5); + awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false); + expect($emojiButton.length).toBe(1); + return expect($emojiButton.next('.js-counter').text()).toBe('4'); + }); + }); + describe('::getAwardUrl', function() { + return it('should return the url for request', function() { + return expect(awardsHandler.getAwardUrl()).toBe('/gitlab-org/gitlab-test/issues/8/toggle_award_emoji'); + }); + }); + describe('::addAward and ::checkMutuality', function() { + return it('should handle :+1: and :-1: mutuality', function() { + var $thumbsDownEmoji, $thumbsUpEmoji, $votesBlock, awardUrl; + awardUrl = awardsHandler.getAwardUrl(); + $votesBlock = $('.js-awards-block').eq(0); + $thumbsUpEmoji = $votesBlock.find('[data-emoji=thumbsup]').parent(); + $thumbsDownEmoji = $votesBlock.find('[data-emoji=thumbsdown]').parent(); + awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false); + expect($thumbsUpEmoji.hasClass('active')).toBe(true); + expect($thumbsDownEmoji.hasClass('active')).toBe(false); + $thumbsUpEmoji.tooltip(); + $thumbsDownEmoji.tooltip(); + awardsHandler.addAward($votesBlock, awardUrl, 'thumbsdown', true); + expect($thumbsUpEmoji.hasClass('active')).toBe(false); + return expect($thumbsDownEmoji.hasClass('active')).toBe(true); + }); + }); + describe('::removeEmoji', function() { + return it('should remove emoji', function() { + var $votesBlock, awardUrl; + awardUrl = awardsHandler.getAwardUrl(); + $votesBlock = $('.js-awards-block').eq(0); + awardsHandler.addAward($votesBlock, awardUrl, 'fire', false); + expect($votesBlock.find('[data-emoji=fire]').length).toBe(1); + awardsHandler.removeEmoji($votesBlock.find('[data-emoji=fire]').closest('button')); + return expect($votesBlock.find('[data-emoji=fire]').length).toBe(0); + }); + }); + describe('search', function() { + return it('should filter the emoji', function() { + $('.js-add-award').eq(0).click(); + expect($('[data-emoji=angel]').is(':visible')).toBe(true); + expect($('[data-emoji=anger]').is(':visible')).toBe(true); + $('#emoji_search').val('ali').trigger('keyup'); + expect($('[data-emoji=angel]').is(':visible')).toBe(false); + expect($('[data-emoji=anger]').is(':visible')).toBe(false); + return expect($('[data-emoji=alien]').is(':visible')).toBe(true); + }); + }); + return describe('emoji menu', function() { + var openEmojiMenuAndAddEmoji, selector; + selector = '[data-emoji=sunglasses]'; + openEmojiMenuAndAddEmoji = function() { + var $block, $emoji, $menu; + $('.js-add-award').eq(0).click(); + $menu = $('.emoji-menu'); + $block = $('.js-awards-block'); + $emoji = $menu.find(".emoji-menu-list-item " + selector); + expect($emoji.length).toBe(1); + expect($block.find(selector).length).toBe(0); + $emoji.click(); + expect($menu.hasClass('.is-visible')).toBe(false); + return expect($block.find(selector).length).toBe(1); + }; + it('should add selected emoji to awards block', function() { + return openEmojiMenuAndAddEmoji(); + }); + return it('should remove already selected emoji', function() { + var $block, $emoji; + openEmojiMenuAndAddEmoji(); + $('.js-add-award').eq(0).click(); + $block = $('.js-awards-block'); + $emoji = $('.emoji-menu').find(".emoji-menu-list-item " + selector); + $emoji.click(); + return expect($block.find(selector).length).toBe(0); + }); + }); + }); + +}).call(this); diff --git a/spec/javascripts/awards_handler_spec.js.coffee b/spec/javascripts/awards_handler_spec.js.coffee deleted file mode 100644 index d7f9c6fc076..00000000000 --- a/spec/javascripts/awards_handler_spec.js.coffee +++ /dev/null @@ -1,200 +0,0 @@ -#= require awards_handler -#= require jquery -#= require jquery.cookie -#= require ./fixtures/emoji_menu - -awardsHandler = null -window.gl or= {} -window.gon or= {} -gl.emojiAliases = -> return { '+1': 'thumbsup', '-1': 'thumbsdown' } -gon.award_menu_url = '/emojis' - - -lazyAssert = (done, assertFn) -> - - setTimeout -> # Maybe jasmine.clock here? - assertFn() - done() - , 333 - - -describe 'AwardsHandler', -> - - fixture.preload 'awards_handler.html' - - beforeEach -> - fixture.load 'awards_handler.html' - awardsHandler = new AwardsHandler - spyOn(awardsHandler, 'postEmoji').and.callFake (url, emoji, cb) => cb() - spyOn(jQuery, 'get').and.callFake (req, cb) -> cb window.emojiMenu - - - describe '::showEmojiMenu', -> - - it 'should show emoji menu when Add emoji button clicked', (done) -> - - $('.js-add-award').eq(0).click() - - lazyAssert done, -> - $emojiMenu = $ '.emoji-menu' - expect($emojiMenu.length).toBe 1 - expect($emojiMenu.hasClass('is-visible')).toBe yes - expect($emojiMenu.find('#emoji_search').length).toBe 1 - expect($('.js-awards-block.current').length).toBe 1 - - - it 'should also show emoji menu for the smiley icon in notes', (done) -> - - $('.note-action-button').click() - - lazyAssert done, -> - $emojiMenu = $ '.emoji-menu' - expect($emojiMenu.length).toBe 1 - - - it 'should remove emoji menu when body is clicked', (done) -> - - $('.js-add-award').eq(0).click() - - lazyAssert done, -> - $emojiMenu = $('.emoji-menu') - $('body').click() - expect($emojiMenu.length).toBe 1 - expect($emojiMenu.hasClass('is-visible')).toBe no - expect($('.js-awards-block.current').length).toBe 0 - - - describe '::addAwardToEmojiBar', -> - - it 'should add emoji to votes block', -> - - $votesBlock = $('.js-awards-block').eq 0 - awardsHandler.addAwardToEmojiBar $votesBlock, 'heart', no - - $emojiButton = $votesBlock.find '[data-emoji=heart]' - - expect($emojiButton.length).toBe 1 - expect($emojiButton.next('.js-counter').text()).toBe '1' - expect($votesBlock.hasClass('hidden')).toBe no - - - it 'should remove the emoji when we click again', -> - - $votesBlock = $('.js-awards-block').eq 0 - awardsHandler.addAwardToEmojiBar $votesBlock, 'heart', no - awardsHandler.addAwardToEmojiBar $votesBlock, 'heart', no - $emojiButton = $votesBlock.find '[data-emoji=heart]' - - expect($emojiButton.length).toBe 0 - - - it 'should decrement the emoji counter', -> - - $votesBlock = $('.js-awards-block').eq 0 - awardsHandler.addAwardToEmojiBar $votesBlock, 'heart', no - - $emojiButton = $votesBlock.find '[data-emoji=heart]' - $emojiButton.next('.js-counter').text 5 - - awardsHandler.addAwardToEmojiBar $votesBlock, 'heart', no - - expect($emojiButton.length).toBe 1 - expect($emojiButton.next('.js-counter').text()).toBe '4' - - - describe '::getAwardUrl', -> - - it 'should return the url for request', -> - - expect(awardsHandler.getAwardUrl()).toBe '/gitlab-org/gitlab-test/issues/8/toggle_award_emoji' - - - describe '::addAward and ::checkMutuality', -> - - it 'should handle :+1: and :-1: mutuality', -> - - awardUrl = awardsHandler.getAwardUrl() - $votesBlock = $('.js-awards-block').eq 0 - $thumbsUpEmoji = $votesBlock.find('[data-emoji=thumbsup]').parent() - $thumbsDownEmoji = $votesBlock.find('[data-emoji=thumbsdown]').parent() - - awardsHandler.addAward $votesBlock, awardUrl, 'thumbsup', no - - expect($thumbsUpEmoji.hasClass('active')).toBe yes - expect($thumbsDownEmoji.hasClass('active')).toBe no - - $thumbsUpEmoji.tooltip() - $thumbsDownEmoji.tooltip() - - awardsHandler.addAward $votesBlock, awardUrl, 'thumbsdown', yes - - expect($thumbsUpEmoji.hasClass('active')).toBe no - expect($thumbsDownEmoji.hasClass('active')).toBe yes - - - describe '::removeEmoji', -> - - it 'should remove emoji', -> - - awardUrl = awardsHandler.getAwardUrl() - $votesBlock = $('.js-awards-block').eq 0 - - awardsHandler.addAward $votesBlock, awardUrl, 'fire', no - expect($votesBlock.find('[data-emoji=fire]').length).toBe 1 - - awardsHandler.removeEmoji $votesBlock.find('[data-emoji=fire]').closest('button') - expect($votesBlock.find('[data-emoji=fire]').length).toBe 0 - - - describe 'search', -> - - it 'should filter the emoji', -> - - $('.js-add-award').eq(0).click() - - expect($('[data-emoji=angel]').is(':visible')).toBe yes - expect($('[data-emoji=anger]').is(':visible')).toBe yes - - $('#emoji_search').val('ali').trigger 'keyup' - - expect($('[data-emoji=angel]').is(':visible')).toBe no - expect($('[data-emoji=anger]').is(':visible')).toBe no - expect($('[data-emoji=alien]').is(':visible')).toBe yes - - - describe 'emoji menu', -> - - selector = '[data-emoji=sunglasses]' - - openEmojiMenuAndAddEmoji = -> - - $('.js-add-award').eq(0).click() - - $menu = $ '.emoji-menu' - $block = $ '.js-awards-block' - $emoji = $menu.find ".emoji-menu-list-item #{selector}" - - expect($emoji.length).toBe 1 - expect($block.find(selector).length).toBe 0 - - $emoji.click() - - expect($menu.hasClass('.is-visible')).toBe no - expect($block.find(selector).length).toBe 1 - - - it 'should add selected emoji to awards block', -> - - openEmojiMenuAndAddEmoji() - - - it 'should remove already selected emoji', -> - - openEmojiMenuAndAddEmoji() - $('.js-add-award').eq(0).click() - - $block = $ '.js-awards-block' - $emoji = $('.emoji-menu').find ".emoji-menu-list-item #{selector}" - - $emoji.click() - expect($block.find(selector).length).toBe 0 diff --git a/spec/javascripts/behaviors/autosize_spec.js b/spec/javascripts/behaviors/autosize_spec.js new file mode 100644 index 00000000000..78795f7654a --- /dev/null +++ b/spec/javascripts/behaviors/autosize_spec.js @@ -0,0 +1,21 @@ + +/*= require behaviors/autosize */ + +(function() { + describe('Autosize behavior', function() { + var load; + beforeEach(function() { + return fixture.set('<textarea class="js-autosize" style="resize: vertical"></textarea>'); + }); + it('does not overwrite the resize property', function() { + load(); + return expect($('textarea')).toHaveCss({ + resize: 'vertical' + }); + }); + return load = function() { + return $(document).trigger('page:load'); + }; + }); + +}).call(this); diff --git a/spec/javascripts/behaviors/autosize_spec.js.coffee b/spec/javascripts/behaviors/autosize_spec.js.coffee deleted file mode 100644 index 7fc1d19c35f..00000000000 --- a/spec/javascripts/behaviors/autosize_spec.js.coffee +++ /dev/null @@ -1,11 +0,0 @@ -#= require behaviors/autosize - -describe 'Autosize behavior', -> - beforeEach -> - fixture.set('<textarea class="js-autosize" style="resize: vertical"></textarea>') - - it 'does not overwrite the resize property', -> - load() - expect($('textarea')).toHaveCss(resize: 'vertical') - - load = -> $(document).trigger('page:load') diff --git a/spec/javascripts/behaviors/quick_submit_spec.js b/spec/javascripts/behaviors/quick_submit_spec.js new file mode 100644 index 00000000000..4c52ecd903d --- /dev/null +++ b/spec/javascripts/behaviors/quick_submit_spec.js @@ -0,0 +1,93 @@ + +/*= require behaviors/quick_submit */ + +(function() { + describe('Quick Submit behavior', function() { + var keydownEvent; + fixture.preload('behaviors/quick_submit.html'); + beforeEach(function() { + fixture.load('behaviors/quick_submit.html'); + $('form').submit(function(e) { + return e.preventDefault(); + }); + return this.spies = { + submit: spyOnEvent('form', 'submit') + }; + }); + it('does not respond to other keyCodes', function() { + $('input.quick-submit-input').trigger(keydownEvent({ + keyCode: 32 + })); + return expect(this.spies.submit).not.toHaveBeenTriggered(); + }); + it('does not respond to Enter alone', function() { + $('input.quick-submit-input').trigger(keydownEvent({ + ctrlKey: false, + metaKey: false + })); + return expect(this.spies.submit).not.toHaveBeenTriggered(); + }); + it('does not respond to repeated events', function() { + $('input.quick-submit-input').trigger(keydownEvent({ + repeat: true + })); + return expect(this.spies.submit).not.toHaveBeenTriggered(); + }); + it('disables submit buttons', function() { + $('textarea').trigger(keydownEvent()); + expect($('input[type=submit]')).toBeDisabled(); + return expect($('button[type=submit]')).toBeDisabled(); + }); + if (navigator.userAgent.match(/Macintosh/)) { + it('responds to Meta+Enter', function() { + $('input.quick-submit-input').trigger(keydownEvent()); + return expect(this.spies.submit).toHaveBeenTriggered(); + }); + it('excludes other modifier keys', function() { + $('input.quick-submit-input').trigger(keydownEvent({ + altKey: true + })); + $('input.quick-submit-input').trigger(keydownEvent({ + ctrlKey: true + })); + $('input.quick-submit-input').trigger(keydownEvent({ + shiftKey: true + })); + return expect(this.spies.submit).not.toHaveBeenTriggered(); + }); + } else { + it('responds to Ctrl+Enter', function() { + $('input.quick-submit-input').trigger(keydownEvent()); + return expect(this.spies.submit).toHaveBeenTriggered(); + }); + it('excludes other modifier keys', function() { + $('input.quick-submit-input').trigger(keydownEvent({ + altKey: true + })); + $('input.quick-submit-input').trigger(keydownEvent({ + metaKey: true + })); + $('input.quick-submit-input').trigger(keydownEvent({ + shiftKey: true + })); + return expect(this.spies.submit).not.toHaveBeenTriggered(); + }); + } + return keydownEvent = function(options) { + var defaults; + if (navigator.userAgent.match(/Macintosh/)) { + defaults = { + keyCode: 13, + metaKey: true + }; + } else { + defaults = { + keyCode: 13, + ctrlKey: true + }; + } + return $.Event('keydown', $.extend({}, defaults, options)); + }; + }); + +}).call(this); diff --git a/spec/javascripts/behaviors/quick_submit_spec.js.coffee b/spec/javascripts/behaviors/quick_submit_spec.js.coffee deleted file mode 100644 index d3b003a328a..00000000000 --- a/spec/javascripts/behaviors/quick_submit_spec.js.coffee +++ /dev/null @@ -1,70 +0,0 @@ -#= require behaviors/quick_submit - -describe 'Quick Submit behavior', -> - fixture.preload('behaviors/quick_submit.html') - - beforeEach -> - fixture.load('behaviors/quick_submit.html') - - # Prevent a form submit from moving us off the testing page - $('form').submit (e) -> e.preventDefault() - - @spies = { - submit: spyOnEvent('form', 'submit') - } - - it 'does not respond to other keyCodes', -> - $('input.quick-submit-input').trigger(keydownEvent(keyCode: 32)) - - expect(@spies.submit).not.toHaveBeenTriggered() - - it 'does not respond to Enter alone', -> - $('input.quick-submit-input').trigger(keydownEvent(ctrlKey: false, metaKey: false)) - - expect(@spies.submit).not.toHaveBeenTriggered() - - it 'does not respond to repeated events', -> - $('input.quick-submit-input').trigger(keydownEvent(repeat: true)) - - expect(@spies.submit).not.toHaveBeenTriggered() - - it 'disables submit buttons', -> - $('textarea').trigger(keydownEvent()) - - expect($('input[type=submit]')).toBeDisabled() - expect($('button[type=submit]')).toBeDisabled() - - # We cannot stub `navigator.userAgent` for CI's `rake teaspoon` task, so we'll - # only run the tests that apply to the current platform - if navigator.userAgent.match(/Macintosh/) - it 'responds to Meta+Enter', -> - $('input.quick-submit-input').trigger(keydownEvent()) - - expect(@spies.submit).toHaveBeenTriggered() - - it 'excludes other modifier keys', -> - $('input.quick-submit-input').trigger(keydownEvent(altKey: true)) - $('input.quick-submit-input').trigger(keydownEvent(ctrlKey: true)) - $('input.quick-submit-input').trigger(keydownEvent(shiftKey: true)) - - expect(@spies.submit).not.toHaveBeenTriggered() - else - it 'responds to Ctrl+Enter', -> - $('input.quick-submit-input').trigger(keydownEvent()) - - expect(@spies.submit).toHaveBeenTriggered() - - it 'excludes other modifier keys', -> - $('input.quick-submit-input').trigger(keydownEvent(altKey: true)) - $('input.quick-submit-input').trigger(keydownEvent(metaKey: true)) - $('input.quick-submit-input').trigger(keydownEvent(shiftKey: true)) - - expect(@spies.submit).not.toHaveBeenTriggered() - - keydownEvent = (options) -> - if navigator.userAgent.match(/Macintosh/) - defaults = { keyCode: 13, metaKey: true } - else - defaults = { keyCode: 13, ctrlKey: true } - - $.Event('keydown', $.extend({}, defaults, options)) diff --git a/spec/javascripts/behaviors/requires_input_spec.js b/spec/javascripts/behaviors/requires_input_spec.js new file mode 100644 index 00000000000..724c3baf989 --- /dev/null +++ b/spec/javascripts/behaviors/requires_input_spec.js @@ -0,0 +1,44 @@ + +/*= require behaviors/requires_input */ + +(function() { + describe('requiresInput', function() { + fixture.preload('behaviors/requires_input.html'); + beforeEach(function() { + return fixture.load('behaviors/requires_input.html'); + }); + it('disables submit when any field is required', function() { + $('.js-requires-input').requiresInput(); + return expect($('.submit')).toBeDisabled(); + }); + it('enables submit when no field is required', function() { + $('*[required=required]').removeAttr('required'); + $('.js-requires-input').requiresInput(); + return expect($('.submit')).not.toBeDisabled(); + }); + it('enables submit when all required fields are pre-filled', function() { + $('*[required=required]').remove(); + $('.js-requires-input').requiresInput(); + return expect($('.submit')).not.toBeDisabled(); + }); + it('enables submit when all required fields receive input', function() { + $('.js-requires-input').requiresInput(); + $('#required1').val('input1').change(); + expect($('.submit')).toBeDisabled(); + $('#optional1').val('input1').change(); + expect($('.submit')).toBeDisabled(); + $('#required2').val('input2').change(); + $('#required3').val('input3').change(); + $('#required4').val('input4').change(); + $('#required5').val('1').change(); + return expect($('.submit')).not.toBeDisabled(); + }); + return it('is called on page:load event', function() { + var spy; + spy = spyOn($.fn, 'requiresInput'); + $(document).trigger('page:load'); + return expect(spy).toHaveBeenCalled(); + }); + }); + +}).call(this); diff --git a/spec/javascripts/behaviors/requires_input_spec.js.coffee b/spec/javascripts/behaviors/requires_input_spec.js.coffee deleted file mode 100644 index 61a17632173..00000000000 --- a/spec/javascripts/behaviors/requires_input_spec.js.coffee +++ /dev/null @@ -1,49 +0,0 @@ -#= require behaviors/requires_input - -describe 'requiresInput', -> - fixture.preload('behaviors/requires_input.html') - - beforeEach -> - fixture.load('behaviors/requires_input.html') - - it 'disables submit when any field is required', -> - $('.js-requires-input').requiresInput() - - expect($('.submit')).toBeDisabled() - - it 'enables submit when no field is required', -> - $('*[required=required]').removeAttr('required') - - $('.js-requires-input').requiresInput() - - expect($('.submit')).not.toBeDisabled() - - it 'enables submit when all required fields are pre-filled', -> - $('*[required=required]').remove() - - $('.js-requires-input').requiresInput() - - expect($('.submit')).not.toBeDisabled() - - it 'enables submit when all required fields receive input', -> - $('.js-requires-input').requiresInput() - - $('#required1').val('input1').change() - expect($('.submit')).toBeDisabled() - - $('#optional1').val('input1').change() - expect($('.submit')).toBeDisabled() - - $('#required2').val('input2').change() - $('#required3').val('input3').change() - $('#required4').val('input4').change() - $('#required5').val('1').change() - - expect($('.submit')).not.toBeDisabled() - - it 'is called on page:load event', -> - spy = spyOn($.fn, 'requiresInput') - - $(document).trigger('page:load') - - expect(spy).toHaveBeenCalled() diff --git a/spec/javascripts/extensions/array_spec.js b/spec/javascripts/extensions/array_spec.js new file mode 100644 index 00000000000..eced2f6575d --- /dev/null +++ b/spec/javascripts/extensions/array_spec.js @@ -0,0 +1,22 @@ + +/*= require extensions/array */ + +(function() { + describe('Array extensions', function() { + describe('first', function() { + return it('returns the first item', function() { + var arr; + arr = [0, 1, 2, 3, 4, 5]; + return expect(arr.first()).toBe(0); + }); + }); + return describe('last', function() { + return it('returns the last item', function() { + var arr; + arr = [0, 1, 2, 3, 4, 5]; + return expect(arr.last()).toBe(5); + }); + }); + }); + +}).call(this); diff --git a/spec/javascripts/extensions/array_spec.js.coffee b/spec/javascripts/extensions/array_spec.js.coffee deleted file mode 100644 index 4ceac619422..00000000000 --- a/spec/javascripts/extensions/array_spec.js.coffee +++ /dev/null @@ -1,12 +0,0 @@ -#= require extensions/array - -describe 'Array extensions', -> - describe 'first', -> - it 'returns the first item', -> - arr = [0, 1, 2, 3, 4, 5] - expect(arr.first()).toBe(0) - - describe 'last', -> - it 'returns the last item', -> - arr = [0, 1, 2, 3, 4, 5] - expect(arr.last()).toBe(5) diff --git a/spec/javascripts/extensions/jquery_spec.js b/spec/javascripts/extensions/jquery_spec.js new file mode 100644 index 00000000000..b644344b95a --- /dev/null +++ b/spec/javascripts/extensions/jquery_spec.js @@ -0,0 +1,42 @@ + +/*= require extensions/jquery */ + +(function() { + describe('jQuery extensions', function() { + describe('disable', function() { + beforeEach(function() { + return fixture.set('<input type="text" />'); + }); + it('adds the disabled attribute', function() { + var $input; + $input = $('input').first(); + $input.disable(); + return expect($input).toHaveAttr('disabled', 'disabled'); + }); + return it('adds the disabled class', function() { + var $input; + $input = $('input').first(); + $input.disable(); + return expect($input).toHaveClass('disabled'); + }); + }); + return describe('enable', function() { + beforeEach(function() { + return fixture.set('<input type="text" disabled="disabled" class="disabled" />'); + }); + it('removes the disabled attribute', function() { + var $input; + $input = $('input').first(); + $input.enable(); + return expect($input).not.toHaveAttr('disabled'); + }); + return it('removes the disabled class', function() { + var $input; + $input = $('input').first(); + $input.enable(); + return expect($input).not.toHaveClass('disabled'); + }); + }); + }); + +}).call(this); diff --git a/spec/javascripts/extensions/jquery_spec.js.coffee b/spec/javascripts/extensions/jquery_spec.js.coffee deleted file mode 100644 index b10e16b7d01..00000000000 --- a/spec/javascripts/extensions/jquery_spec.js.coffee +++ /dev/null @@ -1,34 +0,0 @@ -#= require extensions/jquery - -describe 'jQuery extensions', -> - describe 'disable', -> - beforeEach -> - fixture.set '<input type="text" />' - - it 'adds the disabled attribute', -> - $input = $('input').first() - - $input.disable() - expect($input).toHaveAttr('disabled', 'disabled') - - it 'adds the disabled class', -> - $input = $('input').first() - - $input.disable() - expect($input).toHaveClass('disabled') - - describe 'enable', -> - beforeEach -> - fixture.set '<input type="text" disabled="disabled" class="disabled" />' - - it 'removes the disabled attribute', -> - $input = $('input').first() - - $input.enable() - expect($input).not.toHaveAttr('disabled') - - it 'removes the disabled class', -> - $input = $('input').first() - - $input.enable() - expect($input).not.toHaveClass('disabled') diff --git a/spec/javascripts/fixtures/emoji_menu.coffee b/spec/javascripts/fixtures/emoji_menu.coffee deleted file mode 100644 index ce1a41390d2..00000000000 --- a/spec/javascripts/fixtures/emoji_menu.coffee +++ /dev/null @@ -1,957 +0,0 @@ -window.emojiMenu = """ - <div class='emoji-menu'> - <input type="text" name="emoji_search" id="emoji_search" value="" class="emoji-search search-input form-control" /> - <div class='emoji-menu-content'> - <h5 class='emoji-menu-title'> - Emoticons - </h5> - <ul class='clearfix emoji-menu-list'> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F47D" title="alien" data-aliases="" data-emoji="alien" data-unicode-name="1F47D"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F47C" title="angel" data-aliases="" data-emoji="angel" data-unicode-name="1F47C"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F4A2" title="anger" data-aliases="" data-emoji="anger" data-unicode-name="1F4A2"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F620" title="angry" data-aliases="" data-emoji="angry" data-unicode-name="1F620"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F627" title="anguished" data-aliases="" data-emoji="anguished" data-unicode-name="1F627"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F632" title="astonished" data-aliases="" data-emoji="astonished" data-unicode-name="1F632"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F45F" title="athletic_shoe" data-aliases="" data-emoji="athletic_shoe" data-unicode-name="1F45F"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F476" title="baby" data-aliases="" data-emoji="baby" data-unicode-name="1F476"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F459" title="bikini" data-aliases="" data-emoji="bikini" data-unicode-name="1F459"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F499" title="blue_heart" data-aliases="" data-emoji="blue_heart" data-unicode-name="1F499"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F60A" title="blush" data-aliases="" data-emoji="blush" data-unicode-name="1F60A"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F4A5" title="boom" data-aliases="" data-emoji="boom" data-unicode-name="1F4A5"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F462" title="boot" data-aliases="" data-emoji="boot" data-unicode-name="1F462"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F647" title="bow" data-aliases="" data-emoji="bow" data-unicode-name="1F647"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F466" title="boy" data-aliases="" data-emoji="boy" data-unicode-name="1F466"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F470" title="bride_with_veil" data-aliases="" data-emoji="bride_with_veil" data-unicode-name="1F470"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F4BC" title="briefcase" data-aliases="" data-emoji="briefcase" data-unicode-name="1F4BC"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F494" title="broken_heart" data-aliases="" data-emoji="broken_heart" data-unicode-name="1F494"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F464" title="bust_in_silhouette" data-aliases="" data-emoji="bust_in_silhouette" data-unicode-name="1F464"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F465" title="busts_in_silhouette" data-aliases="" data-emoji="busts_in_silhouette" data-unicode-name="1F465"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F44F" title="clap" data-aliases="" data-emoji="clap" data-unicode-name="1F44F"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F302" title="closed_umbrella" data-aliases="" data-emoji="closed_umbrella" data-unicode-name="1F302"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F630" title="cold_sweat" data-aliases="" data-emoji="cold_sweat" data-unicode-name="1F630"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F616" title="confounded" data-aliases="" data-emoji="confounded" data-unicode-name="1F616"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F615" title="confused" data-aliases="" data-emoji="confused" data-unicode-name="1F615"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F477" title="construction_worker" data-aliases="" data-emoji="construction_worker" data-unicode-name="1F477"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F46E" title="cop" data-aliases="" data-emoji="cop" data-unicode-name="1F46E"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F46B" title="couple" data-aliases="" data-emoji="couple" data-unicode-name="1F46B"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F491" title="couple_with_heart" data-aliases="" data-emoji="couple_with_heart" data-unicode-name="1F491"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F48F" title="couplekiss" data-aliases="" data-emoji="couplekiss" data-unicode-name="1F48F"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F451" title="crown" data-aliases="" data-emoji="crown" data-unicode-name="1F451"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F622" title="cry" data-aliases="" data-emoji="cry" data-unicode-name="1F622"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F63F" title="crying_cat_face" data-aliases="" data-emoji="crying_cat_face" data-unicode-name="1F63F"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F498" title="cupid" data-aliases="" data-emoji="cupid" data-unicode-name="1F498"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F483" title="dancer" data-aliases="" data-emoji="dancer" data-unicode-name="1F483"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F46F" title="dancers" data-aliases="" data-emoji="dancers" data-unicode-name="1F46F"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F4A8" title="dash" data-aliases="" data-emoji="dash" data-unicode-name="1F4A8"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F61E" title="disappointed" data-aliases="" data-emoji="disappointed" data-unicode-name="1F61E"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F625" title="disappointed_relieved" data-aliases="" data-emoji="disappointed_relieved" data-unicode-name="1F625"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F4AB" title="dizzy" data-aliases="" data-emoji="dizzy" data-unicode-name="1F4AB"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F635" title="dizzy_face" data-aliases="" data-emoji="dizzy_face" data-unicode-name="1F635"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F457" title="dress" data-aliases="" data-emoji="dress" data-unicode-name="1F457"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F4A7" title="droplet" data-aliases="" data-emoji="droplet" data-unicode-name="1F4A7"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F442" title="ear" data-aliases="" data-emoji="ear" data-unicode-name="1F442"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F611" title="expressionless" data-aliases="" data-emoji="expressionless" data-unicode-name="1F611"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F453" title="eyeglasses" data-aliases="" data-emoji="eyeglasses" data-unicode-name="1F453"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F440" title="eyes" data-aliases="" data-emoji="eyes" data-unicode-name="1F440"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F46A" title="family" data-aliases="" data-emoji="family" data-unicode-name="1F46A"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F628" title="fearful" data-aliases="" data-emoji="fearful" data-unicode-name="1F628"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F525" title="fire" data-aliases=":flame:" data-emoji="fire" data-unicode-name="1F525"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-270A" title="fist" data-aliases="" data-emoji="fist" data-unicode-name="270A"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F633" title="flushed" data-aliases="" data-emoji="flushed" data-unicode-name="1F633"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F463" title="footprints" data-aliases="" data-emoji="footprints" data-unicode-name="1F463"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F626" title="frowning" data-aliases=":anguished:" data-emoji="frowning" data-unicode-name="1F626"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F48E" title="gem" data-aliases="" data-emoji="gem" data-unicode-name="1F48E"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F467" title="girl" data-aliases="" data-emoji="girl" data-unicode-name="1F467"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F49A" title="green_heart" data-aliases="" data-emoji="green_heart" data-unicode-name="1F49A"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F62C" title="grimacing" data-aliases="" data-emoji="grimacing" data-unicode-name="1F62C"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F601" title="grin" data-aliases="" data-emoji="grin" data-unicode-name="1F601"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F600" title="grinning" data-aliases="" data-emoji="grinning" data-unicode-name="1F600"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F482" title="guardsman" data-aliases="" data-emoji="guardsman" data-unicode-name="1F482"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F487" title="haircut" data-aliases="" data-emoji="haircut" data-unicode-name="1F487"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F45C" title="handbag" data-aliases="" data-emoji="handbag" data-unicode-name="1F45C"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F649" title="hear_no_evil" data-aliases="" data-emoji="hear_no_evil" data-unicode-name="1F649"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-2764" title="heart" data-aliases="" data-emoji="heart" data-unicode-name="2764"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F60D" title="heart_eyes" data-aliases="" data-emoji="heart_eyes" data-unicode-name="1F60D"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F63B" title="heart_eyes_cat" data-aliases="" data-emoji="heart_eyes_cat" data-unicode-name="1F63B"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F493" title="heartbeat" data-aliases="" data-emoji="heartbeat" data-unicode-name="1F493"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F497" title="heartpulse" data-aliases="" data-emoji="heartpulse" data-unicode-name="1F497"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F460" title="high_heel" data-aliases="" data-emoji="high_heel" data-unicode-name="1F460"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F62F" title="hushed" data-aliases="" data-emoji="hushed" data-unicode-name="1F62F"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F47F" title="imp" data-aliases="" data-emoji="imp" data-unicode-name="1F47F"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F481" title="information_desk_person" data-aliases="" data-emoji="information_desk_person" data-unicode-name="1F481"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F607" title="innocent" data-aliases="" data-emoji="innocent" data-unicode-name="1F607"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F47A" title="japanese_goblin" data-aliases="" data-emoji="japanese_goblin" data-unicode-name="1F47A"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F479" title="japanese_ogre" data-aliases="" data-emoji="japanese_ogre" data-unicode-name="1F479"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F456" title="jeans" data-aliases="" data-emoji="jeans" data-unicode-name="1F456"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F602" title="joy" data-aliases="" data-emoji="joy" data-unicode-name="1F602"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F639" title="joy_cat" data-aliases="" data-emoji="joy_cat" data-unicode-name="1F639"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F458" title="kimono" data-aliases="" data-emoji="kimono" data-unicode-name="1F458"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F48B" title="kiss" data-aliases="" data-emoji="kiss" data-unicode-name="1F48B"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F617" title="kissing" data-aliases="" data-emoji="kissing" data-unicode-name="1F617"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F63D" title="kissing_cat" data-aliases="" data-emoji="kissing_cat" data-unicode-name="1F63D"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F61A" title="kissing_closed_eyes" data-aliases="" data-emoji="kissing_closed_eyes" data-unicode-name="1F61A"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F618" title="kissing_heart" data-aliases="" data-emoji="kissing_heart" data-unicode-name="1F618"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F619" title="kissing_smiling_eyes" data-aliases="" data-emoji="kissing_smiling_eyes" data-unicode-name="1F619"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F606" title="laughing" data-aliases=":satisfied:" data-emoji="laughing" data-unicode-name="1F606"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F444" title="lips" data-aliases="" data-emoji="lips" data-unicode-name="1F444"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F484" title="lipstick" data-aliases="" data-emoji="lipstick" data-unicode-name="1F484"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F48C" title="love_letter" data-aliases="" data-emoji="love_letter" data-unicode-name="1F48C"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F468" title="man" data-aliases="" data-emoji="man" data-unicode-name="1F468"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F472" title="man_with_gua_pi_mao" data-aliases="" data-emoji="man_with_gua_pi_mao" data-unicode-name="1F472"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F473" title="man_with_turban" data-aliases="" data-emoji="man_with_turban" data-unicode-name="1F473"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F45E" title="mans_shoe" data-aliases="" data-emoji="mans_shoe" data-unicode-name="1F45E"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F637" title="mask" data-aliases="" data-emoji="mask" data-unicode-name="1F637"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F486" title="massage" data-aliases="" data-emoji="massage" data-unicode-name="1F486"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F4AA" title="muscle" data-aliases="" data-emoji="muscle" data-unicode-name="1F4AA"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F485" title="nail_care" data-aliases="" data-emoji="nail_care" data-unicode-name="1F485"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F454" title="necktie" data-aliases="" data-emoji="necktie" data-unicode-name="1F454"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F610" title="neutral_face" data-aliases="" data-emoji="neutral_face" data-unicode-name="1F610"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F645" title="no_good" data-aliases="" data-emoji="no_good" data-unicode-name="1F645"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F636" title="no_mouth" data-aliases="" data-emoji="no_mouth" data-unicode-name="1F636"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F443" title="nose" data-aliases="" data-emoji="nose" data-unicode-name="1F443"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F44C" title="ok_hand" data-aliases="" data-emoji="ok_hand" data-unicode-name="1F44C"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F646" title="ok_woman" data-aliases="" data-emoji="ok_woman" data-unicode-name="1F646"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F474" title="older_man" data-aliases="" data-emoji="older_man" data-unicode-name="1F474"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F475" title="older_woman" data-aliases=":grandma:" data-emoji="older_woman" data-unicode-name="1F475"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F450" title="open_hands" data-aliases="" data-emoji="open_hands" data-unicode-name="1F450"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F62E" title="open_mouth" data-aliases="" data-emoji="open_mouth" data-unicode-name="1F62E"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F614" title="pensive" data-aliases="" data-emoji="pensive" data-unicode-name="1F614"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F623" title="persevere" data-aliases="" data-emoji="persevere" data-unicode-name="1F623"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F64D" title="person_frowning" data-aliases="" data-emoji="person_frowning" data-unicode-name="1F64D"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F471" title="person_with_blond_hair" data-aliases="" data-emoji="person_with_blond_hair" data-unicode-name="1F471"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F64E" title="person_with_pouting_face" data-aliases="" data-emoji="person_with_pouting_face" data-unicode-name="1F64E"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F447" title="point_down" data-aliases="" data-emoji="point_down" data-unicode-name="1F447"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F448" title="point_left" data-aliases="" data-emoji="point_left" data-unicode-name="1F448"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F449" title="point_right" data-aliases="" data-emoji="point_right" data-unicode-name="1F449"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-261D" title="point_up" data-aliases="" data-emoji="point_up" data-unicode-name="261D"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F446" title="point_up_2" data-aliases="" data-emoji="point_up_2" data-unicode-name="1F446"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F4A9" title="poop" data-aliases=":shit: :hankey: :poo:" data-emoji="poop" data-unicode-name="1F4A9"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F45D" title="pouch" data-aliases="" data-emoji="pouch" data-unicode-name="1F45D"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F63E" title="pouting_cat" data-aliases="" data-emoji="pouting_cat" data-unicode-name="1F63E"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F64F" title="pray" data-aliases="" data-emoji="pray" data-unicode-name="1F64F"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F478" title="princess" data-aliases="" data-emoji="princess" data-unicode-name="1F478"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F44A" title="punch" data-aliases="" data-emoji="punch" data-unicode-name="1F44A"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F49C" title="purple_heart" data-aliases="" data-emoji="purple_heart" data-unicode-name="1F49C"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F45B" title="purse" data-aliases="" data-emoji="purse" data-unicode-name="1F45B"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F621" title="rage" data-aliases="" data-emoji="rage" data-unicode-name="1F621"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-270B" title="raised_hand" data-aliases="" data-emoji="raised_hand" data-unicode-name="270B"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F64C" title="raised_hands" data-aliases="" data-emoji="raised_hands" data-unicode-name="1F64C"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F64B" title="raising_hand" data-aliases="" data-emoji="raising_hand" data-unicode-name="1F64B"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-263A" title="relaxed" data-aliases="" data-emoji="relaxed" data-unicode-name="263A"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F60C" title="relieved" data-aliases="" data-emoji="relieved" data-unicode-name="1F60C"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F49E" title="revolving_hearts" data-aliases="" data-emoji="revolving_hearts" data-unicode-name="1F49E"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F380" title="ribbon" data-aliases="" data-emoji="ribbon" data-unicode-name="1F380"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F48D" title="ring" data-aliases="" data-emoji="ring" data-unicode-name="1F48D"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F3C3" title="runner" data-aliases="" data-emoji="runner" data-unicode-name="1F3C3"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F3BD" title="running_shirt_with_sash" data-aliases="" data-emoji="running_shirt_with_sash" data-unicode-name="1F3BD"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F461" title="sandal" data-aliases="" data-emoji="sandal" data-unicode-name="1F461"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F631" title="scream" data-aliases="" data-emoji="scream" data-unicode-name="1F631"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F640" title="scream_cat" data-aliases="" data-emoji="scream_cat" data-unicode-name="1F640"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F648" title="see_no_evil" data-aliases="" data-emoji="see_no_evil" data-unicode-name="1F648"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F455" title="shirt" data-aliases="" data-emoji="shirt" data-unicode-name="1F455"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F480" title="skull" data-aliases=":skeleton:" data-emoji="skull" data-unicode-name="1F480"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F634" title="sleeping" data-aliases="" data-emoji="sleeping" data-unicode-name="1F634"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F62A" title="sleepy" data-aliases="" data-emoji="sleepy" data-unicode-name="1F62A"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F604" title="smile" data-aliases="" data-emoji="smile" data-unicode-name="1F604"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F638" title="smile_cat" data-aliases="" data-emoji="smile_cat" data-unicode-name="1F638"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F603" title="smiley" data-aliases="" data-emoji="smiley" data-unicode-name="1F603"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F63A" title="smiley_cat" data-aliases="" data-emoji="smiley_cat" data-unicode-name="1F63A"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F608" title="smiling_imp" data-aliases="" data-emoji="smiling_imp" data-unicode-name="1F608"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F60F" title="smirk" data-aliases="" data-emoji="smirk" data-unicode-name="1F60F"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F63C" title="smirk_cat" data-aliases="" data-emoji="smirk_cat" data-unicode-name="1F63C"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F62D" title="sob" data-aliases="" data-emoji="sob" data-unicode-name="1F62D"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-2728" title="sparkles" data-aliases="" data-emoji="sparkles" data-unicode-name="2728"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F496" title="sparkling_heart" data-aliases="" data-emoji="sparkling_heart" data-unicode-name="1F496"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F64A" title="speak_no_evil" data-aliases="" data-emoji="speak_no_evil" data-unicode-name="1F64A"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F4AC" title="speech_balloon" data-aliases="" data-emoji="speech_balloon" data-unicode-name="1F4AC"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F31F" title="star2" data-aliases="" data-emoji="star2" data-unicode-name="1F31F"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F61B" title="stuck_out_tongue" data-aliases="" data-emoji="stuck_out_tongue" data-unicode-name="1F61B"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F61D" title="stuck_out_tongue_closed_eyes" data-aliases="" data-emoji="stuck_out_tongue_closed_eyes" data-unicode-name="1F61D"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F61C" title="stuck_out_tongue_winking_eye" data-aliases="" data-emoji="stuck_out_tongue_winking_eye" data-unicode-name="1F61C"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F60E" title="sunglasses" data-aliases="" data-emoji="sunglasses" data-unicode-name="1F60E"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F613" title="sweat" data-aliases="" data-emoji="sweat" data-unicode-name="1F613"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F4A6" title="sweat_drops" data-aliases="" data-emoji="sweat_drops" data-unicode-name="1F4A6"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F605" title="sweat_smile" data-aliases="" data-emoji="sweat_smile" data-unicode-name="1F605"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F4AD" title="thought_balloon" data-aliases="" data-emoji="thought_balloon" data-unicode-name="1F4AD"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F44E" title="thumbsdown" data-aliases=":-1:" data-emoji="thumbsdown" data-unicode-name="1F44E"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F44D" title="thumbsup" data-aliases=":+1:" data-emoji="thumbsup" data-unicode-name="1F44D"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F62B" title="tired_face" data-aliases="" data-emoji="tired_face" data-unicode-name="1F62B"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F445" title="tongue" data-aliases="" data-emoji="tongue" data-unicode-name="1F445"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F3A9" title="tophat" data-aliases="" data-emoji="tophat" data-unicode-name="1F3A9"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F624" title="triumph" data-aliases="" data-emoji="triumph" data-unicode-name="1F624"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F495" title="two_hearts" data-aliases="" data-emoji="two_hearts" data-unicode-name="1F495"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F46C" title="two_men_holding_hands" data-aliases="" data-emoji="two_men_holding_hands" data-unicode-name="1F46C"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F46D" title="two_women_holding_hands" data-aliases="" data-emoji="two_women_holding_hands" data-unicode-name="1F46D"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F612" title="unamused" data-aliases="" data-emoji="unamused" data-unicode-name="1F612"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-270C" title="v" data-aliases="" data-emoji="v" data-unicode-name="270C"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F6B6" title="walking" data-aliases="" data-emoji="walking" data-unicode-name="1F6B6"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F44B" title="wave" data-aliases="" data-emoji="wave" data-unicode-name="1F44B"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F629" title="weary" data-aliases="" data-emoji="weary" data-unicode-name="1F629"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F609" title="wink" data-aliases="" data-emoji="wink" data-unicode-name="1F609"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F469" title="woman" data-aliases="" data-emoji="woman" data-unicode-name="1F469"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F45A" title="womans_clothes" data-aliases="" data-emoji="womans_clothes" data-unicode-name="1F45A"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F452" title="womans_hat" data-aliases="" data-emoji="womans_hat" data-unicode-name="1F452"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F61F" title="worried" data-aliases="" data-emoji="worried" data-unicode-name="1F61F"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F49B" title="yellow_heart" data-aliases="" data-emoji="yellow_heart" data-unicode-name="1F49B"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F60B" title="yum" data-aliases="" data-emoji="yum" data-unicode-name="1F60B"></div> - </button> - </li> - <li class='pull-left text-center emoji-menu-list-item'> - <button class='emoji-menu-btn text-center js-emoji-btn' type='button'> - <div class="icon emoji-icon emoji-1F4A4" title="zzz" data-aliases="" data-emoji="zzz" data-unicode-name="1F4A4"></div> - </button> - </li> - </ul> - </div> - </div> -""" diff --git a/spec/javascripts/fixtures/emoji_menu.js b/spec/javascripts/fixtures/emoji_menu.js new file mode 100644 index 00000000000..99e3f7247bd --- /dev/null +++ b/spec/javascripts/fixtures/emoji_menu.js @@ -0,0 +1,4 @@ +(function() { + window.emojiMenu = "<div class='emoji-menu'>\n <input type=\"text\" name=\"emoji_search\" id=\"emoji_search\" value=\"\" class=\"emoji-search search-input form-control\" />\n <div class='emoji-menu-content'>\n <h5 class='emoji-menu-title'>\n Emoticons\n </h5>\n <ul class='clearfix emoji-menu-list'>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F47D\" title=\"alien\" data-aliases=\"\" data-emoji=\"alien\" data-unicode-name=\"1F47D\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F47C\" title=\"angel\" data-aliases=\"\" data-emoji=\"angel\" data-unicode-name=\"1F47C\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F4A2\" title=\"anger\" data-aliases=\"\" data-emoji=\"anger\" data-unicode-name=\"1F4A2\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F620\" title=\"angry\" data-aliases=\"\" data-emoji=\"angry\" data-unicode-name=\"1F620\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F627\" title=\"anguished\" data-aliases=\"\" data-emoji=\"anguished\" data-unicode-name=\"1F627\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F632\" title=\"astonished\" data-aliases=\"\" data-emoji=\"astonished\" data-unicode-name=\"1F632\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F45F\" title=\"athletic_shoe\" data-aliases=\"\" data-emoji=\"athletic_shoe\" data-unicode-name=\"1F45F\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F476\" title=\"baby\" data-aliases=\"\" data-emoji=\"baby\" data-unicode-name=\"1F476\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F459\" title=\"bikini\" data-aliases=\"\" data-emoji=\"bikini\" data-unicode-name=\"1F459\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F499\" title=\"blue_heart\" data-aliases=\"\" data-emoji=\"blue_heart\" data-unicode-name=\"1F499\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F60A\" title=\"blush\" data-aliases=\"\" data-emoji=\"blush\" data-unicode-name=\"1F60A\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F4A5\" title=\"boom\" data-aliases=\"\" data-emoji=\"boom\" data-unicode-name=\"1F4A5\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F462\" title=\"boot\" data-aliases=\"\" data-emoji=\"boot\" data-unicode-name=\"1F462\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F647\" title=\"bow\" data-aliases=\"\" data-emoji=\"bow\" data-unicode-name=\"1F647\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F466\" title=\"boy\" data-aliases=\"\" data-emoji=\"boy\" data-unicode-name=\"1F466\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F470\" title=\"bride_with_veil\" data-aliases=\"\" data-emoji=\"bride_with_veil\" data-unicode-name=\"1F470\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F4BC\" title=\"briefcase\" data-aliases=\"\" data-emoji=\"briefcase\" data-unicode-name=\"1F4BC\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F494\" title=\"broken_heart\" data-aliases=\"\" data-emoji=\"broken_heart\" data-unicode-name=\"1F494\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F464\" title=\"bust_in_silhouette\" data-aliases=\"\" data-emoji=\"bust_in_silhouette\" data-unicode-name=\"1F464\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F465\" title=\"busts_in_silhouette\" data-aliases=\"\" data-emoji=\"busts_in_silhouette\" data-unicode-name=\"1F465\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F44F\" title=\"clap\" data-aliases=\"\" data-emoji=\"clap\" data-unicode-name=\"1F44F\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F302\" title=\"closed_umbrella\" data-aliases=\"\" data-emoji=\"closed_umbrella\" data-unicode-name=\"1F302\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F630\" title=\"cold_sweat\" data-aliases=\"\" data-emoji=\"cold_sweat\" data-unicode-name=\"1F630\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F616\" title=\"confounded\" data-aliases=\"\" data-emoji=\"confounded\" data-unicode-name=\"1F616\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F615\" title=\"confused\" data-aliases=\"\" data-emoji=\"confused\" data-unicode-name=\"1F615\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F477\" title=\"construction_worker\" data-aliases=\"\" data-emoji=\"construction_worker\" data-unicode-name=\"1F477\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F46E\" title=\"cop\" data-aliases=\"\" data-emoji=\"cop\" data-unicode-name=\"1F46E\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F46B\" title=\"couple\" data-aliases=\"\" data-emoji=\"couple\" data-unicode-name=\"1F46B\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F491\" title=\"couple_with_heart\" data-aliases=\"\" data-emoji=\"couple_with_heart\" data-unicode-name=\"1F491\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F48F\" title=\"couplekiss\" data-aliases=\"\" data-emoji=\"couplekiss\" data-unicode-name=\"1F48F\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F451\" title=\"crown\" data-aliases=\"\" data-emoji=\"crown\" data-unicode-name=\"1F451\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F622\" title=\"cry\" data-aliases=\"\" data-emoji=\"cry\" data-unicode-name=\"1F622\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F63F\" title=\"crying_cat_face\" data-aliases=\"\" data-emoji=\"crying_cat_face\" data-unicode-name=\"1F63F\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F498\" title=\"cupid\" data-aliases=\"\" data-emoji=\"cupid\" data-unicode-name=\"1F498\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F483\" title=\"dancer\" data-aliases=\"\" data-emoji=\"dancer\" data-unicode-name=\"1F483\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F46F\" title=\"dancers\" data-aliases=\"\" data-emoji=\"dancers\" data-unicode-name=\"1F46F\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F4A8\" title=\"dash\" data-aliases=\"\" data-emoji=\"dash\" data-unicode-name=\"1F4A8\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F61E\" title=\"disappointed\" data-aliases=\"\" data-emoji=\"disappointed\" data-unicode-name=\"1F61E\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F625\" title=\"disappointed_relieved\" data-aliases=\"\" data-emoji=\"disappointed_relieved\" data-unicode-name=\"1F625\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F4AB\" title=\"dizzy\" data-aliases=\"\" data-emoji=\"dizzy\" data-unicode-name=\"1F4AB\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F635\" title=\"dizzy_face\" data-aliases=\"\" data-emoji=\"dizzy_face\" data-unicode-name=\"1F635\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F457\" title=\"dress\" data-aliases=\"\" data-emoji=\"dress\" data-unicode-name=\"1F457\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F4A7\" title=\"droplet\" data-aliases=\"\" data-emoji=\"droplet\" data-unicode-name=\"1F4A7\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F442\" title=\"ear\" data-aliases=\"\" data-emoji=\"ear\" data-unicode-name=\"1F442\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F611\" title=\"expressionless\" data-aliases=\"\" data-emoji=\"expressionless\" data-unicode-name=\"1F611\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F453\" title=\"eyeglasses\" data-aliases=\"\" data-emoji=\"eyeglasses\" data-unicode-name=\"1F453\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F440\" title=\"eyes\" data-aliases=\"\" data-emoji=\"eyes\" data-unicode-name=\"1F440\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F46A\" title=\"family\" data-aliases=\"\" data-emoji=\"family\" data-unicode-name=\"1F46A\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F628\" title=\"fearful\" data-aliases=\"\" data-emoji=\"fearful\" data-unicode-name=\"1F628\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F525\" title=\"fire\" data-aliases=\":flame:\" data-emoji=\"fire\" data-unicode-name=\"1F525\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-270A\" title=\"fist\" data-aliases=\"\" data-emoji=\"fist\" data-unicode-name=\"270A\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F633\" title=\"flushed\" data-aliases=\"\" data-emoji=\"flushed\" data-unicode-name=\"1F633\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F463\" title=\"footprints\" data-aliases=\"\" data-emoji=\"footprints\" data-unicode-name=\"1F463\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F626\" title=\"frowning\" data-aliases=\":anguished:\" data-emoji=\"frowning\" data-unicode-name=\"1F626\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F48E\" title=\"gem\" data-aliases=\"\" data-emoji=\"gem\" data-unicode-name=\"1F48E\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F467\" title=\"girl\" data-aliases=\"\" data-emoji=\"girl\" data-unicode-name=\"1F467\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F49A\" title=\"green_heart\" data-aliases=\"\" data-emoji=\"green_heart\" data-unicode-name=\"1F49A\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F62C\" title=\"grimacing\" data-aliases=\"\" data-emoji=\"grimacing\" data-unicode-name=\"1F62C\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F601\" title=\"grin\" data-aliases=\"\" data-emoji=\"grin\" data-unicode-name=\"1F601\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F600\" title=\"grinning\" data-aliases=\"\" data-emoji=\"grinning\" data-unicode-name=\"1F600\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F482\" title=\"guardsman\" data-aliases=\"\" data-emoji=\"guardsman\" data-unicode-name=\"1F482\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F487\" title=\"haircut\" data-aliases=\"\" data-emoji=\"haircut\" data-unicode-name=\"1F487\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F45C\" title=\"handbag\" data-aliases=\"\" data-emoji=\"handbag\" data-unicode-name=\"1F45C\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F649\" title=\"hear_no_evil\" data-aliases=\"\" data-emoji=\"hear_no_evil\" data-unicode-name=\"1F649\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-2764\" title=\"heart\" data-aliases=\"\" data-emoji=\"heart\" data-unicode-name=\"2764\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F60D\" title=\"heart_eyes\" data-aliases=\"\" data-emoji=\"heart_eyes\" data-unicode-name=\"1F60D\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F63B\" title=\"heart_eyes_cat\" data-aliases=\"\" data-emoji=\"heart_eyes_cat\" data-unicode-name=\"1F63B\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F493\" title=\"heartbeat\" data-aliases=\"\" data-emoji=\"heartbeat\" data-unicode-name=\"1F493\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F497\" title=\"heartpulse\" data-aliases=\"\" data-emoji=\"heartpulse\" data-unicode-name=\"1F497\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F460\" title=\"high_heel\" data-aliases=\"\" data-emoji=\"high_heel\" data-unicode-name=\"1F460\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F62F\" title=\"hushed\" data-aliases=\"\" data-emoji=\"hushed\" data-unicode-name=\"1F62F\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F47F\" title=\"imp\" data-aliases=\"\" data-emoji=\"imp\" data-unicode-name=\"1F47F\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F481\" title=\"information_desk_person\" data-aliases=\"\" data-emoji=\"information_desk_person\" data-unicode-name=\"1F481\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F607\" title=\"innocent\" data-aliases=\"\" data-emoji=\"innocent\" data-unicode-name=\"1F607\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F47A\" title=\"japanese_goblin\" data-aliases=\"\" data-emoji=\"japanese_goblin\" data-unicode-name=\"1F47A\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F479\" title=\"japanese_ogre\" data-aliases=\"\" data-emoji=\"japanese_ogre\" data-unicode-name=\"1F479\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F456\" title=\"jeans\" data-aliases=\"\" data-emoji=\"jeans\" data-unicode-name=\"1F456\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F602\" title=\"joy\" data-aliases=\"\" data-emoji=\"joy\" data-unicode-name=\"1F602\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F639\" title=\"joy_cat\" data-aliases=\"\" data-emoji=\"joy_cat\" data-unicode-name=\"1F639\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F458\" title=\"kimono\" data-aliases=\"\" data-emoji=\"kimono\" data-unicode-name=\"1F458\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F48B\" title=\"kiss\" data-aliases=\"\" data-emoji=\"kiss\" data-unicode-name=\"1F48B\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F617\" title=\"kissing\" data-aliases=\"\" data-emoji=\"kissing\" data-unicode-name=\"1F617\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F63D\" title=\"kissing_cat\" data-aliases=\"\" data-emoji=\"kissing_cat\" data-unicode-name=\"1F63D\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F61A\" title=\"kissing_closed_eyes\" data-aliases=\"\" data-emoji=\"kissing_closed_eyes\" data-unicode-name=\"1F61A\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F618\" title=\"kissing_heart\" data-aliases=\"\" data-emoji=\"kissing_heart\" data-unicode-name=\"1F618\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F619\" title=\"kissing_smiling_eyes\" data-aliases=\"\" data-emoji=\"kissing_smiling_eyes\" data-unicode-name=\"1F619\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F606\" title=\"laughing\" data-aliases=\":satisfied:\" data-emoji=\"laughing\" data-unicode-name=\"1F606\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F444\" title=\"lips\" data-aliases=\"\" data-emoji=\"lips\" data-unicode-name=\"1F444\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F484\" title=\"lipstick\" data-aliases=\"\" data-emoji=\"lipstick\" data-unicode-name=\"1F484\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F48C\" title=\"love_letter\" data-aliases=\"\" data-emoji=\"love_letter\" data-unicode-name=\"1F48C\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F468\" title=\"man\" data-aliases=\"\" data-emoji=\"man\" data-unicode-name=\"1F468\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F472\" title=\"man_with_gua_pi_mao\" data-aliases=\"\" data-emoji=\"man_with_gua_pi_mao\" data-unicode-name=\"1F472\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F473\" title=\"man_with_turban\" data-aliases=\"\" data-emoji=\"man_with_turban\" data-unicode-name=\"1F473\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F45E\" title=\"mans_shoe\" data-aliases=\"\" data-emoji=\"mans_shoe\" data-unicode-name=\"1F45E\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F637\" title=\"mask\" data-aliases=\"\" data-emoji=\"mask\" data-unicode-name=\"1F637\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F486\" title=\"massage\" data-aliases=\"\" data-emoji=\"massage\" data-unicode-name=\"1F486\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F4AA\" title=\"muscle\" data-aliases=\"\" data-emoji=\"muscle\" data-unicode-name=\"1F4AA\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F485\" title=\"nail_care\" data-aliases=\"\" data-emoji=\"nail_care\" data-unicode-name=\"1F485\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F454\" title=\"necktie\" data-aliases=\"\" data-emoji=\"necktie\" data-unicode-name=\"1F454\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F610\" title=\"neutral_face\" data-aliases=\"\" data-emoji=\"neutral_face\" data-unicode-name=\"1F610\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F645\" title=\"no_good\" data-aliases=\"\" data-emoji=\"no_good\" data-unicode-name=\"1F645\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F636\" title=\"no_mouth\" data-aliases=\"\" data-emoji=\"no_mouth\" data-unicode-name=\"1F636\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F443\" title=\"nose\" data-aliases=\"\" data-emoji=\"nose\" data-unicode-name=\"1F443\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F44C\" title=\"ok_hand\" data-aliases=\"\" data-emoji=\"ok_hand\" data-unicode-name=\"1F44C\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F646\" title=\"ok_woman\" data-aliases=\"\" data-emoji=\"ok_woman\" data-unicode-name=\"1F646\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F474\" title=\"older_man\" data-aliases=\"\" data-emoji=\"older_man\" data-unicode-name=\"1F474\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F475\" title=\"older_woman\" data-aliases=\":grandma:\" data-emoji=\"older_woman\" data-unicode-name=\"1F475\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F450\" title=\"open_hands\" data-aliases=\"\" data-emoji=\"open_hands\" data-unicode-name=\"1F450\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F62E\" title=\"open_mouth\" data-aliases=\"\" data-emoji=\"open_mouth\" data-unicode-name=\"1F62E\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F614\" title=\"pensive\" data-aliases=\"\" data-emoji=\"pensive\" data-unicode-name=\"1F614\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F623\" title=\"persevere\" data-aliases=\"\" data-emoji=\"persevere\" data-unicode-name=\"1F623\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F64D\" title=\"person_frowning\" data-aliases=\"\" data-emoji=\"person_frowning\" data-unicode-name=\"1F64D\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F471\" title=\"person_with_blond_hair\" data-aliases=\"\" data-emoji=\"person_with_blond_hair\" data-unicode-name=\"1F471\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F64E\" title=\"person_with_pouting_face\" data-aliases=\"\" data-emoji=\"person_with_pouting_face\" data-unicode-name=\"1F64E\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F447\" title=\"point_down\" data-aliases=\"\" data-emoji=\"point_down\" data-unicode-name=\"1F447\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F448\" title=\"point_left\" data-aliases=\"\" data-emoji=\"point_left\" data-unicode-name=\"1F448\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F449\" title=\"point_right\" data-aliases=\"\" data-emoji=\"point_right\" data-unicode-name=\"1F449\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-261D\" title=\"point_up\" data-aliases=\"\" data-emoji=\"point_up\" data-unicode-name=\"261D\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F446\" title=\"point_up_2\" data-aliases=\"\" data-emoji=\"point_up_2\" data-unicode-name=\"1F446\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F4A9\" title=\"poop\" data-aliases=\":shit: :hankey: :poo:\" data-emoji=\"poop\" data-unicode-name=\"1F4A9\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F45D\" title=\"pouch\" data-aliases=\"\" data-emoji=\"pouch\" data-unicode-name=\"1F45D\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F63E\" title=\"pouting_cat\" data-aliases=\"\" data-emoji=\"pouting_cat\" data-unicode-name=\"1F63E\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F64F\" title=\"pray\" data-aliases=\"\" data-emoji=\"pray\" data-unicode-name=\"1F64F\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F478\" title=\"princess\" data-aliases=\"\" data-emoji=\"princess\" data-unicode-name=\"1F478\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F44A\" title=\"punch\" data-aliases=\"\" data-emoji=\"punch\" data-unicode-name=\"1F44A\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F49C\" title=\"purple_heart\" data-aliases=\"\" data-emoji=\"purple_heart\" data-unicode-name=\"1F49C\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F45B\" title=\"purse\" data-aliases=\"\" data-emoji=\"purse\" data-unicode-name=\"1F45B\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F621\" title=\"rage\" data-aliases=\"\" data-emoji=\"rage\" data-unicode-name=\"1F621\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-270B\" title=\"raised_hand\" data-aliases=\"\" data-emoji=\"raised_hand\" data-unicode-name=\"270B\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F64C\" title=\"raised_hands\" data-aliases=\"\" data-emoji=\"raised_hands\" data-unicode-name=\"1F64C\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F64B\" title=\"raising_hand\" data-aliases=\"\" data-emoji=\"raising_hand\" data-unicode-name=\"1F64B\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-263A\" title=\"relaxed\" data-aliases=\"\" data-emoji=\"relaxed\" data-unicode-name=\"263A\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F60C\" title=\"relieved\" data-aliases=\"\" data-emoji=\"relieved\" data-unicode-name=\"1F60C\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F49E\" title=\"revolving_hearts\" data-aliases=\"\" data-emoji=\"revolving_hearts\" data-unicode-name=\"1F49E\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F380\" title=\"ribbon\" data-aliases=\"\" data-emoji=\"ribbon\" data-unicode-name=\"1F380\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F48D\" title=\"ring\" data-aliases=\"\" data-emoji=\"ring\" data-unicode-name=\"1F48D\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F3C3\" title=\"runner\" data-aliases=\"\" data-emoji=\"runner\" data-unicode-name=\"1F3C3\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F3BD\" title=\"running_shirt_with_sash\" data-aliases=\"\" data-emoji=\"running_shirt_with_sash\" data-unicode-name=\"1F3BD\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F461\" title=\"sandal\" data-aliases=\"\" data-emoji=\"sandal\" data-unicode-name=\"1F461\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F631\" title=\"scream\" data-aliases=\"\" data-emoji=\"scream\" data-unicode-name=\"1F631\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F640\" title=\"scream_cat\" data-aliases=\"\" data-emoji=\"scream_cat\" data-unicode-name=\"1F640\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F648\" title=\"see_no_evil\" data-aliases=\"\" data-emoji=\"see_no_evil\" data-unicode-name=\"1F648\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F455\" title=\"shirt\" data-aliases=\"\" data-emoji=\"shirt\" data-unicode-name=\"1F455\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F480\" title=\"skull\" data-aliases=\":skeleton:\" data-emoji=\"skull\" data-unicode-name=\"1F480\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F634\" title=\"sleeping\" data-aliases=\"\" data-emoji=\"sleeping\" data-unicode-name=\"1F634\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F62A\" title=\"sleepy\" data-aliases=\"\" data-emoji=\"sleepy\" data-unicode-name=\"1F62A\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F604\" title=\"smile\" data-aliases=\"\" data-emoji=\"smile\" data-unicode-name=\"1F604\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F638\" title=\"smile_cat\" data-aliases=\"\" data-emoji=\"smile_cat\" data-unicode-name=\"1F638\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F603\" title=\"smiley\" data-aliases=\"\" data-emoji=\"smiley\" data-unicode-name=\"1F603\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F63A\" title=\"smiley_cat\" data-aliases=\"\" data-emoji=\"smiley_cat\" data-unicode-name=\"1F63A\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F608\" title=\"smiling_imp\" data-aliases=\"\" data-emoji=\"smiling_imp\" data-unicode-name=\"1F608\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F60F\" title=\"smirk\" data-aliases=\"\" data-emoji=\"smirk\" data-unicode-name=\"1F60F\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F63C\" title=\"smirk_cat\" data-aliases=\"\" data-emoji=\"smirk_cat\" data-unicode-name=\"1F63C\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F62D\" title=\"sob\" data-aliases=\"\" data-emoji=\"sob\" data-unicode-name=\"1F62D\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-2728\" title=\"sparkles\" data-aliases=\"\" data-emoji=\"sparkles\" data-unicode-name=\"2728\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F496\" title=\"sparkling_heart\" data-aliases=\"\" data-emoji=\"sparkling_heart\" data-unicode-name=\"1F496\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F64A\" title=\"speak_no_evil\" data-aliases=\"\" data-emoji=\"speak_no_evil\" data-unicode-name=\"1F64A\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F4AC\" title=\"speech_balloon\" data-aliases=\"\" data-emoji=\"speech_balloon\" data-unicode-name=\"1F4AC\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F31F\" title=\"star2\" data-aliases=\"\" data-emoji=\"star2\" data-unicode-name=\"1F31F\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F61B\" title=\"stuck_out_tongue\" data-aliases=\"\" data-emoji=\"stuck_out_tongue\" data-unicode-name=\"1F61B\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F61D\" title=\"stuck_out_tongue_closed_eyes\" data-aliases=\"\" data-emoji=\"stuck_out_tongue_closed_eyes\" data-unicode-name=\"1F61D\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F61C\" title=\"stuck_out_tongue_winking_eye\" data-aliases=\"\" data-emoji=\"stuck_out_tongue_winking_eye\" data-unicode-name=\"1F61C\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F60E\" title=\"sunglasses\" data-aliases=\"\" data-emoji=\"sunglasses\" data-unicode-name=\"1F60E\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F613\" title=\"sweat\" data-aliases=\"\" data-emoji=\"sweat\" data-unicode-name=\"1F613\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F4A6\" title=\"sweat_drops\" data-aliases=\"\" data-emoji=\"sweat_drops\" data-unicode-name=\"1F4A6\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F605\" title=\"sweat_smile\" data-aliases=\"\" data-emoji=\"sweat_smile\" data-unicode-name=\"1F605\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F4AD\" title=\"thought_balloon\" data-aliases=\"\" data-emoji=\"thought_balloon\" data-unicode-name=\"1F4AD\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F44E\" title=\"thumbsdown\" data-aliases=\":-1:\" data-emoji=\"thumbsdown\" data-unicode-name=\"1F44E\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F44D\" title=\"thumbsup\" data-aliases=\":+1:\" data-emoji=\"thumbsup\" data-unicode-name=\"1F44D\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F62B\" title=\"tired_face\" data-aliases=\"\" data-emoji=\"tired_face\" data-unicode-name=\"1F62B\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F445\" title=\"tongue\" data-aliases=\"\" data-emoji=\"tongue\" data-unicode-name=\"1F445\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F3A9\" title=\"tophat\" data-aliases=\"\" data-emoji=\"tophat\" data-unicode-name=\"1F3A9\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F624\" title=\"triumph\" data-aliases=\"\" data-emoji=\"triumph\" data-unicode-name=\"1F624\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F495\" title=\"two_hearts\" data-aliases=\"\" data-emoji=\"two_hearts\" data-unicode-name=\"1F495\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F46C\" title=\"two_men_holding_hands\" data-aliases=\"\" data-emoji=\"two_men_holding_hands\" data-unicode-name=\"1F46C\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F46D\" title=\"two_women_holding_hands\" data-aliases=\"\" data-emoji=\"two_women_holding_hands\" data-unicode-name=\"1F46D\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F612\" title=\"unamused\" data-aliases=\"\" data-emoji=\"unamused\" data-unicode-name=\"1F612\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-270C\" title=\"v\" data-aliases=\"\" data-emoji=\"v\" data-unicode-name=\"270C\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F6B6\" title=\"walking\" data-aliases=\"\" data-emoji=\"walking\" data-unicode-name=\"1F6B6\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F44B\" title=\"wave\" data-aliases=\"\" data-emoji=\"wave\" data-unicode-name=\"1F44B\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F629\" title=\"weary\" data-aliases=\"\" data-emoji=\"weary\" data-unicode-name=\"1F629\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F609\" title=\"wink\" data-aliases=\"\" data-emoji=\"wink\" data-unicode-name=\"1F609\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F469\" title=\"woman\" data-aliases=\"\" data-emoji=\"woman\" data-unicode-name=\"1F469\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F45A\" title=\"womans_clothes\" data-aliases=\"\" data-emoji=\"womans_clothes\" data-unicode-name=\"1F45A\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F452\" title=\"womans_hat\" data-aliases=\"\" data-emoji=\"womans_hat\" data-unicode-name=\"1F452\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F61F\" title=\"worried\" data-aliases=\"\" data-emoji=\"worried\" data-unicode-name=\"1F61F\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F49B\" title=\"yellow_heart\" data-aliases=\"\" data-emoji=\"yellow_heart\" data-unicode-name=\"1F49B\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F60B\" title=\"yum\" data-aliases=\"\" data-emoji=\"yum\" data-unicode-name=\"1F60B\"></div>\n </button>\n </li>\n <li class='pull-left text-center emoji-menu-list-item'>\n <button class='emoji-menu-btn text-center js-emoji-btn' type='button'>\n <div class=\"icon emoji-icon emoji-1F4A4\" title=\"zzz\" data-aliases=\"\" data-emoji=\"zzz\" data-unicode-name=\"1F4A4\"></div>\n </button>\n </li>\n </ul>\n </div>\n</div>"; + +}).call(this); diff --git a/spec/javascripts/issue_spec.js b/spec/javascripts/issue_spec.js new file mode 100644 index 00000000000..dc6231ebb38 --- /dev/null +++ b/spec/javascripts/issue_spec.js @@ -0,0 +1,121 @@ + +/*= require lib/utils/text_utility */ + + +/*= require issue */ + +(function() { + describe('Issue', function() { + return describe('task lists', function() { + fixture.preload('issues_show.html'); + beforeEach(function() { + fixture.load('issues_show.html'); + return this.issue = new Issue(); + }); + it('modifies the Markdown field', function() { + spyOn(jQuery, 'ajax').and.stub(); + $('input[type=checkbox]').attr('checked', true).trigger('change'); + return expect($('.js-task-list-field').val()).toBe('- [x] Task List Item'); + }); + return it('submits an ajax request on tasklist:changed', function() { + spyOn(jQuery, 'ajax').and.callFake(function(req) { + expect(req.type).toBe('PATCH'); + expect(req.url).toBe('/foo'); + return expect(req.data.issue.description).not.toBe(null); + }); + return $('.js-task-list-field').trigger('tasklist:changed'); + }); + }); + }); + + describe('reopen/close issue', function() { + fixture.preload('issues_show.html'); + beforeEach(function() { + fixture.load('issues_show.html'); + return this.issue = new Issue(); + }); + it('closes an issue', function() { + var $btnClose, $btnReopen; + spyOn(jQuery, 'ajax').and.callFake(function(req) { + expect(req.type).toBe('PUT'); + expect(req.url).toBe('http://gitlab.com/issues/6/close'); + return req.success({ + id: 34 + }); + }); + $btnClose = $('a.btn-close'); + $btnReopen = $('a.btn-reopen'); + expect($btnReopen).toBeHidden(); + expect($btnClose.text()).toBe('Close'); + expect(typeof $btnClose.prop('disabled')).toBe('undefined'); + $btnClose.trigger('click'); + expect($btnReopen).toBeVisible(); + expect($btnClose).toBeHidden(); + expect($('div.status-box-closed')).toBeVisible(); + return expect($('div.status-box-open')).toBeHidden(); + }); + it('fails to close an issue with success:false', function() { + var $btnClose, $btnReopen; + spyOn(jQuery, 'ajax').and.callFake(function(req) { + expect(req.type).toBe('PUT'); + expect(req.url).toBe('http://goesnowhere.nothing/whereami'); + return req.success({ + saved: false + }); + }); + $btnClose = $('a.btn-close'); + $btnReopen = $('a.btn-reopen'); + $btnClose.attr('href', 'http://goesnowhere.nothing/whereami'); + expect($btnReopen).toBeHidden(); + expect($btnClose.text()).toBe('Close'); + expect(typeof $btnClose.prop('disabled')).toBe('undefined'); + $btnClose.trigger('click'); + expect($btnReopen).toBeHidden(); + expect($btnClose).toBeVisible(); + expect($('div.status-box-closed')).toBeHidden(); + expect($('div.status-box-open')).toBeVisible(); + expect($('div.flash-alert')).toBeVisible(); + return expect($('div.flash-alert').text()).toBe('Unable to update this issue at this time.'); + }); + it('fails to closes an issue with HTTP error', function() { + var $btnClose, $btnReopen; + spyOn(jQuery, 'ajax').and.callFake(function(req) { + expect(req.type).toBe('PUT'); + expect(req.url).toBe('http://goesnowhere.nothing/whereami'); + return req.error(); + }); + $btnClose = $('a.btn-close'); + $btnReopen = $('a.btn-reopen'); + $btnClose.attr('href', 'http://goesnowhere.nothing/whereami'); + expect($btnReopen).toBeHidden(); + expect($btnClose.text()).toBe('Close'); + expect(typeof $btnClose.prop('disabled')).toBe('undefined'); + $btnClose.trigger('click'); + expect($btnReopen).toBeHidden(); + expect($btnClose).toBeVisible(); + expect($('div.status-box-closed')).toBeHidden(); + expect($('div.status-box-open')).toBeVisible(); + expect($('div.flash-alert')).toBeVisible(); + return expect($('div.flash-alert').text()).toBe('Unable to update this issue at this time.'); + }); + return it('reopens an issue', function() { + var $btnClose, $btnReopen; + spyOn(jQuery, 'ajax').and.callFake(function(req) { + expect(req.type).toBe('PUT'); + expect(req.url).toBe('http://gitlab.com/issues/6/reopen'); + return req.success({ + id: 34 + }); + }); + $btnClose = $('a.btn-close'); + $btnReopen = $('a.btn-reopen'); + expect($btnReopen.text()).toBe('Reopen'); + $btnReopen.trigger('click'); + expect($btnReopen).toBeHidden(); + expect($btnClose).toBeVisible(); + expect($('div.status-box-open')).toBeVisible(); + return expect($('div.status-box-closed')).toBeHidden(); + }); + }); + +}).call(this); diff --git a/spec/javascripts/issue_spec.js.coffee b/spec/javascripts/issue_spec.js.coffee deleted file mode 100644 index d84d80f266b..00000000000 --- a/spec/javascripts/issue_spec.js.coffee +++ /dev/null @@ -1,109 +0,0 @@ -#= require lib/utils/text_utility -#= require issue - -describe 'Issue', -> - describe 'task lists', -> - fixture.preload('issues_show.html') - - beforeEach -> - fixture.load('issues_show.html') - @issue = new Issue() - - it 'modifies the Markdown field', -> - spyOn(jQuery, 'ajax').and.stub() - $('input[type=checkbox]').attr('checked', true).trigger('change') - expect($('.js-task-list-field').val()).toBe('- [x] Task List Item') - - it 'submits an ajax request on tasklist:changed', -> - spyOn(jQuery, 'ajax').and.callFake (req) -> - expect(req.type).toBe('PATCH') - expect(req.url).toBe('/foo') - expect(req.data.issue.description).not.toBe(null) - - $('.js-task-list-field').trigger('tasklist:changed') -describe 'reopen/close issue', -> - fixture.preload('issues_show.html') - beforeEach -> - fixture.load('issues_show.html') - @issue = new Issue() - it 'closes an issue', -> - spyOn(jQuery, 'ajax').and.callFake (req) -> - expect(req.type).toBe('PUT') - expect(req.url).toBe('http://gitlab.com/issues/6/close') - req.success id: 34 - - $btnClose = $('a.btn-close') - $btnReopen = $('a.btn-reopen') - expect($btnReopen).toBeHidden() - expect($btnClose.text()).toBe('Close') - expect(typeof $btnClose.prop('disabled')).toBe('undefined') - - $btnClose.trigger('click') - - expect($btnReopen).toBeVisible() - expect($btnClose).toBeHidden() - expect($('div.status-box-closed')).toBeVisible() - expect($('div.status-box-open')).toBeHidden() - - it 'fails to close an issue with success:false', -> - - spyOn(jQuery, 'ajax').and.callFake (req) -> - expect(req.type).toBe('PUT') - expect(req.url).toBe('http://goesnowhere.nothing/whereami') - req.success saved: false - - $btnClose = $('a.btn-close') - $btnReopen = $('a.btn-reopen') - $btnClose.attr('href','http://goesnowhere.nothing/whereami') - expect($btnReopen).toBeHidden() - expect($btnClose.text()).toBe('Close') - expect(typeof $btnClose.prop('disabled')).toBe('undefined') - - $btnClose.trigger('click') - - expect($btnReopen).toBeHidden() - expect($btnClose).toBeVisible() - expect($('div.status-box-closed')).toBeHidden() - expect($('div.status-box-open')).toBeVisible() - expect($('div.flash-alert')).toBeVisible() - expect($('div.flash-alert').text()).toBe('Unable to update this issue at this time.') - - it 'fails to closes an issue with HTTP error', -> - - spyOn(jQuery, 'ajax').and.callFake (req) -> - expect(req.type).toBe('PUT') - expect(req.url).toBe('http://goesnowhere.nothing/whereami') - req.error() - - $btnClose = $('a.btn-close') - $btnReopen = $('a.btn-reopen') - $btnClose.attr('href','http://goesnowhere.nothing/whereami') - expect($btnReopen).toBeHidden() - expect($btnClose.text()).toBe('Close') - expect(typeof $btnClose.prop('disabled')).toBe('undefined') - - $btnClose.trigger('click') - - expect($btnReopen).toBeHidden() - expect($btnClose).toBeVisible() - expect($('div.status-box-closed')).toBeHidden() - expect($('div.status-box-open')).toBeVisible() - expect($('div.flash-alert')).toBeVisible() - expect($('div.flash-alert').text()).toBe('Unable to update this issue at this time.') - - it 'reopens an issue', -> - spyOn(jQuery, 'ajax').and.callFake (req) -> - expect(req.type).toBe('PUT') - expect(req.url).toBe('http://gitlab.com/issues/6/reopen') - req.success id: 34 - - $btnClose = $('a.btn-close') - $btnReopen = $('a.btn-reopen') - expect($btnReopen.text()).toBe('Reopen') - - $btnReopen.trigger('click') - - expect($btnReopen).toBeHidden() - expect($btnClose).toBeVisible() - expect($('div.status-box-open')).toBeVisible() - expect($('div.status-box-closed')).toBeHidden() diff --git a/spec/javascripts/line_highlighter_spec.js b/spec/javascripts/line_highlighter_spec.js new file mode 100644 index 00000000000..e2789571607 --- /dev/null +++ b/spec/javascripts/line_highlighter_spec.js @@ -0,0 +1,229 @@ + +/*= require line_highlighter */ + +(function() { + describe('LineHighlighter', function() { + var clickLine; + fixture.preload('line_highlighter.html'); + clickLine = function(number, eventData) { + var e; + if (eventData == null) { + eventData = {}; + } + if ($.isEmptyObject(eventData)) { + return $("#L" + number).mousedown().click(); + } else { + e = $.Event('mousedown', eventData); + return $("#L" + number).trigger(e).click(); + } + }; + beforeEach(function() { + fixture.load('line_highlighter.html'); + this["class"] = new LineHighlighter(); + this.css = this["class"].highlightClass; + return this.spies = { + __setLocationHash__: spyOn(this["class"], '__setLocationHash__').and.callFake(function() {}) + }; + }); + describe('behavior', function() { + it('highlights one line given in the URL hash', function() { + new LineHighlighter('#L13'); + return expect($('#LC13')).toHaveClass(this.css); + }); + it('highlights a range of lines given in the URL hash', function() { + var i, line, results; + new LineHighlighter('#L5-25'); + expect($("." + this.css).length).toBe(21); + results = []; + for (line = i = 5; i <= 25; line = ++i) { + results.push(expect($("#LC" + line)).toHaveClass(this.css)); + } + return results; + }); + it('scrolls to the first highlighted line on initial load', function() { + var spy; + spy = spyOn($, 'scrollTo'); + new LineHighlighter('#L5-25'); + return expect(spy).toHaveBeenCalledWith('#L5', jasmine.anything()); + }); + it('discards click events', function() { + var spy; + spy = spyOnEvent('a[data-line-number]', 'click'); + clickLine(13); + return expect(spy).toHaveBeenPrevented(); + }); + return it('handles garbage input from the hash', function() { + var func; + func = function() { + return new LineHighlighter('#blob-content-holder'); + }; + return expect(func).not.toThrow(); + }); + }); + describe('#clickHandler', function() { + it('discards the mousedown event', function() { + var spy; + spy = spyOnEvent('a[data-line-number]', 'mousedown'); + clickLine(13); + return expect(spy).toHaveBeenPrevented(); + }); + it('handles clicking on a child icon element', function() { + var spy; + spy = spyOn(this["class"], 'setHash').and.callThrough(); + $('#L13 i').mousedown().click(); + expect(spy).toHaveBeenCalledWith(13); + return expect($('#LC13')).toHaveClass(this.css); + }); + describe('without shiftKey', function() { + it('highlights one line when clicked', function() { + clickLine(13); + return expect($('#LC13')).toHaveClass(this.css); + }); + it('unhighlights previously highlighted lines', function() { + clickLine(13); + clickLine(20); + expect($('#LC13')).not.toHaveClass(this.css); + return expect($('#LC20')).toHaveClass(this.css); + }); + return it('sets the hash', function() { + var spy; + spy = spyOn(this["class"], 'setHash').and.callThrough(); + clickLine(13); + return expect(spy).toHaveBeenCalledWith(13); + }); + }); + return describe('with shiftKey', function() { + it('sets the hash', function() { + var spy; + spy = spyOn(this["class"], 'setHash').and.callThrough(); + clickLine(13); + clickLine(20, { + shiftKey: true + }); + expect(spy).toHaveBeenCalledWith(13); + return expect(spy).toHaveBeenCalledWith(13, 20); + }); + describe('without existing highlight', function() { + it('highlights the clicked line', function() { + clickLine(13, { + shiftKey: true + }); + expect($('#LC13')).toHaveClass(this.css); + return expect($("." + this.css).length).toBe(1); + }); + return it('sets the hash', function() { + var spy; + spy = spyOn(this["class"], 'setHash'); + clickLine(13, { + shiftKey: true + }); + return expect(spy).toHaveBeenCalledWith(13); + }); + }); + describe('with existing single-line highlight', function() { + it('uses existing line as last line when target is lesser', function() { + var i, line, results; + clickLine(20); + clickLine(15, { + shiftKey: true + }); + expect($("." + this.css).length).toBe(6); + results = []; + for (line = i = 15; i <= 20; line = ++i) { + results.push(expect($("#LC" + line)).toHaveClass(this.css)); + } + return results; + }); + return it('uses existing line as first line when target is greater', function() { + var i, line, results; + clickLine(5); + clickLine(10, { + shiftKey: true + }); + expect($("." + this.css).length).toBe(6); + results = []; + for (line = i = 5; i <= 10; line = ++i) { + results.push(expect($("#LC" + line)).toHaveClass(this.css)); + } + return results; + }); + }); + return describe('with existing multi-line highlight', function() { + beforeEach(function() { + clickLine(10, { + shiftKey: true + }); + return clickLine(13, { + shiftKey: true + }); + }); + it('uses target as first line when it is less than existing first line', function() { + var i, line, results; + clickLine(5, { + shiftKey: true + }); + expect($("." + this.css).length).toBe(6); + results = []; + for (line = i = 5; i <= 10; line = ++i) { + results.push(expect($("#LC" + line)).toHaveClass(this.css)); + } + return results; + }); + return it('uses target as last line when it is greater than existing first line', function() { + var i, line, results; + clickLine(15, { + shiftKey: true + }); + expect($("." + this.css).length).toBe(6); + results = []; + for (line = i = 10; i <= 15; line = ++i) { + results.push(expect($("#LC" + line)).toHaveClass(this.css)); + } + return results; + }); + }); + }); + }); + describe('#hashToRange', function() { + beforeEach(function() { + return this.subject = this["class"].hashToRange; + }); + it('extracts a single line number from the hash', function() { + return expect(this.subject('#L5')).toEqual([5, null]); + }); + it('extracts a range of line numbers from the hash', function() { + return expect(this.subject('#L5-15')).toEqual([5, 15]); + }); + return it('returns [null, null] when the hash is not a line number', function() { + return expect(this.subject('#foo')).toEqual([null, null]); + }); + }); + describe('#highlightLine', function() { + beforeEach(function() { + return this.subject = this["class"].highlightLine; + }); + it('highlights the specified line', function() { + this.subject(13); + return expect($('#LC13')).toHaveClass(this.css); + }); + return it('accepts a String-based number', function() { + this.subject('13'); + return expect($('#LC13')).toHaveClass(this.css); + }); + }); + return describe('#setHash', function() { + beforeEach(function() { + return this.subject = this["class"].setHash; + }); + it('sets the location hash for a single line', function() { + this.subject(5); + return expect(this.spies.__setLocationHash__).toHaveBeenCalledWith('#L5'); + }); + return it('sets the location hash for a range', function() { + this.subject(5, 15); + return expect(this.spies.__setLocationHash__).toHaveBeenCalledWith('#L5-15'); + }); + }); + }); + +}).call(this); diff --git a/spec/javascripts/line_highlighter_spec.js.coffee b/spec/javascripts/line_highlighter_spec.js.coffee deleted file mode 100644 index a073f21e7bc..00000000000 --- a/spec/javascripts/line_highlighter_spec.js.coffee +++ /dev/null @@ -1,158 +0,0 @@ -#= require line_highlighter - -describe 'LineHighlighter', -> - fixture.preload('line_highlighter.html') - - clickLine = (number, eventData = {}) -> - if $.isEmptyObject(eventData) - $("#L#{number}").mousedown().click() - else - e = $.Event 'mousedown', eventData - $("#L#{number}").trigger(e).click() - - beforeEach -> - fixture.load('line_highlighter.html') - @class = new LineHighlighter() - @css = @class.highlightClass - @spies = { - __setLocationHash__: spyOn(@class, '__setLocationHash__').and.callFake -> - } - - describe 'behavior', -> - it 'highlights one line given in the URL hash', -> - new LineHighlighter('#L13') - expect($('#LC13')).toHaveClass(@css) - - it 'highlights a range of lines given in the URL hash', -> - new LineHighlighter('#L5-25') - expect($(".#{@css}").length).toBe(21) - expect($("#LC#{line}")).toHaveClass(@css) for line in [5..25] - - it 'scrolls to the first highlighted line on initial load', -> - spy = spyOn($, 'scrollTo') - new LineHighlighter('#L5-25') - expect(spy).toHaveBeenCalledWith('#L5', jasmine.anything()) - - it 'discards click events', -> - spy = spyOnEvent('a[data-line-number]', 'click') - clickLine(13) - expect(spy).toHaveBeenPrevented() - - it 'handles garbage input from the hash', -> - func = -> new LineHighlighter('#blob-content-holder') - expect(func).not.toThrow() - - describe '#clickHandler', -> - it 'discards the mousedown event', -> - spy = spyOnEvent('a[data-line-number]', 'mousedown') - clickLine(13) - expect(spy).toHaveBeenPrevented() - - it 'handles clicking on a child icon element', -> - spy = spyOn(@class, 'setHash').and.callThrough() - - $('#L13 i').mousedown().click() - - expect(spy).toHaveBeenCalledWith(13) - expect($('#LC13')).toHaveClass(@css) - - describe 'without shiftKey', -> - it 'highlights one line when clicked', -> - clickLine(13) - expect($('#LC13')).toHaveClass(@css) - - it 'unhighlights previously highlighted lines', -> - clickLine(13) - clickLine(20) - - expect($('#LC13')).not.toHaveClass(@css) - expect($('#LC20')).toHaveClass(@css) - - it 'sets the hash', -> - spy = spyOn(@class, 'setHash').and.callThrough() - clickLine(13) - expect(spy).toHaveBeenCalledWith(13) - - describe 'with shiftKey', -> - it 'sets the hash', -> - spy = spyOn(@class, 'setHash').and.callThrough() - clickLine(13) - clickLine(20, shiftKey: true) - expect(spy).toHaveBeenCalledWith(13) - expect(spy).toHaveBeenCalledWith(13, 20) - - describe 'without existing highlight', -> - it 'highlights the clicked line', -> - clickLine(13, shiftKey: true) - expect($('#LC13')).toHaveClass(@css) - expect($(".#{@css}").length).toBe(1) - - it 'sets the hash', -> - spy = spyOn(@class, 'setHash') - clickLine(13, shiftKey: true) - expect(spy).toHaveBeenCalledWith(13) - - describe 'with existing single-line highlight', -> - it 'uses existing line as last line when target is lesser', -> - clickLine(20) - clickLine(15, shiftKey: true) - expect($(".#{@css}").length).toBe(6) - expect($("#LC#{line}")).toHaveClass(@css) for line in [15..20] - - it 'uses existing line as first line when target is greater', -> - clickLine(5) - clickLine(10, shiftKey: true) - expect($(".#{@css}").length).toBe(6) - expect($("#LC#{line}")).toHaveClass(@css) for line in [5..10] - - describe 'with existing multi-line highlight', -> - beforeEach -> - clickLine(10, shiftKey: true) - clickLine(13, shiftKey: true) - - it 'uses target as first line when it is less than existing first line', -> - clickLine(5, shiftKey: true) - expect($(".#{@css}").length).toBe(6) - expect($("#LC#{line}")).toHaveClass(@css) for line in [5..10] - - it 'uses target as last line when it is greater than existing first line', -> - clickLine(15, shiftKey: true) - expect($(".#{@css}").length).toBe(6) - expect($("#LC#{line}")).toHaveClass(@css) for line in [10..15] - - describe '#hashToRange', -> - beforeEach -> - @subject = @class.hashToRange - - it 'extracts a single line number from the hash', -> - expect(@subject('#L5')).toEqual([5, null]) - - it 'extracts a range of line numbers from the hash', -> - expect(@subject('#L5-15')).toEqual([5, 15]) - - it 'returns [null, null] when the hash is not a line number', -> - expect(@subject('#foo')).toEqual([null, null]) - - describe '#highlightLine', -> - beforeEach -> - @subject = @class.highlightLine - - it 'highlights the specified line', -> - @subject(13) - expect($('#LC13')).toHaveClass(@css) - - it 'accepts a String-based number', -> - @subject('13') - expect($('#LC13')).toHaveClass(@css) - - describe '#setHash', -> - beforeEach -> - @subject = @class.setHash - - it 'sets the location hash for a single line', -> - @subject(5) - expect(@spies.__setLocationHash__).toHaveBeenCalledWith('#L5') - - it 'sets the location hash for a range', -> - @subject(5, 15) - expect(@spies.__setLocationHash__).toHaveBeenCalledWith('#L5-15') diff --git a/spec/javascripts/merge_request_spec.js b/spec/javascripts/merge_request_spec.js new file mode 100644 index 00000000000..61830d267a9 --- /dev/null +++ b/spec/javascripts/merge_request_spec.js @@ -0,0 +1,28 @@ + +/*= require merge_request */ + +(function() { + describe('MergeRequest', function() { + return describe('task lists', function() { + fixture.preload('merge_requests_show.html'); + beforeEach(function() { + fixture.load('merge_requests_show.html'); + return this.merge = new MergeRequest(); + }); + it('modifies the Markdown field', function() { + spyOn(jQuery, 'ajax').and.stub(); + $('input[type=checkbox]').attr('checked', true).trigger('change'); + return expect($('.js-task-list-field').val()).toBe('- [x] Task List Item'); + }); + return it('submits an ajax request on tasklist:changed', function() { + spyOn(jQuery, 'ajax').and.callFake(function(req) { + expect(req.type).toBe('PATCH'); + expect(req.url).toBe('/foo'); + return expect(req.data.merge_request.description).not.toBe(null); + }); + return $('.js-task-list-field').trigger('tasklist:changed'); + }); + }); + }); + +}).call(this); diff --git a/spec/javascripts/merge_request_spec.js.coffee b/spec/javascripts/merge_request_spec.js.coffee deleted file mode 100644 index 3cb67d51c85..00000000000 --- a/spec/javascripts/merge_request_spec.js.coffee +++ /dev/null @@ -1,23 +0,0 @@ -#= require merge_request - -describe 'MergeRequest', -> - describe 'task lists', -> - fixture.preload('merge_requests_show.html') - - beforeEach -> - fixture.load('merge_requests_show.html') - @merge = new MergeRequest() - - it 'modifies the Markdown field', -> - spyOn(jQuery, 'ajax').and.stub() - - $('input[type=checkbox]').attr('checked', true).trigger('change') - expect($('.js-task-list-field').val()).toBe('- [x] Task List Item') - - it 'submits an ajax request on tasklist:changed', -> - spyOn(jQuery, 'ajax').and.callFake (req) -> - expect(req.type).toBe('PATCH') - expect(req.url).toBe('/foo') - expect(req.data.merge_request.description).not.toBe(null) - - $('.js-task-list-field').trigger('tasklist:changed') diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js new file mode 100644 index 00000000000..395032a7416 --- /dev/null +++ b/spec/javascripts/merge_request_tabs_spec.js @@ -0,0 +1,106 @@ + +/*= require merge_request_tabs */ + +(function() { + describe('MergeRequestTabs', function() { + var stubLocation; + stubLocation = function(stubs) { + var defaults; + defaults = { + pathname: '', + search: '', + hash: '' + }; + return $.extend(defaults, stubs); + }; + fixture.preload('merge_request_tabs.html'); + beforeEach(function() { + this["class"] = new MergeRequestTabs(); + return this.spies = { + ajax: spyOn($, 'ajax').and.callFake(function() {}), + history: spyOn(history, 'replaceState').and.callFake(function() {}) + }; + }); + describe('#activateTab', function() { + beforeEach(function() { + fixture.load('merge_request_tabs.html'); + return this.subject = this["class"].activateTab; + }); + it('shows the first tab when action is show', function() { + this.subject('show'); + return expect($('#notes')).toHaveClass('active'); + }); + it('shows the notes tab when action is notes', function() { + this.subject('notes'); + return expect($('#notes')).toHaveClass('active'); + }); + it('shows the commits tab when action is commits', function() { + this.subject('commits'); + return expect($('#commits')).toHaveClass('active'); + }); + return it('shows the diffs tab when action is diffs', function() { + this.subject('diffs'); + return expect($('#diffs')).toHaveClass('active'); + }); + }); + return describe('#setCurrentAction', function() { + beforeEach(function() { + return this.subject = this["class"].setCurrentAction; + }); + it('changes from commits', function() { + this["class"]._location = stubLocation({ + pathname: '/foo/bar/merge_requests/1/commits' + }); + expect(this.subject('notes')).toBe('/foo/bar/merge_requests/1'); + return expect(this.subject('diffs')).toBe('/foo/bar/merge_requests/1/diffs'); + }); + it('changes from diffs', function() { + this["class"]._location = stubLocation({ + pathname: '/foo/bar/merge_requests/1/diffs' + }); + expect(this.subject('notes')).toBe('/foo/bar/merge_requests/1'); + return expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits'); + }); + it('changes from diffs.html', function() { + this["class"]._location = stubLocation({ + pathname: '/foo/bar/merge_requests/1/diffs.html' + }); + expect(this.subject('notes')).toBe('/foo/bar/merge_requests/1'); + return expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits'); + }); + it('changes from notes', function() { + this["class"]._location = stubLocation({ + pathname: '/foo/bar/merge_requests/1' + }); + expect(this.subject('diffs')).toBe('/foo/bar/merge_requests/1/diffs'); + return expect(this.subject('commits')).toBe('/foo/bar/merge_requests/1/commits'); + }); + it('includes search parameters and hash string', function() { + this["class"]._location = stubLocation({ + pathname: '/foo/bar/merge_requests/1/diffs', + search: '?view=parallel', + hash: '#L15-35' + }); + return expect(this.subject('show')).toBe('/foo/bar/merge_requests/1?view=parallel#L15-35'); + }); + it('replaces the current history state', function() { + var new_state; + this["class"]._location = stubLocation({ + pathname: '/foo/bar/merge_requests/1' + }); + new_state = this.subject('commits'); + return expect(this.spies.history).toHaveBeenCalledWith({ + turbolinks: true, + url: new_state + }, document.title, new_state); + }); + return it('treats "show" like "notes"', function() { + this["class"]._location = stubLocation({ + pathname: '/foo/bar/merge_requests/1/commits' + }); + return expect(this.subject('show')).toBe('/foo/bar/merge_requests/1'); + }); + }); + }); + +}).call(this); diff --git a/spec/javascripts/merge_request_tabs_spec.js.coffee b/spec/javascripts/merge_request_tabs_spec.js.coffee deleted file mode 100644 index a0cfba455ea..00000000000 --- a/spec/javascripts/merge_request_tabs_spec.js.coffee +++ /dev/null @@ -1,88 +0,0 @@ -#= require merge_request_tabs - -describe 'MergeRequestTabs', -> - stubLocation = (stubs) -> - defaults = {pathname: '', search: '', hash: ''} - $.extend(defaults, stubs) - - fixture.preload('merge_request_tabs.html') - - beforeEach -> - @class = new MergeRequestTabs() - @spies = { - ajax: spyOn($, 'ajax').and.callFake -> - history: spyOn(history, 'replaceState').and.callFake -> - } - - describe '#activateTab', -> - beforeEach -> - fixture.load('merge_request_tabs.html') - @subject = @class.activateTab - - it 'shows the first tab when action is show', -> - @subject('show') - expect($('#notes')).toHaveClass('active') - - it 'shows the notes tab when action is notes', -> - @subject('notes') - expect($('#notes')).toHaveClass('active') - - it 'shows the commits tab when action is commits', -> - @subject('commits') - expect($('#commits')).toHaveClass('active') - - it 'shows the diffs tab when action is diffs', -> - @subject('diffs') - expect($('#diffs')).toHaveClass('active') - - describe '#setCurrentAction', -> - beforeEach -> - @subject = @class.setCurrentAction - - it 'changes from commits', -> - @class._location = stubLocation(pathname: '/foo/bar/merge_requests/1/commits') - - expect(@subject('notes')).toBe('/foo/bar/merge_requests/1') - expect(@subject('diffs')).toBe('/foo/bar/merge_requests/1/diffs') - - it 'changes from diffs', -> - @class._location = stubLocation(pathname: '/foo/bar/merge_requests/1/diffs') - - expect(@subject('notes')).toBe('/foo/bar/merge_requests/1') - expect(@subject('commits')).toBe('/foo/bar/merge_requests/1/commits') - - it 'changes from diffs.html', -> - @class._location = stubLocation(pathname: '/foo/bar/merge_requests/1/diffs.html') - - expect(@subject('notes')).toBe('/foo/bar/merge_requests/1') - expect(@subject('commits')).toBe('/foo/bar/merge_requests/1/commits') - - it 'changes from notes', -> - @class._location = stubLocation(pathname: '/foo/bar/merge_requests/1') - - expect(@subject('diffs')).toBe('/foo/bar/merge_requests/1/diffs') - expect(@subject('commits')).toBe('/foo/bar/merge_requests/1/commits') - - it 'includes search parameters and hash string', -> - @class._location = stubLocation({ - pathname: '/foo/bar/merge_requests/1/diffs' - search: '?view=parallel' - hash: '#L15-35' - }) - - expect(@subject('show')).toBe('/foo/bar/merge_requests/1?view=parallel#L15-35') - - it 'replaces the current history state', -> - @class._location = stubLocation(pathname: '/foo/bar/merge_requests/1') - new_state = @subject('commits') - - expect(@spies.history).toHaveBeenCalledWith( - {turbolinks: true, url: new_state}, - document.title, - new_state - ) - - it 'treats "show" like "notes"', -> - @class._location = stubLocation(pathname: '/foo/bar/merge_requests/1/commits') - - expect(@subject('show')).toBe('/foo/bar/merge_requests/1') diff --git a/spec/javascripts/merge_request_widget_spec.js b/spec/javascripts/merge_request_widget_spec.js new file mode 100644 index 00000000000..17b32914ec3 --- /dev/null +++ b/spec/javascripts/merge_request_widget_spec.js @@ -0,0 +1,74 @@ + +/*= require merge_request_widget */ + +(function() { + describe('MergeRequestWidget', function() { + beforeEach(function() { + window.notifyPermissions = function() {}; + window.notify = function() {}; + this.opts = { + ci_status_url: "http://sampledomain.local/ci/getstatus", + ci_status: "", + ci_message: { + normal: "Build {{status}} for \"{{title}}\"", + preparing: "{{status}} build for \"{{title}}\"" + }, + ci_title: { + preparing: "{{status}} build", + normal: "Build {{status}}" + }, + gitlab_icon: "gitlab_logo.png", + builds_path: "http://sampledomain.local/sampleBuildsPath" + }; + this["class"] = new MergeRequestWidget(this.opts); + return this.ciStatusData = { + "title": "Sample MR title", + "sha": "12a34bc5", + "status": "success", + "coverage": 98 + }; + }); + return describe('getCIStatus', function() { + beforeEach(function() { + return spyOn(jQuery, 'getJSON').and.callFake((function(_this) { + return function(req, cb) { + return cb(_this.ciStatusData); + }; + })(this)); + }); + it('should call showCIStatus even if a notification should not be displayed', function() { + var spy; + spy = spyOn(this["class"], 'showCIStatus').and.stub(); + this["class"].getCIStatus(false); + return expect(spy).toHaveBeenCalledWith(this.ciStatusData.status); + }); + it('should call showCIStatus when a notification should be displayed', function() { + var spy; + spy = spyOn(this["class"], 'showCIStatus').and.stub(); + this["class"].getCIStatus(true); + return expect(spy).toHaveBeenCalledWith(this.ciStatusData.status); + }); + it('should call showCICoverage when the coverage rate is set', function() { + var spy; + spy = spyOn(this["class"], 'showCICoverage').and.stub(); + this["class"].getCIStatus(false); + return expect(spy).toHaveBeenCalledWith(this.ciStatusData.coverage); + }); + it('should not call showCICoverage when the coverage rate is not set', function() { + var spy; + this.ciStatusData.coverage = null; + spy = spyOn(this["class"], 'showCICoverage').and.stub(); + this["class"].getCIStatus(false); + return expect(spy).not.toHaveBeenCalled(); + }); + return it('should not display a notification on the first check after the widget has been created', function() { + var spy; + spy = spyOn(window, 'notify'); + this["class"] = new MergeRequestWidget(this.opts); + this["class"].getCIStatus(true); + return expect(spy).not.toHaveBeenCalled(); + }); + }); + }); + +}).call(this); diff --git a/spec/javascripts/merge_request_widget_spec.js.coffee b/spec/javascripts/merge_request_widget_spec.js.coffee deleted file mode 100644 index 92b7eeb1116..00000000000 --- a/spec/javascripts/merge_request_widget_spec.js.coffee +++ /dev/null @@ -1,55 +0,0 @@ -#= require merge_request_widget - -describe 'MergeRequestWidget', -> - - beforeEach -> - window.notifyPermissions = () -> - window.notify = () -> - @opts = { - ci_status_url:"http://sampledomain.local/ci/getstatus", - ci_status:"", - ci_message: { - normal: "Build {{status}} for \"{{title}}\"", - preparing: "{{status}} build for \"{{title}}\"" - }, - ci_title: { - preparing: "{{status}} build", - normal: "Build {{status}}" - }, - gitlab_icon:"gitlab_logo.png", - builds_path:"http://sampledomain.local/sampleBuildsPath" - } - @class = new MergeRequestWidget(@opts) - @ciStatusData = {"title":"Sample MR title","sha":"12a34bc5","status":"success","coverage":98} - - describe 'getCIStatus', -> - beforeEach -> - spyOn(jQuery, 'getJSON').and.callFake (req, cb) => - cb(@ciStatusData) - - it 'should call showCIStatus even if a notification should not be displayed', -> - spy = spyOn(@class, 'showCIStatus').and.stub() - @class.getCIStatus(false) - expect(spy).toHaveBeenCalledWith(@ciStatusData.status) - - it 'should call showCIStatus when a notification should be displayed', -> - spy = spyOn(@class, 'showCIStatus').and.stub() - @class.getCIStatus(true) - expect(spy).toHaveBeenCalledWith(@ciStatusData.status) - - it 'should call showCICoverage when the coverage rate is set', -> - spy = spyOn(@class, 'showCICoverage').and.stub() - @class.getCIStatus(false) - expect(spy).toHaveBeenCalledWith(@ciStatusData.coverage) - - it 'should not call showCICoverage when the coverage rate is not set', -> - @ciStatusData.coverage = null - spy = spyOn(@class, 'showCICoverage').and.stub() - @class.getCIStatus(false) - expect(spy).not.toHaveBeenCalled() - - it 'should not display a notification on the first check after the widget has been created', -> - spy = spyOn(window, 'notify') - @class = new MergeRequestWidget(@opts) - @class.getCIStatus(true) - expect(spy).not.toHaveBeenCalled() diff --git a/spec/javascripts/new_branch_spec.js b/spec/javascripts/new_branch_spec.js new file mode 100644 index 00000000000..25d3f5b6c04 --- /dev/null +++ b/spec/javascripts/new_branch_spec.js @@ -0,0 +1,170 @@ + +/*= require jquery-ui/autocomplete */ + + +/*= require new_branch_form */ + +(function() { + describe('Branch', function() { + return describe('create a new branch', function() { + var expectToHaveError, fillNameWith; + fixture.preload('new_branch.html'); + fillNameWith = function(value) { + return $('.js-branch-name').val(value).trigger('blur'); + }; + expectToHaveError = function(error) { + return expect($('.js-branch-name-error span').text()).toEqual(error); + }; + beforeEach(function() { + fixture.load('new_branch.html'); + $('form').on('submit', function(e) { + return e.preventDefault(); + }); + return this.form = new NewBranchForm($('.js-create-branch-form'), []); + }); + it("can't start with a dot", function() { + fillNameWith('.foo'); + return expectToHaveError("can't start with '.'"); + }); + it("can't start with a slash", function() { + fillNameWith('/foo'); + return expectToHaveError("can't start with '/'"); + }); + it("can't have two consecutive dots", function() { + fillNameWith('foo..bar'); + return expectToHaveError("can't contain '..'"); + }); + it("can't have spaces anywhere", function() { + fillNameWith(' foo'); + expectToHaveError("can't contain spaces"); + fillNameWith('foo bar'); + expectToHaveError("can't contain spaces"); + fillNameWith('foo '); + return expectToHaveError("can't contain spaces"); + }); + it("can't have ~ anywhere", function() { + fillNameWith('~foo'); + expectToHaveError("can't contain '~'"); + fillNameWith('foo~bar'); + expectToHaveError("can't contain '~'"); + fillNameWith('foo~'); + return expectToHaveError("can't contain '~'"); + }); + it("can't have tilde anwhere", function() { + fillNameWith('~foo'); + expectToHaveError("can't contain '~'"); + fillNameWith('foo~bar'); + expectToHaveError("can't contain '~'"); + fillNameWith('foo~'); + return expectToHaveError("can't contain '~'"); + }); + it("can't have caret anywhere", function() { + fillNameWith('^foo'); + expectToHaveError("can't contain '^'"); + fillNameWith('foo^bar'); + expectToHaveError("can't contain '^'"); + fillNameWith('foo^'); + return expectToHaveError("can't contain '^'"); + }); + it("can't have : anywhere", function() { + fillNameWith(':foo'); + expectToHaveError("can't contain ':'"); + fillNameWith('foo:bar'); + expectToHaveError("can't contain ':'"); + fillNameWith(':foo'); + return expectToHaveError("can't contain ':'"); + }); + it("can't have question mark anywhere", function() { + fillNameWith('?foo'); + expectToHaveError("can't contain '?'"); + fillNameWith('foo?bar'); + expectToHaveError("can't contain '?'"); + fillNameWith('foo?'); + return expectToHaveError("can't contain '?'"); + }); + it("can't have asterisk anywhere", function() { + fillNameWith('*foo'); + expectToHaveError("can't contain '*'"); + fillNameWith('foo*bar'); + expectToHaveError("can't contain '*'"); + fillNameWith('foo*'); + return expectToHaveError("can't contain '*'"); + }); + it("can't have open bracket anywhere", function() { + fillNameWith('[foo'); + expectToHaveError("can't contain '['"); + fillNameWith('foo[bar'); + expectToHaveError("can't contain '['"); + fillNameWith('foo['); + return expectToHaveError("can't contain '['"); + }); + it("can't have a backslash anywhere", function() { + fillNameWith('\\foo'); + expectToHaveError("can't contain '\\'"); + fillNameWith('foo\\bar'); + expectToHaveError("can't contain '\\'"); + fillNameWith('foo\\'); + return expectToHaveError("can't contain '\\'"); + }); + it("can't contain a sequence @{ anywhere", function() { + fillNameWith('@{foo'); + expectToHaveError("can't contain '@{'"); + fillNameWith('foo@{bar'); + expectToHaveError("can't contain '@{'"); + fillNameWith('foo@{'); + return expectToHaveError("can't contain '@{'"); + }); + it("can't have consecutive slashes", function() { + fillNameWith('foo//bar'); + return expectToHaveError("can't contain consecutive slashes"); + }); + it("can't end with a slash", function() { + fillNameWith('foo/'); + return expectToHaveError("can't end in '/'"); + }); + it("can't end with a dot", function() { + fillNameWith('foo.'); + return expectToHaveError("can't end in '.'"); + }); + it("can't end with .lock", function() { + fillNameWith('foo.lock'); + return expectToHaveError("can't end in '.lock'"); + }); + it("can't be the single character @", function() { + fillNameWith('@'); + return expectToHaveError("can't be '@'"); + }); + it("concatenates all error messages", function() { + fillNameWith('/foo bar?~.'); + return expectToHaveError("can't start with '/', can't contain spaces, '?', '~', can't end in '.'"); + }); + it("doesn't duplicate error messages", function() { + fillNameWith('?foo?bar?zoo?'); + return expectToHaveError("can't contain '?'"); + }); + it("removes the error message when is a valid name", function() { + fillNameWith('foo?bar'); + expect($('.js-branch-name-error span').length).toEqual(1); + fillNameWith('foobar'); + return expect($('.js-branch-name-error span').length).toEqual(0); + }); + it("can have dashes anywhere", function() { + fillNameWith('-foo-bar-zoo-'); + return expect($('.js-branch-name-error span').length).toEqual(0); + }); + it("can have underscores anywhere", function() { + fillNameWith('_foo_bar_zoo_'); + return expect($('.js-branch-name-error span').length).toEqual(0); + }); + it("can have numbers anywhere", function() { + fillNameWith('1foo2bar3zoo4'); + return expect($('.js-branch-name-error span').length).toEqual(0); + }); + return it("can be only letters", function() { + fillNameWith('foo'); + return expect($('.js-branch-name-error span').length).toEqual(0); + }); + }); + }); + +}).call(this); diff --git a/spec/javascripts/new_branch_spec.js.coffee b/spec/javascripts/new_branch_spec.js.coffee deleted file mode 100644 index ce773793817..00000000000 --- a/spec/javascripts/new_branch_spec.js.coffee +++ /dev/null @@ -1,160 +0,0 @@ -#= require jquery-ui/autocomplete -#= require new_branch_form - -describe 'Branch', -> - describe 'create a new branch', -> - fixture.preload('new_branch.html') - - fillNameWith = (value) -> - $('.js-branch-name').val(value).trigger('blur') - - expectToHaveError = (error) -> - expect($('.js-branch-name-error span').text()).toEqual(error) - - beforeEach -> - fixture.load('new_branch.html') - $('form').on 'submit', (e) -> e.preventDefault() - - @form = new NewBranchForm($('.js-create-branch-form'), []) - - it "can't start with a dot", -> - fillNameWith '.foo' - expectToHaveError "can't start with '.'" - - it "can't start with a slash", -> - fillNameWith '/foo' - expectToHaveError "can't start with '/'" - - it "can't have two consecutive dots", -> - fillNameWith 'foo..bar' - expectToHaveError "can't contain '..'" - - it "can't have spaces anywhere", -> - fillNameWith ' foo' - expectToHaveError "can't contain spaces" - fillNameWith 'foo bar' - expectToHaveError "can't contain spaces" - fillNameWith 'foo ' - expectToHaveError "can't contain spaces" - - it "can't have ~ anywhere", -> - fillNameWith '~foo' - expectToHaveError "can't contain '~'" - fillNameWith 'foo~bar' - expectToHaveError "can't contain '~'" - fillNameWith 'foo~' - expectToHaveError "can't contain '~'" - - it "can't have tilde anwhere", -> - fillNameWith '~foo' - expectToHaveError "can't contain '~'" - fillNameWith 'foo~bar' - expectToHaveError "can't contain '~'" - fillNameWith 'foo~' - expectToHaveError "can't contain '~'" - - it "can't have caret anywhere", -> - fillNameWith '^foo' - expectToHaveError "can't contain '^'" - fillNameWith 'foo^bar' - expectToHaveError "can't contain '^'" - fillNameWith 'foo^' - expectToHaveError "can't contain '^'" - - it "can't have : anywhere", -> - fillNameWith ':foo' - expectToHaveError "can't contain ':'" - fillNameWith 'foo:bar' - expectToHaveError "can't contain ':'" - fillNameWith ':foo' - expectToHaveError "can't contain ':'" - - it "can't have question mark anywhere", -> - fillNameWith '?foo' - expectToHaveError "can't contain '?'" - fillNameWith 'foo?bar' - expectToHaveError "can't contain '?'" - fillNameWith 'foo?' - expectToHaveError "can't contain '?'" - - it "can't have asterisk anywhere", -> - fillNameWith '*foo' - expectToHaveError "can't contain '*'" - fillNameWith 'foo*bar' - expectToHaveError "can't contain '*'" - fillNameWith 'foo*' - expectToHaveError "can't contain '*'" - - it "can't have open bracket anywhere", -> - fillNameWith '[foo' - expectToHaveError "can't contain '['" - fillNameWith 'foo[bar' - expectToHaveError "can't contain '['" - fillNameWith 'foo[' - expectToHaveError "can't contain '['" - - it "can't have a backslash anywhere", -> - fillNameWith '\\foo' - expectToHaveError "can't contain '\\'" - fillNameWith 'foo\\bar' - expectToHaveError "can't contain '\\'" - fillNameWith 'foo\\' - expectToHaveError "can't contain '\\'" - - it "can't contain a sequence @{ anywhere", -> - fillNameWith '@{foo' - expectToHaveError "can't contain '@{'" - fillNameWith 'foo@{bar' - expectToHaveError "can't contain '@{'" - fillNameWith 'foo@{' - expectToHaveError "can't contain '@{'" - - it "can't have consecutive slashes", -> - fillNameWith 'foo//bar' - expectToHaveError "can't contain consecutive slashes" - - it "can't end with a slash", -> - fillNameWith 'foo/' - expectToHaveError "can't end in '/'" - - it "can't end with a dot", -> - fillNameWith 'foo.' - expectToHaveError "can't end in '.'" - - it "can't end with .lock", -> - fillNameWith 'foo.lock' - expectToHaveError "can't end in '.lock'" - - it "can't be the single character @", -> - fillNameWith '@' - expectToHaveError "can't be '@'" - - it "concatenates all error messages", -> - fillNameWith '/foo bar?~.' - expectToHaveError "can't start with '/', can't contain spaces, '?', '~', can't end in '.'" - - it "doesn't duplicate error messages", -> - fillNameWith '?foo?bar?zoo?' - expectToHaveError "can't contain '?'" - - it "removes the error message when is a valid name", -> - fillNameWith 'foo?bar' - expect($('.js-branch-name-error span').length).toEqual(1) - fillNameWith 'foobar' - expect($('.js-branch-name-error span').length).toEqual(0) - - it "can have dashes anywhere", -> - fillNameWith '-foo-bar-zoo-' - expect($('.js-branch-name-error span').length).toEqual(0) - - it "can have underscores anywhere", -> - fillNameWith '_foo_bar_zoo_' - expect($('.js-branch-name-error span').length).toEqual(0) - - it "can have numbers anywhere", -> - fillNameWith '1foo2bar3zoo4' - expect($('.js-branch-name-error span').length).toEqual(0) - - it "can be only letters", -> - fillNameWith 'foo' - expect($('.js-branch-name-error span').length).toEqual(0) diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js new file mode 100644 index 00000000000..14dc6bfdfde --- /dev/null +++ b/spec/javascripts/notes_spec.js @@ -0,0 +1,41 @@ + +/*= require notes */ + + +/*= require gl_form */ + +(function() { + window.gon || (window.gon = {}); + + window.disableButtonIfEmptyField = function() { + return null; + }; + + describe('Notes', function() { + return describe('task lists', function() { + fixture.preload('issue_note.html'); + beforeEach(function() { + fixture.load('issue_note.html'); + $('form').on('submit', function(e) { + return e.preventDefault(); + }); + return this.notes = new Notes(); + }); + it('modifies the Markdown field', function() { + $('input[type=checkbox]').attr('checked', true).trigger('change'); + return expect($('.js-task-list-field').val()).toBe('- [x] Task List Item'); + }); + return it('submits the form on tasklist:changed', function() { + var submitted; + submitted = false; + $('form').on('submit', function(e) { + submitted = true; + return e.preventDefault(); + }); + $('.js-task-list-field').trigger('tasklist:changed'); + return expect(submitted).toBe(true); + }); + }); + }); + +}).call(this); diff --git a/spec/javascripts/notes_spec.js.coffee b/spec/javascripts/notes_spec.js.coffee deleted file mode 100644 index 3a3c8d63e82..00000000000 --- a/spec/javascripts/notes_spec.js.coffee +++ /dev/null @@ -1,26 +0,0 @@ -#= require notes -#= require gl_form - -window.gon or= {} -window.disableButtonIfEmptyField = -> null - -describe 'Notes', -> - describe 'task lists', -> - fixture.preload('issue_note.html') - - beforeEach -> - fixture.load('issue_note.html') - $('form').on 'submit', (e) -> e.preventDefault() - - @notes = new Notes() - - it 'modifies the Markdown field', -> - $('input[type=checkbox]').attr('checked', true).trigger('change') - expect($('.js-task-list-field').val()).toBe('- [x] Task List Item') - - it 'submits the form on tasklist:changed', -> - submitted = false - $('form').on 'submit', (e) -> submitted = true; e.preventDefault() - - $('.js-task-list-field').trigger('tasklist:changed') - expect(submitted).toBe(true) diff --git a/spec/javascripts/project_title_spec.js b/spec/javascripts/project_title_spec.js new file mode 100644 index 00000000000..ffe49828492 --- /dev/null +++ b/spec/javascripts/project_title_spec.js @@ -0,0 +1,60 @@ + +/*= require bootstrap */ + + +/*= require select2 */ + + +/*= require lib/utils/type_utility */ + + +/*= require gl_dropdown */ + + +/*= require api */ + + +/*= require project_select */ + + +/*= require project */ + +(function() { + window.gon || (window.gon = {}); + + window.gon.api_version = 'v3'; + + describe('Project Title', function() { + fixture.preload('project_title.html'); + fixture.preload('projects.json'); + beforeEach(function() { + fixture.load('project_title.html'); + return this.project = new Project(); + }); + return describe('project list', function() { + beforeEach((function(_this) { + return function() { + _this.projects_data = fixture.load('projects.json')[0]; + return spyOn(jQuery, 'ajax').and.callFake(function(req) { + var d; + expect(req.url).toBe('/api/v3/projects.json?simple=true'); + d = $.Deferred(); + d.resolve(_this.projects_data); + return d.promise(); + }); + }; + })(this)); + it('to show on toggle click', (function(_this) { + return function() { + $('.js-projects-dropdown-toggle').click(); + return expect($('.header-content').hasClass('open')).toBe(true); + }; + })(this)); + return it('hide dropdown', function() { + $(".dropdown-menu-close-icon").click(); + return expect($('.header-content').hasClass('open')).toBe(false); + }); + }); + }); + +}).call(this); diff --git a/spec/javascripts/project_title_spec.js.coffee b/spec/javascripts/project_title_spec.js.coffee deleted file mode 100644 index 0244119fa0e..00000000000 --- a/spec/javascripts/project_title_spec.js.coffee +++ /dev/null @@ -1,37 +0,0 @@ -#= require bootstrap -#= require select2 -#= require lib/utils/type_utility -#= require gl_dropdown -#= require api -#= require project_select -#= require project - -window.gon or= {} -window.gon.api_version = 'v3' - -describe 'Project Title', -> - fixture.preload('project_title.html') - fixture.preload('projects.json') - - beforeEach -> - fixture.load('project_title.html') - @project = new Project() - - describe 'project list', -> - beforeEach => - @projects_data = fixture.load('projects.json')[0] - - spyOn(jQuery, 'ajax').and.callFake (req) => - expect(req.url).toBe('/api/v3/projects.json?simple=true') - d = $.Deferred() - d.resolve @projects_data - d.promise() - - it 'to show on toggle click', => - $('.js-projects-dropdown-toggle').click() - expect($('.header-content').hasClass('open')).toBe(true) - - it 'hide dropdown', -> - $(".dropdown-menu-close-icon").click() - - expect($('.header-content').hasClass('open')).toBe(false) diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js new file mode 100644 index 00000000000..38b3b2653ec --- /dev/null +++ b/spec/javascripts/right_sidebar_spec.js @@ -0,0 +1,70 @@ + +/*= require right_sidebar */ + + +/*= require jquery */ + + +/*= require jquery.cookie */ + +(function() { + var $aside, $icon, $labelsIcon, $page, $toggle, assertSidebarState; + + this.sidebar = null; + + $aside = null; + + $toggle = null; + + $icon = null; + + $page = null; + + $labelsIcon = null; + + assertSidebarState = function(state) { + var shouldBeCollapsed, shouldBeExpanded; + shouldBeExpanded = state === 'expanded'; + shouldBeCollapsed = state === 'collapsed'; + expect($aside.hasClass('right-sidebar-expanded')).toBe(shouldBeExpanded); + expect($page.hasClass('right-sidebar-expanded')).toBe(shouldBeExpanded); + expect($icon.hasClass('fa-angle-double-right')).toBe(shouldBeExpanded); + expect($aside.hasClass('right-sidebar-collapsed')).toBe(shouldBeCollapsed); + expect($page.hasClass('right-sidebar-collapsed')).toBe(shouldBeCollapsed); + return expect($icon.hasClass('fa-angle-double-left')).toBe(shouldBeCollapsed); + }; + + describe('RightSidebar', function() { + fixture.preload('right_sidebar.html'); + beforeEach(function() { + fixture.load('right_sidebar.html'); + this.sidebar = new Sidebar; + $aside = $('.right-sidebar'); + $page = $('.page-with-sidebar'); + $icon = $aside.find('i'); + $toggle = $aside.find('.js-sidebar-toggle'); + return $labelsIcon = $aside.find('.sidebar-collapsed-icon'); + }); + it('should expand the sidebar when arrow is clicked', function() { + $toggle.click(); + return assertSidebarState('expanded'); + }); + it('should collapse the sidebar when arrow is clicked', function() { + $toggle.click(); + assertSidebarState('expanded'); + $toggle.click(); + return assertSidebarState('collapsed'); + }); + it('should float over the page and when sidebar icons clicked', function() { + $labelsIcon.click(); + return assertSidebarState('expanded'); + }); + return it('should collapse when the icon arrow clicked while it is floating on page', function() { + $labelsIcon.click(); + assertSidebarState('expanded'); + $toggle.click(); + return assertSidebarState('collapsed'); + }); + }); + +}).call(this); diff --git a/spec/javascripts/right_sidebar_spec.js.coffee b/spec/javascripts/right_sidebar_spec.js.coffee deleted file mode 100644 index 2075cacdb67..00000000000 --- a/spec/javascripts/right_sidebar_spec.js.coffee +++ /dev/null @@ -1,69 +0,0 @@ -#= require right_sidebar -#= require jquery -#= require jquery.cookie - -@sidebar = null -$aside = null -$toggle = null -$icon = null -$page = null -$labelsIcon = null - - -assertSidebarState = (state) -> - - shouldBeExpanded = state is 'expanded' - shouldBeCollapsed = state is 'collapsed' - - expect($aside.hasClass('right-sidebar-expanded')).toBe shouldBeExpanded - expect($page.hasClass('right-sidebar-expanded')).toBe shouldBeExpanded - expect($icon.hasClass('fa-angle-double-right')).toBe shouldBeExpanded - - expect($aside.hasClass('right-sidebar-collapsed')).toBe shouldBeCollapsed - expect($page.hasClass('right-sidebar-collapsed')).toBe shouldBeCollapsed - expect($icon.hasClass('fa-angle-double-left')).toBe shouldBeCollapsed - - -describe 'RightSidebar', -> - - fixture.preload 'right_sidebar.html' - - beforeEach -> - fixture.load 'right_sidebar.html' - - @sidebar = new Sidebar - $aside = $ '.right-sidebar' - $page = $ '.page-with-sidebar' - $icon = $aside.find 'i' - $toggle = $aside.find '.js-sidebar-toggle' - $labelsIcon = $aside.find '.sidebar-collapsed-icon' - - - it 'should expand the sidebar when arrow is clicked', -> - - $toggle.click() - assertSidebarState 'expanded' - - - it 'should collapse the sidebar when arrow is clicked', -> - - $toggle.click() - assertSidebarState 'expanded' - - $toggle.click() - assertSidebarState 'collapsed' - - - it 'should float over the page and when sidebar icons clicked', -> - - $labelsIcon.click() - assertSidebarState 'expanded' - - - it 'should collapse when the icon arrow clicked while it is floating on page', -> - - $labelsIcon.click() - assertSidebarState 'expanded' - - $toggle.click() - assertSidebarState 'collapsed' diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js new file mode 100644 index 00000000000..68d64483d67 --- /dev/null +++ b/spec/javascripts/search_autocomplete_spec.js @@ -0,0 +1,159 @@ + +/*= require gl_dropdown */ + + +/*= require search_autocomplete */ + + +/*= require jquery */ + + +/*= require lib/utils/common_utils */ + + +/*= require lib/utils/type_utility */ + + +/*= require fuzzaldrin-plus */ + +(function() { + var addBodyAttributes, assertLinks, dashboardIssuesPath, dashboardMRsPath, groupIssuesPath, groupMRsPath, groupName, mockDashboardOptions, mockGroupOptions, mockProjectOptions, projectIssuesPath, projectMRsPath, projectName, userId, widget; + + widget = null; + + userId = 1; + + window.gon || (window.gon = {}); + + window.gon.current_user_id = userId; + + dashboardIssuesPath = '/dashboard/issues'; + + dashboardMRsPath = '/dashboard/merge_requests'; + + projectIssuesPath = '/gitlab-org/gitlab-ce/issues'; + + projectMRsPath = '/gitlab-org/gitlab-ce/merge_requests'; + + groupIssuesPath = '/groups/gitlab-org/issues'; + + groupMRsPath = '/groups/gitlab-org/merge_requests'; + + projectName = 'GitLab Community Edition'; + + groupName = 'Gitlab Org'; + + addBodyAttributes = function(section) { + var $body; + if (section == null) { + section = 'dashboard'; + } + $body = $('body'); + $body.removeAttr('data-page'); + $body.removeAttr('data-project'); + $body.removeAttr('data-group'); + switch (section) { + case 'dashboard': + return $body.data('page', 'root:index'); + case 'group': + $body.data('page', 'groups:show'); + return $body.data('group', 'gitlab-org'); + case 'project': + $body.data('page', 'projects:show'); + return $body.data('project', 'gitlab-ce'); + } + }; + + mockDashboardOptions = function() { + window.gl || (window.gl = {}); + return window.gl.dashboardOptions = { + issuesPath: dashboardIssuesPath, + mrPath: dashboardMRsPath + }; + }; + + mockProjectOptions = function() { + window.gl || (window.gl = {}); + return window.gl.projectOptions = { + 'gitlab-ce': { + issuesPath: projectIssuesPath, + mrPath: projectMRsPath, + projectName: projectName + } + }; + }; + + mockGroupOptions = function() { + window.gl || (window.gl = {}); + return window.gl.groupOptions = { + 'gitlab-org': { + issuesPath: groupIssuesPath, + mrPath: groupMRsPath, + projectName: groupName + } + }; + }; + + assertLinks = function(list, issuesPath, mrsPath) { + var a1, a2, a3, a4, issuesAssignedToMeLink, issuesIHaveCreatedLink, mrsAssignedToMeLink, mrsIHaveCreatedLink; + issuesAssignedToMeLink = issuesPath + "/?assignee_id=" + userId; + issuesIHaveCreatedLink = issuesPath + "/?author_id=" + userId; + mrsAssignedToMeLink = mrsPath + "/?assignee_id=" + userId; + mrsIHaveCreatedLink = mrsPath + "/?author_id=" + userId; + a1 = "a[href='" + issuesAssignedToMeLink + "']"; + a2 = "a[href='" + issuesIHaveCreatedLink + "']"; + a3 = "a[href='" + mrsAssignedToMeLink + "']"; + a4 = "a[href='" + mrsIHaveCreatedLink + "']"; + expect(list.find(a1).length).toBe(1); + expect(list.find(a1).text()).toBe(' Issues assigned to me '); + expect(list.find(a2).length).toBe(1); + expect(list.find(a2).text()).toBe(" Issues I've created "); + expect(list.find(a3).length).toBe(1); + expect(list.find(a3).text()).toBe(' Merge requests assigned to me '); + expect(list.find(a4).length).toBe(1); + return expect(list.find(a4).text()).toBe(" Merge requests I've created "); + }; + + describe('Search autocomplete dropdown', function() { + fixture.preload('search_autocomplete.html'); + beforeEach(function() { + fixture.load('search_autocomplete.html'); + return widget = new SearchAutocomplete; + }); + it('should show Dashboard specific dropdown menu', function() { + var list; + addBodyAttributes(); + mockDashboardOptions(); + widget.searchInput.focus(); + list = widget.wrap.find('.dropdown-menu').find('ul'); + return assertLinks(list, dashboardIssuesPath, dashboardMRsPath); + }); + it('should show Group specific dropdown menu', function() { + var list; + addBodyAttributes('group'); + mockGroupOptions(); + widget.searchInput.focus(); + list = widget.wrap.find('.dropdown-menu').find('ul'); + return assertLinks(list, groupIssuesPath, groupMRsPath); + }); + it('should show Project specific dropdown menu', function() { + var list; + addBodyAttributes('project'); + mockProjectOptions(); + widget.searchInput.focus(); + list = widget.wrap.find('.dropdown-menu').find('ul'); + return assertLinks(list, projectIssuesPath, projectMRsPath); + }); + return it('should not show category related menu if there is text in the input', function() { + var link, list; + addBodyAttributes('project'); + mockProjectOptions(); + widget.searchInput.val('help'); + widget.searchInput.focus(); + list = widget.wrap.find('.dropdown-menu').find('ul'); + link = "a[href='" + projectIssuesPath + "/?assignee_id=" + userId + "']"; + return expect(list.find(link).length).toBe(0); + }); + }); + +}).call(this); diff --git a/spec/javascripts/search_autocomplete_spec.js.coffee b/spec/javascripts/search_autocomplete_spec.js.coffee deleted file mode 100644 index 1c1faca3333..00000000000 --- a/spec/javascripts/search_autocomplete_spec.js.coffee +++ /dev/null @@ -1,149 +0,0 @@ -#= require gl_dropdown -#= require search_autocomplete -#= require jquery -#= require lib/utils/common_utils -#= require lib/utils/type_utility -#= require fuzzaldrin-plus - - -widget = null -userId = 1 -window.gon or= {} -window.gon.current_user_id = userId - -dashboardIssuesPath = '/dashboard/issues' -dashboardMRsPath = '/dashboard/merge_requests' -projectIssuesPath = '/gitlab-org/gitlab-ce/issues' -projectMRsPath = '/gitlab-org/gitlab-ce/merge_requests' -groupIssuesPath = '/groups/gitlab-org/issues' -groupMRsPath = '/groups/gitlab-org/merge_requests' -projectName = 'GitLab Community Edition' -groupName = 'Gitlab Org' - - -# Add required attributes to body before starting the test. -# section would be dashboard|group|project -addBodyAttributes = (section = 'dashboard') -> - - $body = $ 'body' - - $body.removeAttr 'data-page' - $body.removeAttr 'data-project' - $body.removeAttr 'data-group' - - switch section - when 'dashboard' - $body.data 'page', 'root:index' - when 'group' - $body.data 'page', 'groups:show' - $body.data 'group', 'gitlab-org' - when 'project' - $body.data 'page', 'projects:show' - $body.data 'project', 'gitlab-ce' - - -# Mock `gl` object in window for dashboard specific page. App code will need it. -mockDashboardOptions = -> - - window.gl or= {} - window.gl.dashboardOptions = - issuesPath: dashboardIssuesPath - mrPath : dashboardMRsPath - - -# Mock `gl` object in window for project specific page. App code will need it. -mockProjectOptions = -> - - window.gl or= {} - window.gl.projectOptions = - 'gitlab-ce' : - issuesPath : projectIssuesPath - mrPath : projectMRsPath - projectName : projectName - - -mockGroupOptions = -> - - window.gl or= {} - window.gl.groupOptions = - 'gitlab-org' : - issuesPath : groupIssuesPath - mrPath : groupMRsPath - projectName : groupName - - -assertLinks = (list, issuesPath, mrsPath) -> - - issuesAssignedToMeLink = "#{issuesPath}/?assignee_id=#{userId}" - issuesIHaveCreatedLink = "#{issuesPath}/?author_id=#{userId}" - mrsAssignedToMeLink = "#{mrsPath}/?assignee_id=#{userId}" - mrsIHaveCreatedLink = "#{mrsPath}/?author_id=#{userId}" - - a1 = "a[href='#{issuesAssignedToMeLink}']" - a2 = "a[href='#{issuesIHaveCreatedLink}']" - a3 = "a[href='#{mrsAssignedToMeLink}']" - a4 = "a[href='#{mrsIHaveCreatedLink}']" - - expect(list.find(a1).length).toBe 1 - expect(list.find(a1).text()).toBe ' Issues assigned to me ' - - expect(list.find(a2).length).toBe 1 - expect(list.find(a2).text()).toBe " Issues I've created " - - expect(list.find(a3).length).toBe 1 - expect(list.find(a3).text()).toBe ' Merge requests assigned to me ' - - expect(list.find(a4).length).toBe 1 - expect(list.find(a4).text()).toBe " Merge requests I've created " - - -describe 'Search autocomplete dropdown', -> - - fixture.preload 'search_autocomplete.html' - - beforeEach -> - - fixture.load 'search_autocomplete.html' - widget = new SearchAutocomplete - - - it 'should show Dashboard specific dropdown menu', -> - - addBodyAttributes() - mockDashboardOptions() - widget.searchInput.focus() - - list = widget.wrap.find('.dropdown-menu').find 'ul' - assertLinks list, dashboardIssuesPath, dashboardMRsPath - - - it 'should show Group specific dropdown menu', -> - - addBodyAttributes 'group' - mockGroupOptions() - widget.searchInput.focus() - - list = widget.wrap.find('.dropdown-menu').find 'ul' - assertLinks list, groupIssuesPath, groupMRsPath - - - it 'should show Project specific dropdown menu', -> - - addBodyAttributes 'project' - mockProjectOptions() - widget.searchInput.focus() - - list = widget.wrap.find('.dropdown-menu').find 'ul' - assertLinks list, projectIssuesPath, projectMRsPath - - - it 'should not show category related menu if there is text in the input', -> - - addBodyAttributes 'project' - mockProjectOptions() - widget.searchInput.val 'help' - widget.searchInput.focus() - - list = widget.wrap.find('.dropdown-menu').find 'ul' - link = "a[href='#{projectIssuesPath}/?assignee_id=#{userId}']" - expect(list.find(link).length).toBe 0 diff --git a/spec/javascripts/shortcuts_issuable_spec.js b/spec/javascripts/shortcuts_issuable_spec.js new file mode 100644 index 00000000000..7b6b55fe545 --- /dev/null +++ b/spec/javascripts/shortcuts_issuable_spec.js @@ -0,0 +1,74 @@ + +/*= require shortcuts_issuable */ + +(function() { + describe('ShortcutsIssuable', function() { + fixture.preload('issuable.html'); + beforeEach(function() { + fixture.load('issuable.html'); + return this.shortcut = new ShortcutsIssuable(); + }); + return describe('#replyWithSelectedText', function() { + var stubSelection; + stubSelection = function(text) { + return window.getSelection = function() { + return text; + }; + }; + beforeEach(function() { + return this.selector = 'form.js-main-target-form textarea#note_note'; + }); + describe('with empty selection', function() { + return it('does nothing', function() { + stubSelection(''); + this.shortcut.replyWithSelectedText(); + return expect($(this.selector).val()).toBe(''); + }); + }); + describe('with any selection', function() { + beforeEach(function() { + return stubSelection('Selected text.'); + }); + it('leaves existing input intact', function() { + $(this.selector).val('This text was already here.'); + expect($(this.selector).val()).toBe('This text was already here.'); + this.shortcut.replyWithSelectedText(); + return expect($(this.selector).val()).toBe("This text was already here.\n> Selected text.\n\n"); + }); + it('triggers `input`', function() { + var triggered; + triggered = false; + $(this.selector).on('input', function() { + return triggered = true; + }); + this.shortcut.replyWithSelectedText(); + return expect(triggered).toBe(true); + }); + return it('triggers `focus`', function() { + var focused; + focused = false; + $(this.selector).on('focus', function() { + return focused = true; + }); + this.shortcut.replyWithSelectedText(); + return expect(focused).toBe(true); + }); + }); + describe('with a one-line selection', function() { + return it('quotes the selection', function() { + stubSelection('This text has been selected.'); + this.shortcut.replyWithSelectedText(); + return expect($(this.selector).val()).toBe("> This text has been selected.\n\n"); + }); + }); + return describe('with a multi-line selection', function() { + return it('quotes the selected lines as a group', function() { + stubSelection("Selected line one.\n\nSelected line two.\nSelected line three.\n"); + this.shortcut.replyWithSelectedText(); + return expect($(this.selector).val()).toBe("> Selected line one.\n> Selected line two.\n> Selected line three.\n\n"); + }); + }); + }); + }); + +}).call(this); diff --git a/spec/javascripts/shortcuts_issuable_spec.js.coffee b/spec/javascripts/shortcuts_issuable_spec.js.coffee deleted file mode 100644 index a01ad7140dd..00000000000 --- a/spec/javascripts/shortcuts_issuable_spec.js.coffee +++ /dev/null @@ -1,82 +0,0 @@ -#= require shortcuts_issuable - -describe 'ShortcutsIssuable', -> - fixture.preload('issuable.html') - - beforeEach -> - fixture.load('issuable.html') - @shortcut = new ShortcutsIssuable() - - describe '#replyWithSelectedText', -> - # Stub window.getSelection to return the provided String. - stubSelection = (text) -> - window.getSelection = -> text - - beforeEach -> - @selector = 'form.js-main-target-form textarea#note_note' - - describe 'with empty selection', -> - it 'does nothing', -> - stubSelection('') - @shortcut.replyWithSelectedText() - expect($(@selector).val()).toBe('') - - describe 'with any selection', -> - beforeEach -> - stubSelection('Selected text.') - - it 'leaves existing input intact', -> - $(@selector).val('This text was already here.') - expect($(@selector).val()).toBe('This text was already here.') - - @shortcut.replyWithSelectedText() - expect($(@selector).val()). - toBe("This text was already here.\n> Selected text.\n\n") - - it 'triggers `input`', -> - triggered = false - $(@selector).on 'input', -> triggered = true - @shortcut.replyWithSelectedText() - - expect(triggered).toBe(true) - - it 'triggers `focus`', -> - focused = false - $(@selector).on 'focus', -> focused = true - @shortcut.replyWithSelectedText() - - expect(focused).toBe(true) - - describe 'with a one-line selection', -> - it 'quotes the selection', -> - stubSelection('This text has been selected.') - - @shortcut.replyWithSelectedText() - - expect($(@selector).val()). - toBe("> This text has been selected.\n\n") - - describe 'with a multi-line selection', -> - it 'quotes the selected lines as a group', -> - stubSelection( - """ - Selected line one. - - Selected line two. - Selected line three. - - """ - ) - - @shortcut.replyWithSelectedText() - - expect($(@selector).val()). - toBe( - """ - > Selected line one. - > Selected line two. - > Selected line three. - - - """ - ) diff --git a/spec/javascripts/spec_helper.coffee b/spec/javascripts/spec_helper.coffee deleted file mode 100644 index 90b02a6aec5..00000000000 --- a/spec/javascripts/spec_helper.coffee +++ /dev/null @@ -1,47 +0,0 @@ -# PhantomJS (Teaspoons default driver) doesn't have support for -# Function.prototype.bind, which has caused confusion. Use this polyfill to -# avoid the confusion. - -#= require support/bind-poly - -# You can require your own javascript files here. By default this will include -# everything in application, however you may get better load performance if you -# require the specific files that are being used in the spec that tests them. - -#= require jquery -#= require jquery.turbolinks -#= require bootstrap -#= require underscore - -# Teaspoon includes some support files, but you can use anything from your own -# support path too. - -# require support/jasmine-jquery-1.7.0 -# require support/jasmine-jquery-2.0.0 -#= require support/jasmine-jquery-2.1.0 -# require support/sinon -# require support/your-support-file - -# Deferring execution - -# If you're using CommonJS, RequireJS or some other asynchronous library you can -# defer execution. Call Teaspoon.execute() after everything has been loaded. -# Simple example of a timeout: - -# Teaspoon.defer = true -# setTimeout(Teaspoon.execute, 1000) - -# Matching files - -# By default Teaspoon will look for files that match -# _spec.{js,js.coffee,.coffee}. Add a filename_spec.js file in your spec path -# and it'll be included in the default suite automatically. If you want to -# customize suites, check out the configuration in teaspoon_env.rb - -# Manifest - -# If you'd rather require your spec files manually (to control order for -# instance) you can disable the suite matcher in the configuration and use this -# file as a manifest. - -# For more information: http://github.com/modeset/teaspoon diff --git a/spec/javascripts/spec_helper.js b/spec/javascripts/spec_helper.js new file mode 100644 index 00000000000..7d91ed0f855 --- /dev/null +++ b/spec/javascripts/spec_helper.js @@ -0,0 +1,22 @@ + +/*= require support/bind-poly */ + + +/*= require jquery */ + + +/*= require jquery.turbolinks */ + + +/*= require bootstrap */ + + +/*= require underscore */ + + +/*= require support/jasmine-jquery-2.1.0 */ + +(function() { + + +}).call(this); diff --git a/spec/javascripts/syntax_highlight_spec.js b/spec/javascripts/syntax_highlight_spec.js new file mode 100644 index 00000000000..4e5dd1e59bf --- /dev/null +++ b/spec/javascripts/syntax_highlight_spec.js @@ -0,0 +1,44 @@ + +/*= require syntax_highlight */ + +(function() { + describe('Syntax Highlighter', function() { + var stubUserColorScheme; + stubUserColorScheme = function(value) { + if (window.gon == null) { + window.gon = {}; + } + return window.gon.user_color_scheme = value; + }; + describe('on a js-syntax-highlight element', function() { + beforeEach(function() { + return fixture.set('<div class="js-syntax-highlight"></div>'); + }); + return it('applies syntax highlighting', function() { + stubUserColorScheme('monokai'); + $('.js-syntax-highlight').syntaxHighlight(); + return expect($('.js-syntax-highlight')).toHaveClass('monokai'); + }); + }); + return describe('on a parent element', function() { + beforeEach(function() { + return fixture.set("<div class=\"parent\">\n <div class=\"js-syntax-highlight\"></div>\n <div class=\"foo\"></div>\n <div class=\"js-syntax-highlight\"></div>\n</div>"); + }); + it('applies highlighting to all applicable children', function() { + stubUserColorScheme('monokai'); + $('.parent').syntaxHighlight(); + expect($('.parent, .foo')).not.toHaveClass('monokai'); + return expect($('.monokai').length).toBe(2); + }); + return it('prevents an infinite loop when no matches exist', function() { + var highlight; + fixture.set('<div></div>'); + highlight = function() { + return $('div').syntaxHighlight(); + }; + return expect(highlight).not.toThrow(); + }); + }); + }); + +}).call(this); diff --git a/spec/javascripts/syntax_highlight_spec.js.coffee b/spec/javascripts/syntax_highlight_spec.js.coffee deleted file mode 100644 index 6a73b6bf32c..00000000000 --- a/spec/javascripts/syntax_highlight_spec.js.coffee +++ /dev/null @@ -1,42 +0,0 @@ -#= require syntax_highlight - -describe 'Syntax Highlighter', -> - stubUserColorScheme = (value) -> - window.gon ?= {} - window.gon.user_color_scheme = value - - describe 'on a js-syntax-highlight element', -> - beforeEach -> - fixture.set('<div class="js-syntax-highlight"></div>') - - it 'applies syntax highlighting', -> - stubUserColorScheme('monokai') - - $('.js-syntax-highlight').syntaxHighlight() - - expect($('.js-syntax-highlight')).toHaveClass('monokai') - - describe 'on a parent element', -> - beforeEach -> - fixture.set """ - <div class="parent"> - <div class="js-syntax-highlight"></div> - <div class="foo"></div> - <div class="js-syntax-highlight"></div> - </div> - """ - - it 'applies highlighting to all applicable children', -> - stubUserColorScheme('monokai') - - $('.parent').syntaxHighlight() - - expect($('.parent, .foo')).not.toHaveClass('monokai') - expect($('.monokai').length).toBe(2) - - it 'prevents an infinite loop when no matches exist', -> - fixture.set('<div></div>') - - highlight = -> $('div').syntaxHighlight() - - expect(highlight).not.toThrow() diff --git a/spec/javascripts/u2f/authenticate_spec.coffee b/spec/javascripts/u2f/authenticate_spec.coffee deleted file mode 100644 index 8ffeda11704..00000000000 --- a/spec/javascripts/u2f/authenticate_spec.coffee +++ /dev/null @@ -1,51 +0,0 @@ -#= require u2f/authenticate -#= require u2f/util -#= require u2f/error -#= require u2f -#= require ./mock_u2f_device - -describe 'U2FAuthenticate', -> - fixture.load('u2f/authenticate') - - beforeEach -> - @u2fDevice = new MockU2FDevice - @container = $("#js-authenticate-u2f") - @component = new U2FAuthenticate(@container, {sign_requests: []}, "token") - @component.start() - - it 'allows authenticating via a U2F device', -> - setupButton = @container.find("#js-login-u2f-device") - setupMessage = @container.find("p") - expect(setupMessage.text()).toContain('Insert your security key') - expect(setupButton.text()).toBe('Login Via U2F Device') - setupButton.trigger('click') - - inProgressMessage = @container.find("p") - expect(inProgressMessage.text()).toContain("Trying to communicate with your device") - - @u2fDevice.respondToAuthenticateRequest({deviceData: "this is data from the device"}) - authenticatedMessage = @container.find("p") - deviceResponse = @container.find('#js-device-response') - expect(authenticatedMessage.text()).toContain("Click this button to authenticate with the GitLab server") - expect(deviceResponse.val()).toBe('{"deviceData":"this is data from the device"}') - - describe "errors", -> - it "displays an error message", -> - setupButton = @container.find("#js-login-u2f-device") - setupButton.trigger('click') - @u2fDevice.respondToAuthenticateRequest({errorCode: "error!"}) - errorMessage = @container.find("p") - expect(errorMessage.text()).toContain("There was a problem communicating with your device") - - it "allows retrying authentication after an error", -> - setupButton = @container.find("#js-login-u2f-device") - setupButton.trigger('click') - @u2fDevice.respondToAuthenticateRequest({errorCode: "error!"}) - retryButton = @container.find("#js-u2f-try-again") - retryButton.trigger('click') - - setupButton = @container.find("#js-login-u2f-device") - setupButton.trigger('click') - @u2fDevice.respondToAuthenticateRequest({deviceData: "this is data from the device"}) - authenticatedMessage = @container.find("p") - expect(authenticatedMessage.text()).toContain("Click this button to authenticate with the GitLab server") diff --git a/spec/javascripts/u2f/authenticate_spec.js b/spec/javascripts/u2f/authenticate_spec.js new file mode 100644 index 00000000000..e008ce956ad --- /dev/null +++ b/spec/javascripts/u2f/authenticate_spec.js @@ -0,0 +1,75 @@ + +/*= require u2f/authenticate */ + + +/*= require u2f/util */ + + +/*= require u2f/error */ + + +/*= require u2f */ + + +/*= require ./mock_u2f_device */ + +(function() { + describe('U2FAuthenticate', function() { + fixture.load('u2f/authenticate'); + beforeEach(function() { + this.u2fDevice = new MockU2FDevice; + this.container = $("#js-authenticate-u2f"); + this.component = new U2FAuthenticate(this.container, { + sign_requests: [] + }, "token"); + return this.component.start(); + }); + it('allows authenticating via a U2F device', function() { + var authenticatedMessage, deviceResponse, inProgressMessage, setupButton, setupMessage; + setupButton = this.container.find("#js-login-u2f-device"); + setupMessage = this.container.find("p"); + expect(setupMessage.text()).toContain('Insert your security key'); + expect(setupButton.text()).toBe('Login Via U2F Device'); + setupButton.trigger('click'); + inProgressMessage = this.container.find("p"); + expect(inProgressMessage.text()).toContain("Trying to communicate with your device"); + this.u2fDevice.respondToAuthenticateRequest({ + deviceData: "this is data from the device" + }); + authenticatedMessage = this.container.find("p"); + deviceResponse = this.container.find('#js-device-response'); + expect(authenticatedMessage.text()).toContain("Click this button to authenticate with the GitLab server"); + return expect(deviceResponse.val()).toBe('{"deviceData":"this is data from the device"}'); + }); + return describe("errors", function() { + it("displays an error message", function() { + var errorMessage, setupButton; + setupButton = this.container.find("#js-login-u2f-device"); + setupButton.trigger('click'); + this.u2fDevice.respondToAuthenticateRequest({ + errorCode: "error!" + }); + errorMessage = this.container.find("p"); + return expect(errorMessage.text()).toContain("There was a problem communicating with your device"); + }); + return it("allows retrying authentication after an error", function() { + var authenticatedMessage, retryButton, setupButton; + setupButton = this.container.find("#js-login-u2f-device"); + setupButton.trigger('click'); + this.u2fDevice.respondToAuthenticateRequest({ + errorCode: "error!" + }); + retryButton = this.container.find("#js-u2f-try-again"); + retryButton.trigger('click'); + setupButton = this.container.find("#js-login-u2f-device"); + setupButton.trigger('click'); + this.u2fDevice.respondToAuthenticateRequest({ + deviceData: "this is data from the device" + }); + authenticatedMessage = this.container.find("p"); + return expect(authenticatedMessage.text()).toContain("Click this button to authenticate with the GitLab server"); + }); + }); + }); + +}).call(this); diff --git a/spec/javascripts/u2f/mock_u2f_device.js b/spec/javascripts/u2f/mock_u2f_device.js new file mode 100644 index 00000000000..ca91a716ba3 --- /dev/null +++ b/spec/javascripts/u2f/mock_u2f_device.js @@ -0,0 +1,33 @@ +(function() { + var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + + this.MockU2FDevice = (function() { + function MockU2FDevice() { + this.respondToAuthenticateRequest = bind(this.respondToAuthenticateRequest, this); + this.respondToRegisterRequest = bind(this.respondToRegisterRequest, this); + window.u2f || (window.u2f = {}); + window.u2f.register = (function(_this) { + return function(appId, registerRequests, signRequests, callback) { + return _this.registerCallback = callback; + }; + })(this); + window.u2f.sign = (function(_this) { + return function(appId, challenges, signRequests, callback) { + return _this.authenticateCallback = callback; + }; + })(this); + } + + MockU2FDevice.prototype.respondToRegisterRequest = function(params) { + return this.registerCallback(params); + }; + + MockU2FDevice.prototype.respondToAuthenticateRequest = function(params) { + return this.authenticateCallback(params); + }; + + return MockU2FDevice; + + })(); + +}).call(this); diff --git a/spec/javascripts/u2f/mock_u2f_device.js.coffee b/spec/javascripts/u2f/mock_u2f_device.js.coffee deleted file mode 100644 index 97ed0e83a0e..00000000000 --- a/spec/javascripts/u2f/mock_u2f_device.js.coffee +++ /dev/null @@ -1,15 +0,0 @@ -class @MockU2FDevice - constructor: () -> - window.u2f ||= {} - - window.u2f.register = (appId, registerRequests, signRequests, callback) => - @registerCallback = callback - - window.u2f.sign = (appId, challenges, signRequests, callback) => - @authenticateCallback = callback - - respondToRegisterRequest: (params) => - @registerCallback(params) - - respondToAuthenticateRequest: (params) => - @authenticateCallback(params) diff --git a/spec/javascripts/u2f/register_spec.js b/spec/javascripts/u2f/register_spec.js new file mode 100644 index 00000000000..21c5266c60e --- /dev/null +++ b/spec/javascripts/u2f/register_spec.js @@ -0,0 +1,81 @@ + +/*= require u2f/register */ + + +/*= require u2f/util */ + + +/*= require u2f/error */ + + +/*= require u2f */ + + +/*= require ./mock_u2f_device */ + +(function() { + describe('U2FRegister', function() { + fixture.load('u2f/register'); + beforeEach(function() { + this.u2fDevice = new MockU2FDevice; + this.container = $("#js-register-u2f"); + this.component = new U2FRegister(this.container, $("#js-register-u2f-templates"), {}, "token"); + return this.component.start(); + }); + it('allows registering a U2F device', function() { + var deviceResponse, inProgressMessage, registeredMessage, setupButton; + setupButton = this.container.find("#js-setup-u2f-device"); + expect(setupButton.text()).toBe('Setup New U2F Device'); + setupButton.trigger('click'); + inProgressMessage = this.container.children("p"); + expect(inProgressMessage.text()).toContain("Trying to communicate with your device"); + this.u2fDevice.respondToRegisterRequest({ + deviceData: "this is data from the device" + }); + registeredMessage = this.container.find('p'); + deviceResponse = this.container.find('#js-device-response'); + expect(registeredMessage.text()).toContain("Your device was successfully set up!"); + return expect(deviceResponse.val()).toBe('{"deviceData":"this is data from the device"}'); + }); + return describe("errors", function() { + it("doesn't allow the same device to be registered twice (for the same user", function() { + var errorMessage, setupButton; + setupButton = this.container.find("#js-setup-u2f-device"); + setupButton.trigger('click'); + this.u2fDevice.respondToRegisterRequest({ + errorCode: 4 + }); + errorMessage = this.container.find("p"); + return expect(errorMessage.text()).toContain("already been registered with us"); + }); + it("displays an error message for other errors", function() { + var errorMessage, setupButton; + setupButton = this.container.find("#js-setup-u2f-device"); + setupButton.trigger('click'); + this.u2fDevice.respondToRegisterRequest({ + errorCode: "error!" + }); + errorMessage = this.container.find("p"); + return expect(errorMessage.text()).toContain("There was a problem communicating with your device"); + }); + return it("allows retrying registration after an error", function() { + var registeredMessage, retryButton, setupButton; + setupButton = this.container.find("#js-setup-u2f-device"); + setupButton.trigger('click'); + this.u2fDevice.respondToRegisterRequest({ + errorCode: "error!" + }); + retryButton = this.container.find("#U2FTryAgain"); + retryButton.trigger('click'); + setupButton = this.container.find("#js-setup-u2f-device"); + setupButton.trigger('click'); + this.u2fDevice.respondToRegisterRequest({ + deviceData: "this is data from the device" + }); + registeredMessage = this.container.find("p"); + return expect(registeredMessage.text()).toContain("Your device was successfully set up!"); + }); + }); + }); + +}).call(this); diff --git a/spec/javascripts/u2f/register_spec.js.coffee b/spec/javascripts/u2f/register_spec.js.coffee deleted file mode 100644 index 87dc769792b..00000000000 --- a/spec/javascripts/u2f/register_spec.js.coffee +++ /dev/null @@ -1,56 +0,0 @@ -#= require u2f/register -#= require u2f/util -#= require u2f/error -#= require u2f -#= require ./mock_u2f_device - -describe 'U2FRegister', -> - fixture.load('u2f/register') - - beforeEach -> - @u2fDevice = new MockU2FDevice - @container = $("#js-register-u2f") - @component = new U2FRegister(@container, $("#js-register-u2f-templates"), {}, "token") - @component.start() - - it 'allows registering a U2F device', -> - setupButton = @container.find("#js-setup-u2f-device") - expect(setupButton.text()).toBe('Setup New U2F Device') - setupButton.trigger('click') - - inProgressMessage = @container.children("p") - expect(inProgressMessage.text()).toContain("Trying to communicate with your device") - - @u2fDevice.respondToRegisterRequest({deviceData: "this is data from the device"}) - registeredMessage = @container.find('p') - deviceResponse = @container.find('#js-device-response') - expect(registeredMessage.text()).toContain("Your device was successfully set up!") - expect(deviceResponse.val()).toBe('{"deviceData":"this is data from the device"}') - - describe "errors", -> - it "doesn't allow the same device to be registered twice (for the same user", -> - setupButton = @container.find("#js-setup-u2f-device") - setupButton.trigger('click') - @u2fDevice.respondToRegisterRequest({errorCode: 4}) - errorMessage = @container.find("p") - expect(errorMessage.text()).toContain("already been registered with us") - - it "displays an error message for other errors", -> - setupButton = @container.find("#js-setup-u2f-device") - setupButton.trigger('click') - @u2fDevice.respondToRegisterRequest({errorCode: "error!"}) - errorMessage = @container.find("p") - expect(errorMessage.text()).toContain("There was a problem communicating with your device") - - it "allows retrying registration after an error", -> - setupButton = @container.find("#js-setup-u2f-device") - setupButton.trigger('click') - @u2fDevice.respondToRegisterRequest({errorCode: "error!"}) - retryButton = @container.find("#U2FTryAgain") - retryButton.trigger('click') - - setupButton = @container.find("#js-setup-u2f-device") - setupButton.trigger('click') - @u2fDevice.respondToRegisterRequest({deviceData: "this is data from the device"}) - registeredMessage = @container.find("p") - expect(registeredMessage.text()).toContain("Your device was successfully set up!") diff --git a/spec/javascripts/zen_mode_spec.js b/spec/javascripts/zen_mode_spec.js new file mode 100644 index 00000000000..3d680ec8ea3 --- /dev/null +++ b/spec/javascripts/zen_mode_spec.js @@ -0,0 +1,73 @@ + +/*= require zen_mode */ + +(function() { + var enterZen, escapeKeydown, exitZen; + + describe('ZenMode', function() { + fixture.preload('zen_mode.html'); + beforeEach(function() { + fixture.load('zen_mode.html'); + spyOn(Dropzone, 'forElement').and.callFake(function() { + return { + enable: function() { + return true; + } + }; + }); + this.zen = new ZenMode(); + return this.zen.scroll_position = 456; + }); + describe('on enter', function() { + it('pauses Mousetrap', function() { + spyOn(Mousetrap, 'pause'); + enterZen(); + return expect(Mousetrap.pause).toHaveBeenCalled(); + }); + return it('removes textarea styling', function() { + $('textarea').attr('style', 'height: 400px'); + enterZen(); + return expect('textarea').not.toHaveAttr('style'); + }); + }); + describe('in use', function() { + beforeEach(function() { + return enterZen(); + }); + return it('exits on Escape', function() { + escapeKeydown(); + return expect($('.zen-backdrop')).not.toHaveClass('fullscreen'); + }); + }); + return describe('on exit', function() { + beforeEach(function() { + return enterZen(); + }); + it('unpauses Mousetrap', function() { + spyOn(Mousetrap, 'unpause'); + exitZen(); + return expect(Mousetrap.unpause).toHaveBeenCalled(); + }); + return it('restores the scroll position', function() { + spyOn(this.zen, 'scrollTo'); + exitZen(); + return expect(this.zen.scrollTo).toHaveBeenCalled(); + }); + }); + }); + + enterZen = function() { + return $('a.js-zen-enter').click(); + }; + + exitZen = function() { + return $('a.js-zen-leave').click(); + }; + + escapeKeydown = function() { + return $('textarea').trigger($.Event('keydown', { + keyCode: 27 + })); + }; + +}).call(this); diff --git a/spec/javascripts/zen_mode_spec.js.coffee b/spec/javascripts/zen_mode_spec.js.coffee deleted file mode 100644 index b790fce01ed..00000000000 --- a/spec/javascripts/zen_mode_spec.js.coffee +++ /dev/null @@ -1,51 +0,0 @@ -#= require zen_mode - -describe 'ZenMode', -> - fixture.preload('zen_mode.html') - - beforeEach -> - fixture.load('zen_mode.html') - - # Stub Dropzone.forElement(...).enable() - spyOn(Dropzone, 'forElement').and.callFake -> - enable: -> true - - @zen = new ZenMode() - - # Set this manually because we can't actually scroll the window - @zen.scroll_position = 456 - - describe 'on enter', -> - it 'pauses Mousetrap', -> - spyOn(Mousetrap, 'pause') - enterZen() - expect(Mousetrap.pause).toHaveBeenCalled() - - it 'removes textarea styling', -> - $('textarea').attr('style', 'height: 400px') - enterZen() - expect('textarea').not.toHaveAttr('style') - - describe 'in use', -> - beforeEach -> enterZen() - - it 'exits on Escape', -> - escapeKeydown() - expect($('.zen-backdrop')).not.toHaveClass('fullscreen') - - describe 'on exit', -> - beforeEach -> enterZen() - - it 'unpauses Mousetrap', -> - spyOn(Mousetrap, 'unpause') - exitZen() - expect(Mousetrap.unpause).toHaveBeenCalled() - - it 'restores the scroll position', -> - spyOn(@zen, 'scrollTo') - exitZen() - expect(@zen.scrollTo).toHaveBeenCalled() - -enterZen = -> $('a.js-zen-enter').click() # Ohmmmmmmm -exitZen = -> $('a.js-zen-leave').click() -escapeKeydown = -> $('textarea').trigger($.Event('keydown', {keyCode: 27})) |