diff options
Diffstat (limited to 'spec/javascripts')
393 files changed, 8017 insertions, 6051 deletions
diff --git a/spec/javascripts/.eslintrc.yml b/spec/javascripts/.eslintrc.yml index 1448849a0b7..b863156b57c 100644 --- a/spec/javascripts/.eslintrc.yml +++ b/spec/javascripts/.eslintrc.yml @@ -36,6 +36,4 @@ rules: - ignore: - 'fixtures/blob' # Temporarily disabled to facilitate an upgrade to eslint-plugin-jasmine - jasmine/new-line-before-expect: off - jasmine/no-promise-without-done-fail: off jasmine/prefer-toHaveBeenCalledWith: off diff --git a/spec/javascripts/ajax_loading_spinner_spec.js b/spec/javascripts/ajax_loading_spinner_spec.js index 261375d3a0e..9389fc94f17 100644 --- a/spec/javascripts/ajax_loading_spinner_spec.js +++ b/spec/javascripts/ajax_loading_spinner_spec.js @@ -10,8 +10,8 @@ describe('Ajax Loading Spinner', () => { AjaxLoadingSpinner.init(); }); - it('change current icon with spinner icon and disable link while waiting ajax response', (done) => { - spyOn($, 'ajax').and.callFake((req) => { + it('change current icon with spinner icon and disable link while waiting ajax response', done => { + spyOn($, 'ajax').and.callFake(req => { const xhr = new XMLHttpRequest(); const ajaxLoadingSpinner = document.querySelector('.js-ajax-loading-spinner'); const icon = ajaxLoadingSpinner.querySelector('i'); @@ -33,8 +33,8 @@ describe('Ajax Loading Spinner', () => { document.querySelector('.js-ajax-loading-spinner').click(); }); - it('use original icon again and enabled the link after complete the ajax request', (done) => { - spyOn($, 'ajax').and.callFake((req) => { + it('use original icon again and enabled the link after complete the ajax request', done => { + spyOn($, 'ajax').and.callFake(req => { const xhr = new XMLHttpRequest(); const ajaxLoadingSpinner = document.querySelector('.js-ajax-loading-spinner'); @@ -42,6 +42,7 @@ describe('Ajax Loading Spinner', () => { req.complete({}); const icon = ajaxLoadingSpinner.querySelector('i'); + expect(icon).toHaveClass('fa-trash-o'); expect(icon).not.toHaveClass('fa-spinner'); expect(icon).not.toHaveClass('fa-spin'); diff --git a/spec/javascripts/avatar_helper_spec.js b/spec/javascripts/avatar_helper_spec.js index b2f80678ae7..c1ef08e0f1b 100644 --- a/spec/javascripts/avatar_helper_spec.js +++ b/spec/javascripts/avatar_helper_spec.js @@ -21,7 +21,7 @@ describe('avatar_helper', () => { it(`wraps around if id is bigger than ${IDENTICON_BG_COUNT}`, () => { expect(getIdenticonBackgroundClass(IDENTICON_BG_COUNT + 4)).toEqual('bg5'); - expect(getIdenticonBackgroundClass((IDENTICON_BG_COUNT * 5) + 6)).toEqual('bg7'); + expect(getIdenticonBackgroundClass(IDENTICON_BG_COUNT * 5 + 6)).toEqual('bg7'); }); }); diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js index 3f84a46e8d9..b0689fc7cfe 100644 --- a/spec/javascripts/awards_handler_spec.js +++ b/spec/javascripts/awards_handler_spec.js @@ -1,398 +1,400 @@ -/* eslint-disable no-var, one-var, no-unused-expressions, no-unused-vars, prefer-template */ - import $ from 'jquery'; import Cookies from 'js-cookie'; import loadAwardsHandler from '~/awards_handler'; - import '~/lib/utils/common_utils'; -(function() { - var awardsHandler, lazyAssert, urlRoot, openAndWaitForEmojiMenu; +window.gl = window.gl || {}; +window.gon = window.gon || {}; + +let openAndWaitForEmojiMenu; +let awardsHandler = null; +const urlRoot = gon.relative_url_root; + +const lazyAssert = function(done, assertFn) { + setTimeout(function() { + assertFn(); + done(); + // Maybe jasmine.clock here? + }, 333); +}; + +describe('AwardsHandler', function() { + preloadFixtures('snippets/show.html.raw'); + beforeEach(function(done) { + loadFixtures('snippets/show.html.raw'); + loadAwardsHandler(true) + .then(obj => { + awardsHandler = obj; + spyOn(awardsHandler, 'postEmoji').and.callFake((button, url, emoji, cb) => cb()); + done(); + }) + .catch(fail); + + let isEmojiMenuBuilt = false; + openAndWaitForEmojiMenu = function() { + return new Promise(resolve => { + if (isEmojiMenuBuilt) { + resolve(); + } else { + $('.js-add-award') + .eq(0) + .click(); + const $menu = $('.emoji-menu'); + $menu.one('build-emoji-menu-finish', () => { + isEmojiMenuBuilt = true; + resolve(); + }); + } + }); + }; + }); - awardsHandler = null; + afterEach(function() { + // restore original url root value + gon.relative_url_root = urlRoot; - window.gl || (window.gl = {}); + // Undo what we did to the shared <body> + $('body').removeAttr('data-page'); - window.gon || (window.gon = {}); + awardsHandler.destroy(); + }); - urlRoot = gon.relative_url_root; + describe('::showEmojiMenu', function() { + it('should show emoji menu when Add emoji button clicked', function(done) { + $('.js-add-award') + .eq(0) + .click(); + lazyAssert(done, function() { + const $emojiMenu = $('.emoji-menu'); + + expect($emojiMenu.length).toBe(1); + expect($emojiMenu.hasClass('is-visible')).toBe(true); + expect($emojiMenu.find('.js-emoji-menu-search').length).toBe(1); + expect($('.js-awards-block.current').length).toBe(1); + }); + }); - lazyAssert = function(done, assertFn) { - return setTimeout(function() { - assertFn(); - return done(); - // Maybe jasmine.clock here? - }, 333); - }; + it('should also show emoji menu for the smiley icon in notes', function(done) { + $('.js-add-award.note-action-button').click(); + lazyAssert(done, function() { + const $emojiMenu = $('.emoji-menu'); - describe('AwardsHandler', function() { - preloadFixtures('snippets/show.html.raw'); - beforeEach(function(done) { - loadFixtures('snippets/show.html.raw'); - loadAwardsHandler(true) - .then(obj => { - awardsHandler = obj; - spyOn(awardsHandler, 'postEmoji').and.callFake((button, url, emoji, cb) => cb()); - done(); - }) - .catch(fail); + expect($emojiMenu.length).toBe(1); + }); + }); - let isEmojiMenuBuilt = false; - openAndWaitForEmojiMenu = function() { - return new Promise((resolve, reject) => { - if (isEmojiMenuBuilt) { - resolve(); - } else { - $('.js-add-award') - .eq(0) - .click(); - const $menu = $('.emoji-menu'); - $menu.one('build-emoji-menu-finish', () => { - isEmojiMenuBuilt = true; - resolve(); - }); - } - }); - }; + it('should remove emoji menu when body is clicked', function(done) { + $('.js-add-award') + .eq(0) + .click(); + lazyAssert(done, function() { + const $emojiMenu = $('.emoji-menu'); + $('body').click(); + + expect($emojiMenu.length).toBe(1); + expect($emojiMenu.hasClass('is-visible')).toBe(false); + expect($('.js-awards-block.current').length).toBe(0); + }); }); - afterEach(function() { - // restore original url root value - gon.relative_url_root = urlRoot; + it('should not remove emoji menu when search is clicked', function(done) { + $('.js-add-award') + .eq(0) + .click(); + lazyAssert(done, function() { + const $emojiMenu = $('.emoji-menu'); + $('.emoji-search').click(); + + expect($emojiMenu.length).toBe(1); + expect($emojiMenu.hasClass('is-visible')).toBe(true); + expect($('.js-awards-block.current').length).toBe(1); + }); + }); + }); - // Undo what we did to the shared <body> - $('body').removeAttr('data-page'); + describe('::addAwardToEmojiBar', function() { + it('should add emoji to votes block', function() { + const $votesBlock = $('.js-awards-block').eq(0); + awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false); + const $emojiButton = $votesBlock.find('[data-name=heart]'); - awardsHandler.destroy(); + expect($emojiButton.length).toBe(1); + expect($emojiButton.next('.js-counter').text()).toBe('1'); + expect($votesBlock.hasClass('hidden')).toBe(false); }); - 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('.js-emoji-menu-search').length).toBe(1); - return expect($('.js-awards-block.current').length).toBe(1); - }); - }); + it('should remove the emoji when we click again', function() { + const $votesBlock = $('.js-awards-block').eq(0); + awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false); + awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false); + const $emojiButton = $votesBlock.find('[data-name=heart]'); - it('should also show emoji menu for the smiley icon in notes', function(done) { - $('.js-add-award.note-action-button').click(); - return lazyAssert(done, function() { - var $emojiMenu = $('.emoji-menu'); - return expect($emojiMenu.length).toBe(1); - }); - }); + expect($emojiButton.length).toBe(0); + }); - 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); - }); - }); + it('should decrement the emoji counter', function() { + const $votesBlock = $('.js-awards-block').eq(0); + awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false); + const $emojiButton = $votesBlock.find('[data-name=heart]'); + $emojiButton.next('.js-counter').text(5); + awardsHandler.addAwardToEmojiBar($votesBlock, 'heart', false); - it('should not remove emoji menu when search is clicked', function(done) { - $('.js-add-award') - .eq(0) - .click(); - return lazyAssert(done, function() { - var $emojiMenu; - $emojiMenu = $('.emoji-menu'); - $('.emoji-search').click(); - expect($emojiMenu.length).toBe(1); - expect($emojiMenu.hasClass('is-visible')).toBe(true); - return expect($('.js-awards-block.current').length).toBe(1); - }); - }); + expect($emojiButton.length).toBe(1); + expect($emojiButton.next('.js-counter').text()).toBe('4'); }); + }); - 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-name=heart]'); - expect($emojiButton.length).toBe(1); - expect($emojiButton.next('.js-counter').text()).toBe('1'); - return expect($votesBlock.hasClass('hidden')).toBe(false); - }); + describe('::userAuthored', function() { + it('should update tooltip to user authored title', function() { + const $votesBlock = $('.js-awards-block').eq(0); + const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent(); + $thumbsUpEmoji.attr('data-title', 'sam'); + awardsHandler.userAuthored($thumbsUpEmoji); - 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-name=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-name=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'); - }); + expect($thumbsUpEmoji.data('originalTitle')).toBe( + 'You cannot vote on your own issue, MR and note', + ); }); - describe('::userAuthored', function() { - it('should update tooltip to user authored title', function() { - var $thumbsUpEmoji, $votesBlock; - $votesBlock = $('.js-awards-block').eq(0); - $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent(); - $thumbsUpEmoji.attr('data-title', 'sam'); - awardsHandler.userAuthored($thumbsUpEmoji); - return expect($thumbsUpEmoji.data('originalTitle')).toBe( - 'You cannot vote on your own issue, MR and note', - ); - }); + it('should restore tooltip back to initial vote list', function() { + jasmine.clock().install(); + const $votesBlock = $('.js-awards-block').eq(0); + const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent(); + $thumbsUpEmoji.attr('data-title', 'sam'); + awardsHandler.userAuthored($thumbsUpEmoji); + jasmine.clock().tick(2801); + jasmine.clock().uninstall(); - it('should restore tooltip back to initial vote list', function() { - var $thumbsUpEmoji, $votesBlock; - jasmine.clock().install(); - $votesBlock = $('.js-awards-block').eq(0); - $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent(); - $thumbsUpEmoji.attr('data-title', 'sam'); - awardsHandler.userAuthored($thumbsUpEmoji); - jasmine.clock().tick(2801); - jasmine.clock().uninstall(); - return expect($thumbsUpEmoji.data('originalTitle')).toBe('sam'); - }); + expect($thumbsUpEmoji.data('originalTitle')).toBe('sam'); }); + }); - describe('::getAwardUrl', function() { - return it('returns the url for request', function() { - return expect(awardsHandler.getAwardUrl()).toBe( - 'http://test.host/snippets/1/toggle_award_emoji', - ); - }); + describe('::getAwardUrl', function() { + it('returns the url for request', function() { + expect(awardsHandler.getAwardUrl()).toBe('http://test.host/snippets/1/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-name=thumbsup]').parent(); - $thumbsDownEmoji = $votesBlock.find('[data-name=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('::addAward and ::checkMutuality', function() { + it('should handle :+1: and :-1: mutuality', function() { + const awardUrl = awardsHandler.getAwardUrl(); + const $votesBlock = $('.js-awards-block').eq(0); + const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent(); + const $thumbsDownEmoji = $votesBlock.find('[data-name=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); + 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-name=fire]').length).toBe(1); - awardsHandler.removeEmoji($votesBlock.find('[data-name=fire]').closest('button')); - return expect($votesBlock.find('[data-name=fire]').length).toBe(0); - }); + describe('::removeEmoji', function() { + it('should remove emoji', function() { + const awardUrl = awardsHandler.getAwardUrl(); + const $votesBlock = $('.js-awards-block').eq(0); + awardsHandler.addAward($votesBlock, awardUrl, 'fire', false); + + expect($votesBlock.find('[data-name=fire]').length).toBe(1); + awardsHandler.removeEmoji($votesBlock.find('[data-name=fire]').closest('button')); + + expect($votesBlock.find('[data-name=fire]').length).toBe(0); }); + }); - describe('::addYouToUserList', function() { - it('should prepend "You" to the award tooltip', function() { - var $thumbsUpEmoji, $votesBlock, awardUrl; - awardUrl = awardsHandler.getAwardUrl(); - $votesBlock = $('.js-awards-block').eq(0); - $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent(); - $thumbsUpEmoji.attr('data-title', 'sam, jerry, max, and andy'); - awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false); - $thumbsUpEmoji.tooltip(); - return expect($thumbsUpEmoji.data('originalTitle')).toBe('You, sam, jerry, max, and andy'); - }); - return it('handles the special case where "You" is not cleanly comma seperated', function() { - var $thumbsUpEmoji, $votesBlock, awardUrl; - awardUrl = awardsHandler.getAwardUrl(); - $votesBlock = $('.js-awards-block').eq(0); - $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent(); - $thumbsUpEmoji.attr('data-title', 'sam'); - awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false); - $thumbsUpEmoji.tooltip(); - return expect($thumbsUpEmoji.data('originalTitle')).toBe('You and sam'); - }); + describe('::addYouToUserList', function() { + it('should prepend "You" to the award tooltip', function() { + const awardUrl = awardsHandler.getAwardUrl(); + const $votesBlock = $('.js-awards-block').eq(0); + const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent(); + $thumbsUpEmoji.attr('data-title', 'sam, jerry, max, and andy'); + awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false); + $thumbsUpEmoji.tooltip(); + + expect($thumbsUpEmoji.data('originalTitle')).toBe('You, sam, jerry, max, and andy'); }); - describe('::removeYouToUserList', function() { - it('removes "You" from the front of the tooltip', function() { - var $thumbsUpEmoji, $votesBlock, awardUrl; - awardUrl = awardsHandler.getAwardUrl(); - $votesBlock = $('.js-awards-block').eq(0); - $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent(); - $thumbsUpEmoji.attr('data-title', 'You, sam, jerry, max, and andy'); - $thumbsUpEmoji.addClass('active'); - awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false); - $thumbsUpEmoji.tooltip(); - return expect($thumbsUpEmoji.data('originalTitle')).toBe('sam, jerry, max, and andy'); - }); - return it('handles the special case where "You" is not cleanly comma seperated', function() { - var $thumbsUpEmoji, $votesBlock, awardUrl; - awardUrl = awardsHandler.getAwardUrl(); - $votesBlock = $('.js-awards-block').eq(0); - $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent(); - $thumbsUpEmoji.attr('data-title', 'You and sam'); - $thumbsUpEmoji.addClass('active'); - awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false); - $thumbsUpEmoji.tooltip(); - return expect($thumbsUpEmoji.data('originalTitle')).toBe('sam'); - }); + it('handles the special case where "You" is not cleanly comma seperated', function() { + const awardUrl = awardsHandler.getAwardUrl(); + const $votesBlock = $('.js-awards-block').eq(0); + const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent(); + $thumbsUpEmoji.attr('data-title', 'sam'); + awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false); + $thumbsUpEmoji.tooltip(); + + expect($thumbsUpEmoji.data('originalTitle')).toBe('You and sam'); }); + }); - describe('::searchEmojis', () => { - it('should filter the emoji', function(done) { - return openAndWaitForEmojiMenu() - .then(() => { - expect($('[data-name=angel]').is(':visible')).toBe(true); - expect($('[data-name=anger]').is(':visible')).toBe(true); - awardsHandler.searchEmojis('ali'); - expect($('[data-name=angel]').is(':visible')).toBe(false); - expect($('[data-name=anger]').is(':visible')).toBe(false); - expect($('[data-name=alien]').is(':visible')).toBe(true); - expect($('.js-emoji-menu-search').val()).toBe('ali'); - }) - .then(done) - .catch(err => { - done.fail(`Failed to open and build emoji menu: ${err.message}`); - }); - }); + describe('::removeYouToUserList', function() { + it('removes "You" from the front of the tooltip', function() { + const awardUrl = awardsHandler.getAwardUrl(); + const $votesBlock = $('.js-awards-block').eq(0); + const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent(); + $thumbsUpEmoji.attr('data-title', 'You, sam, jerry, max, and andy'); + $thumbsUpEmoji.addClass('active'); + awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false); + $thumbsUpEmoji.tooltip(); + + expect($thumbsUpEmoji.data('originalTitle')).toBe('sam, jerry, max, and andy'); + }); - it('should clear the search when searching for nothing', function(done) { - return openAndWaitForEmojiMenu() - .then(() => { - awardsHandler.searchEmojis('ali'); - expect($('[data-name=angel]').is(':visible')).toBe(false); - expect($('[data-name=anger]').is(':visible')).toBe(false); - expect($('[data-name=alien]').is(':visible')).toBe(true); - awardsHandler.searchEmojis(''); - expect($('[data-name=angel]').is(':visible')).toBe(true); - expect($('[data-name=anger]').is(':visible')).toBe(true); - expect($('[data-name=alien]').is(':visible')).toBe(true); - expect($('.js-emoji-menu-search').val()).toBe(''); - }) - .then(done) - .catch(err => { - done.fail(`Failed to open and build emoji menu: ${err.message}`); - }); + it('handles the special case where "You" is not cleanly comma seperated', function() { + const awardUrl = awardsHandler.getAwardUrl(); + const $votesBlock = $('.js-awards-block').eq(0); + const $thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent(); + $thumbsUpEmoji.attr('data-title', 'You and sam'); + $thumbsUpEmoji.addClass('active'); + awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false); + $thumbsUpEmoji.tooltip(); + + expect($thumbsUpEmoji.data('originalTitle')).toBe('sam'); + }); + }); + + describe('::searchEmojis', () => { + it('should filter the emoji', function(done) { + openAndWaitForEmojiMenu() + .then(() => { + expect($('[data-name=angel]').is(':visible')).toBe(true); + expect($('[data-name=anger]').is(':visible')).toBe(true); + awardsHandler.searchEmojis('ali'); + + expect($('[data-name=angel]').is(':visible')).toBe(false); + expect($('[data-name=anger]').is(':visible')).toBe(false); + expect($('[data-name=alien]').is(':visible')).toBe(true); + expect($('.js-emoji-menu-search').val()).toBe('ali'); + }) + .then(done) + .catch(err => { + done.fail(`Failed to open and build emoji menu: ${err.message}`); + }); + }); + + it('should clear the search when searching for nothing', function(done) { + openAndWaitForEmojiMenu() + .then(() => { + awardsHandler.searchEmojis('ali'); + + expect($('[data-name=angel]').is(':visible')).toBe(false); + expect($('[data-name=anger]').is(':visible')).toBe(false); + expect($('[data-name=alien]').is(':visible')).toBe(true); + awardsHandler.searchEmojis(''); + + expect($('[data-name=angel]').is(':visible')).toBe(true); + expect($('[data-name=anger]').is(':visible')).toBe(true); + expect($('[data-name=alien]').is(':visible')).toBe(true); + expect($('.js-emoji-menu-search').val()).toBe(''); + }) + .then(done) + .catch(err => { + done.fail(`Failed to open and build emoji menu: ${err.message}`); + }); + }); + }); + + describe('emoji menu', function() { + const emojiSelector = '[data-name="sunglasses"]'; + const openEmojiMenuAndAddEmoji = function() { + return openAndWaitForEmojiMenu().then(() => { + const $menu = $('.emoji-menu'); + const $block = $('.js-awards-block'); + const $emoji = $menu.find(`.emoji-menu-list:not(.frequent-emojis) ${emojiSelector}`); + + expect($emoji.length).toBe(1); + expect($block.find(emojiSelector).length).toBe(0); + $emoji.click(); + + expect($menu.hasClass('.is-visible')).toBe(false); + expect($block.find(emojiSelector).length).toBe(1); }); + }; + + it('should add selected emoji to awards block', function(done) { + openEmojiMenuAndAddEmoji() + .then(done) + .catch(err => { + done.fail(`Failed to open and build emoji menu: ${err.message}`); + }); }); - describe('emoji menu', function() { - const emojiSelector = '[data-name="sunglasses"]'; - const openEmojiMenuAndAddEmoji = function() { - return openAndWaitForEmojiMenu().then(() => { - const $menu = $('.emoji-menu'); + it('should remove already selected emoji', function(done) { + openEmojiMenuAndAddEmoji() + .then(() => { + $('.js-add-award') + .eq(0) + .click(); const $block = $('.js-awards-block'); - const $emoji = $menu.find('.emoji-menu-list:not(.frequent-emojis) ' + emojiSelector); + const $emoji = $('.emoji-menu').find( + `.emoji-menu-list:not(.frequent-emojis) ${emojiSelector}`, + ); + $emoji.click(); - expect($emoji.length).toBe(1); expect($block.find(emojiSelector).length).toBe(0); - $emoji.click(); - expect($menu.hasClass('.is-visible')).toBe(false); - expect($block.find(emojiSelector).length).toBe(1); + }) + .then(done) + .catch(err => { + done.fail(`Failed to open and build emoji menu: ${err.message}`); }); - }; - it('should add selected emoji to awards block', function(done) { - return openEmojiMenuAndAddEmoji() - .then(done) - .catch(err => { - done.fail(`Failed to open and build emoji menu: ${err.message}`); - }); - }); + }); + }); + + describe('frequently used emojis', function() { + beforeEach(() => { + // Clear it out + Cookies.set('frequently_used_emojis', ''); + }); - it('should remove already selected emoji', function(done) { - return openEmojiMenuAndAddEmoji() - .then(() => { - $('.js-add-award') - .eq(0) - .click(); - const $block = $('.js-awards-block'); - const $emoji = $('.emoji-menu').find( - `.emoji-menu-list:not(.frequent-emojis) ${emojiSelector}`, - ); - $emoji.click(); - expect($block.find(emojiSelector).length).toBe(0); - }) - .then(done) - .catch(err => { - done.fail(`Failed to open and build emoji menu: ${err.message}`); + it('shouldn\'t have any "Frequently used" heading if no frequently used emojis', function(done) { + return openAndWaitForEmojiMenu() + .then(() => { + const emojiMenu = document.querySelector('.emoji-menu'); + Array.prototype.forEach.call(emojiMenu.querySelectorAll('.emoji-menu-title'), title => { + expect(title.textContent.trim().toLowerCase()).not.toBe('frequently used'); }); - }); + }) + .then(done) + .catch(err => { + done.fail(`Failed to open and build emoji menu: ${err.message}`); + }); }); - describe('frequently used emojis', function() { - beforeEach(() => { - // Clear it out - Cookies.set('frequently_used_emojis', ''); - }); + it('should have any frequently used section when there are frequently used emojis', function(done) { + awardsHandler.addEmojiToFrequentlyUsedList('8ball'); - it('shouldn\'t have any "Frequently used" heading if no frequently used emojis', function(done) { - return openAndWaitForEmojiMenu() - .then(() => { - const emojiMenu = document.querySelector('.emoji-menu'); - Array.prototype.forEach.call(emojiMenu.querySelectorAll('.emoji-menu-title'), title => { - expect(title.textContent.trim().toLowerCase()).not.toBe('frequently used'); - }); - }) - .then(done) - .catch(err => { - done.fail(`Failed to open and build emoji menu: ${err.message}`); - }); - }); + return openAndWaitForEmojiMenu() + .then(() => { + const emojiMenu = document.querySelector('.emoji-menu'); + const hasFrequentlyUsedHeading = Array.prototype.some.call( + emojiMenu.querySelectorAll('.emoji-menu-title'), + title => title.textContent.trim().toLowerCase() === 'frequently used', + ); - it('should have any frequently used section when there are frequently used emojis', function(done) { - awardsHandler.addEmojiToFrequentlyUsedList('8ball'); - - return openAndWaitForEmojiMenu() - .then(() => { - const emojiMenu = document.querySelector('.emoji-menu'); - const hasFrequentlyUsedHeading = Array.prototype.some.call( - emojiMenu.querySelectorAll('.emoji-menu-title'), - title => title.textContent.trim().toLowerCase() === 'frequently used', - ); - - expect(hasFrequentlyUsedHeading).toBe(true); - }) - .then(done) - .catch(err => { - done.fail(`Failed to open and build emoji menu: ${err.message}`); - }); - }); + expect(hasFrequentlyUsedHeading).toBe(true); + }) + .then(done) + .catch(err => { + done.fail(`Failed to open and build emoji menu: ${err.message}`); + }); + }); - it('should disregard invalid frequently used emoji that are being attempted to be added', function() { - awardsHandler.addEmojiToFrequentlyUsedList('8ball'); - awardsHandler.addEmojiToFrequentlyUsedList('invalid_emoji'); - awardsHandler.addEmojiToFrequentlyUsedList('grinning'); + it('should disregard invalid frequently used emoji that are being attempted to be added', function() { + awardsHandler.addEmojiToFrequentlyUsedList('8ball'); + awardsHandler.addEmojiToFrequentlyUsedList('invalid_emoji'); + awardsHandler.addEmojiToFrequentlyUsedList('grinning'); - expect(awardsHandler.getFrequentlyUsedEmojis()).toEqual(['8ball', 'grinning']); - }); + expect(awardsHandler.getFrequentlyUsedEmojis()).toEqual(['8ball', 'grinning']); + }); - it('should disregard invalid frequently used emoji already set in cookie', function() { - Cookies.set('frequently_used_emojis', '8ball,invalid_emoji,grinning'); + it('should disregard invalid frequently used emoji already set in cookie', function() { + Cookies.set('frequently_used_emojis', '8ball,invalid_emoji,grinning'); - expect(awardsHandler.getFrequentlyUsedEmojis()).toEqual(['8ball', 'grinning']); - }); + expect(awardsHandler.getFrequentlyUsedEmojis()).toEqual(['8ball', 'grinning']); }); }); -}.call(window)); +}); diff --git a/spec/javascripts/badges/components/badge_form_spec.js b/spec/javascripts/badges/components/badge_form_spec.js index 31195bd762b..651ac3ba3f9 100644 --- a/spec/javascripts/badges/components/badge_form_spec.js +++ b/spec/javascripts/badges/components/badge_form_spec.js @@ -66,8 +66,10 @@ describe('BadgeForm component', () => { }; const expectInvalidInput = inputElementSelector => { const inputElement = vm.$el.querySelector(inputElementSelector); + expect(inputElement).toBeMatchedBy(':invalid'); const feedbackElement = vm.$el.querySelector(`${inputElementSelector} + .invalid-feedback`); + expect(feedbackElement).toBeVisible(); }; @@ -90,6 +92,7 @@ describe('BadgeForm component', () => { submitForm(); expectInvalidInput(imageUrlSelector); + expect(vm[submitAction]).not.toHaveBeenCalled(); }); @@ -99,6 +102,7 @@ describe('BadgeForm component', () => { submitForm(); expectInvalidInput(imageUrlSelector); + expect(vm[submitAction]).not.toHaveBeenCalled(); }); @@ -108,6 +112,7 @@ describe('BadgeForm component', () => { submitForm(); expectInvalidInput(linkUrlSelector); + expect(vm[submitAction]).not.toHaveBeenCalled(); }); @@ -117,6 +122,7 @@ describe('BadgeForm component', () => { submitForm(); expectInvalidInput(linkUrlSelector); + expect(vm[submitAction]).not.toHaveBeenCalled(); }); @@ -143,8 +149,10 @@ describe('BadgeForm component', () => { it('renders one button', () => { expect(vm.$el.querySelector('.row-content-block')).toBeNull(); const buttons = vm.$el.querySelectorAll('.form-group:last-of-type button'); + expect(buttons.length).toBe(1); const buttonAddElement = buttons[0]; + expect(buttonAddElement).toBeVisible(); expect(buttonAddElement).toHaveText('Add badge'); }); @@ -165,11 +173,14 @@ describe('BadgeForm component', () => { it('renders two buttons', () => { const buttons = vm.$el.querySelectorAll('.row-content-block button'); + expect(buttons.length).toBe(2); const buttonSaveElement = buttons[0]; + expect(buttonSaveElement).toBeVisible(); expect(buttonSaveElement).toHaveText('Save changes'); const buttonCancelElement = buttons[1]; + expect(buttonCancelElement).toBeVisible(); expect(buttonCancelElement).toHaveText('Cancel'); }); diff --git a/spec/javascripts/badges/components/badge_list_row_spec.js b/spec/javascripts/badges/components/badge_list_row_spec.js index 21bd00d82f0..a5b47cc5f32 100644 --- a/spec/javascripts/badges/components/badge_list_row_spec.js +++ b/spec/javascripts/badges/components/badge_list_row_spec.js @@ -34,6 +34,7 @@ describe('BadgeListRow component', () => { it('renders the badge', () => { const badgeElement = vm.$el.querySelector('.project-badge'); + expect(badgeElement).not.toBeNull(); expect(badgeElement.getAttribute('src')).toBe(badge.renderedImageUrl); }); @@ -48,11 +49,14 @@ describe('BadgeListRow component', () => { it('shows edit and delete buttons', () => { const buttons = vm.$el.querySelectorAll('.table-button-footer button'); + expect(buttons).toHaveLength(2); const buttonEditElement = buttons[0]; + expect(buttonEditElement).toBeVisible(); expect(buttonEditElement).toHaveSpriteIcon('pencil'); const buttonDeleteElement = buttons[1]; + expect(buttonDeleteElement).toBeVisible(); expect(buttonDeleteElement).toHaveSpriteIcon('remove'); }); @@ -91,6 +95,7 @@ describe('BadgeListRow component', () => { it('hides edit and delete buttons', () => { const buttons = vm.$el.querySelectorAll('.table-button-footer button'); + expect(buttons).toHaveLength(0); }); }); diff --git a/spec/javascripts/badges/components/badge_list_spec.js b/spec/javascripts/badges/components/badge_list_spec.js index 02e59ae0843..536671db377 100644 --- a/spec/javascripts/badges/components/badge_list_spec.js +++ b/spec/javascripts/badges/components/badge_list_spec.js @@ -34,11 +34,13 @@ describe('BadgeList component', () => { it('renders a header with the badge count', () => { const header = vm.$el.querySelector('.card-header'); + expect(header).toHaveText(new RegExp(`Your badges\\s+${numberOfDummyBadges}`)); }); it('renders a row for each badge', () => { const rows = vm.$el.querySelectorAll('.gl-responsive-table-row'); + expect(rows).toHaveLength(numberOfDummyBadges); }); @@ -59,6 +61,7 @@ describe('BadgeList component', () => { Vue.nextTick() .then(() => { const loadingIcon = vm.$el.querySelector('.fa-spinner'); + expect(loadingIcon).toBeVisible(); }) .then(done) diff --git a/spec/javascripts/badges/components/badge_settings_spec.js b/spec/javascripts/badges/components/badge_settings_spec.js index 59367c85125..aca26b736ca 100644 --- a/spec/javascripts/badges/components/badge_settings_spec.js +++ b/spec/javascripts/badges/components/badge_settings_spec.js @@ -38,6 +38,7 @@ describe('BadgeSettings component', () => { $(modal).on('shown.bs.modal', () => { expect(modal).toContainText('Delete badge?'); const badgeElement = modal.querySelector('img.project-badge'); + expect(badgeElement).not.toBe(null); expect(badgeElement.getAttribute('src')).toBe(badge.renderedImageUrl); @@ -53,14 +54,17 @@ describe('BadgeSettings component', () => { it('displays a form to add a badge', () => { const form = vm.$el.querySelector('form:nth-of-type(2)'); + expect(form).not.toBe(null); const button = form.querySelector('.btn-success'); + expect(button).not.toBe(null); expect(button).toHaveText(/Add badge/); }); it('displays badge list', () => { const badgeListElement = vm.$el.querySelector('.card'); + expect(badgeListElement).not.toBe(null); expect(badgeListElement).toBeVisible(); expect(badgeListElement).toContainText('Your badges'); @@ -77,17 +81,21 @@ describe('BadgeSettings component', () => { it('displays a form to edit a badge', () => { const form = vm.$el.querySelector('form:nth-of-type(1)'); + expect(form).not.toBe(null); const submitButton = form.querySelector('.btn-success'); + expect(submitButton).not.toBe(null); expect(submitButton).toHaveText(/Save changes/); const cancelButton = form.querySelector('.btn-cancel'); + expect(cancelButton).not.toBe(null); expect(cancelButton).toHaveText(/Cancel/); }); it('displays no badge list', () => { const badgeListElement = vm.$el.querySelector('.card'); + expect(badgeListElement).toBeHidden(); }); }); @@ -102,6 +110,7 @@ describe('BadgeSettings component', () => { deleteButton.click(); const badge = store.state.badgeInModal; + expect(vm.deleteBadge).toHaveBeenCalledWith(badge); }); }); diff --git a/spec/javascripts/badges/components/badge_spec.js b/spec/javascripts/badges/components/badge_spec.js index fd1ecc9cdd8..29805408bcf 100644 --- a/spec/javascripts/badges/components/badge_spec.js +++ b/spec/javascripts/badges/components/badge_spec.js @@ -107,6 +107,7 @@ describe('Badge component', () => { expect(vm.isLoading).toBe(false); expect(vm.hasError).toBe(false); const { badgeImage, loadingIcon, reloadButton } = findElements(); + expect(badgeImage).toBeVisible(); expect(loadingIcon).toBeHidden(); expect(reloadButton).toBeHidden(); @@ -119,6 +120,7 @@ describe('Badge component', () => { Vue.nextTick() .then(() => { const { badgeImage, loadingIcon, reloadButton } = findElements(); + expect(badgeImage).toBeHidden(); expect(loadingIcon).toBeVisible(); expect(reloadButton).toBeHidden(); @@ -134,6 +136,7 @@ describe('Badge component', () => { Vue.nextTick() .then(() => { const { badgeImage, loadingIcon, reloadButton } = findElements(); + expect(badgeImage).toBeHidden(); expect(loadingIcon).toBeHidden(); expect(reloadButton).toBeVisible(); diff --git a/spec/javascripts/badges/store/actions_spec.js b/spec/javascripts/badges/store/actions_spec.js index bb6263c6de4..2623465ebd6 100644 --- a/spec/javascripts/badges/store/actions_spec.js +++ b/spec/javascripts/badges/store/actions_spec.js @@ -94,6 +94,7 @@ describe('Badges store actions', () => { link_url: badgeInAddForm.linkUrl, }), ); + expect(dispatch.calls.allArgs()).toEqual([['requestNewBadge']]); dispatch.calls.reset(); return [200, dummyResponse]; @@ -117,6 +118,7 @@ describe('Badges store actions', () => { link_url: badgeInAddForm.linkUrl, }), ); + expect(dispatch.calls.allArgs()).toEqual([['requestNewBadge']]); dispatch.calls.reset(); return [500, '']; @@ -296,6 +298,7 @@ describe('Badges store actions', () => { .loadBadges({ state, dispatch }, dummyData) .then(() => { const badges = dummyReponse.map(transformBackendBadge); + expect(dispatch.calls.allArgs()).toEqual([['receiveLoadBadges', badges]]); }) .then(done) @@ -416,6 +419,7 @@ describe('Badges store actions', () => { .then(() => { expect(axios.get.calls.count()).toBe(1); const url = axios.get.calls.argsFor(0)[0]; + expect(url).toMatch(`^${dummyEndpointUrl}/render?`); expect(url).toMatch('\\?link_url=%3Cscript%3EI%20am%20dangerous!%3C%2Fscript%3E&'); expect(url).toMatch('&image_url=%26make-sandwhich%3Dtrue$'); @@ -436,6 +440,7 @@ describe('Badges store actions', () => { .renderBadge({ state, dispatch }) .then(() => { const renderedBadge = transformBackendBadge(dummyReponse); + expect(dispatch.calls.allArgs()).toEqual([['receiveRenderedBadge', renderedBadge]]); }) .then(done) @@ -525,6 +530,7 @@ describe('Badges store actions', () => { link_url: badgeInEditForm.linkUrl, }), ); + expect(dispatch.calls.allArgs()).toEqual([['requestUpdatedBadge']]); dispatch.calls.reset(); return [200, dummyResponse]; @@ -548,6 +554,7 @@ describe('Badges store actions', () => { link_url: badgeInEditForm.linkUrl, }), ); + expect(dispatch.calls.allArgs()).toEqual([['requestUpdatedBadge']]); dispatch.calls.reset(); return [500, '']; diff --git a/spec/javascripts/behaviors/autosize_spec.js b/spec/javascripts/behaviors/autosize_spec.js index c411c5174fb..59abae479d4 100644 --- a/spec/javascripts/behaviors/autosize_spec.js +++ b/spec/javascripts/behaviors/autosize_spec.js @@ -12,6 +12,7 @@ describe('Autosize behavior', () => { it('does not overwrite the resize property', () => { load(); + expect($('textarea')).toHaveCss({ resize: 'vertical', }); diff --git a/spec/javascripts/behaviors/bind_in_out_spec.js b/spec/javascripts/behaviors/bind_in_out_spec.js index 5ff66167718..0c214f5886a 100644 --- a/spec/javascripts/behaviors/bind_in_out_spec.js +++ b/spec/javascripts/behaviors/bind_in_out_spec.js @@ -1,60 +1,60 @@ import BindInOut from '~/behaviors/bind_in_out'; import ClassSpecHelper from '../helpers/class_spec_helper'; -describe('BindInOut', function () { - describe('constructor', function () { - beforeEach(function () { +describe('BindInOut', function() { + describe('constructor', function() { + beforeEach(function() { this.in = {}; this.out = {}; this.bindInOut = new BindInOut(this.in, this.out); }); - it('should set .in', function () { + it('should set .in', function() { expect(this.bindInOut.in).toBe(this.in); }); - it('should set .out', function () { + it('should set .out', function() { expect(this.bindInOut.out).toBe(this.out); }); - it('should set .eventWrapper', function () { + it('should set .eventWrapper', function() { expect(this.bindInOut.eventWrapper).toEqual({}); }); - describe('if .in is an input', function () { - beforeEach(function () { + describe('if .in is an input', function() { + beforeEach(function() { this.bindInOut = new BindInOut({ tagName: 'INPUT' }); }); - it('should set .eventType to keyup ', function () { + it('should set .eventType to keyup ', function() { expect(this.bindInOut.eventType).toEqual('keyup'); }); }); - describe('if .in is a textarea', function () { - beforeEach(function () { + describe('if .in is a textarea', function() { + beforeEach(function() { this.bindInOut = new BindInOut({ tagName: 'TEXTAREA' }); }); - it('should set .eventType to keyup ', function () { + it('should set .eventType to keyup ', function() { expect(this.bindInOut.eventType).toEqual('keyup'); }); }); - describe('if .in is not an input or textarea', function () { - beforeEach(function () { + describe('if .in is not an input or textarea', function() { + beforeEach(function() { this.bindInOut = new BindInOut({ tagName: 'SELECT' }); }); - it('should set .eventType to change ', function () { + it('should set .eventType to change ', function() { expect(this.bindInOut.eventType).toEqual('change'); }); }); }); - describe('addEvents', function () { - beforeEach(function () { + describe('addEvents', function() { + beforeEach(function() { this.in = jasmine.createSpyObj('in', ['addEventListener']); this.bindInOut = new BindInOut(this.in); @@ -62,25 +62,24 @@ describe('BindInOut', function () { this.addEvents = this.bindInOut.addEvents(); }); - it('should set .eventWrapper.updateOut', function () { + it('should set .eventWrapper.updateOut', function() { expect(this.bindInOut.eventWrapper.updateOut).toEqual(jasmine.any(Function)); }); - it('should call .addEventListener', function () { - expect(this.in.addEventListener) - .toHaveBeenCalledWith( - this.bindInOut.eventType, - this.bindInOut.eventWrapper.updateOut, - ); + it('should call .addEventListener', function() { + expect(this.in.addEventListener).toHaveBeenCalledWith( + this.bindInOut.eventType, + this.bindInOut.eventWrapper.updateOut, + ); }); - it('should return the instance', function () { + it('should return the instance', function() { expect(this.addEvents).toBe(this.bindInOut); }); }); - describe('updateOut', function () { - beforeEach(function () { + describe('updateOut', function() { + beforeEach(function() { this.in = { value: 'the-value' }; this.out = { textContent: 'not-the-value' }; @@ -89,17 +88,17 @@ describe('BindInOut', function () { this.updateOut = this.bindInOut.updateOut(); }); - it('should set .out.textContent to .in.value', function () { + it('should set .out.textContent to .in.value', function() { expect(this.out.textContent).toBe(this.in.value); }); - it('should return the instance', function () { + it('should return the instance', function() { expect(this.updateOut).toBe(this.bindInOut); }); }); - describe('removeEvents', function () { - beforeEach(function () { + describe('removeEvents', function() { + beforeEach(function() { this.in = jasmine.createSpyObj('in', ['removeEventListener']); this.updateOut = () => {}; @@ -109,21 +108,20 @@ describe('BindInOut', function () { this.removeEvents = this.bindInOut.removeEvents(); }); - it('should call .removeEventListener', function () { - expect(this.in.removeEventListener) - .toHaveBeenCalledWith( - this.bindInOut.eventType, - this.updateOut, - ); + it('should call .removeEventListener', function() { + expect(this.in.removeEventListener).toHaveBeenCalledWith( + this.bindInOut.eventType, + this.updateOut, + ); }); - it('should return the instance', function () { + it('should return the instance', function() { expect(this.removeEvents).toBe(this.bindInOut); }); }); - describe('initAll', function () { - beforeEach(function () { + describe('initAll', function() { + beforeEach(function() { this.ins = [0, 1, 2]; this.instances = []; @@ -136,43 +134,47 @@ describe('BindInOut', function () { ClassSpecHelper.itShouldBeAStaticMethod(BindInOut, 'initAll'); - it('should call .querySelectorAll', function () { + it('should call .querySelectorAll', function() { expect(document.querySelectorAll).toHaveBeenCalledWith('*[data-bind-in]'); }); - it('should call .map', function () { + it('should call .map', function() { expect(Array.prototype.map).toHaveBeenCalledWith(jasmine.any(Function)); }); - it('should call .init for each element', function () { + it('should call .init for each element', function() { expect(BindInOut.init.calls.count()).toEqual(3); }); - it('should return an array of instances', function () { + it('should return an array of instances', function() { expect(this.initAll).toEqual(jasmine.any(Array)); }); }); - describe('init', function () { - beforeEach(function () { - spyOn(BindInOut.prototype, 'addEvents').and.callFake(function () { return this; }); - spyOn(BindInOut.prototype, 'updateOut').and.callFake(function () { return this; }); + describe('init', function() { + beforeEach(function() { + spyOn(BindInOut.prototype, 'addEvents').and.callFake(function() { + return this; + }); + spyOn(BindInOut.prototype, 'updateOut').and.callFake(function() { + return this; + }); this.init = BindInOut.init({}, {}); }); ClassSpecHelper.itShouldBeAStaticMethod(BindInOut, 'init'); - it('should call .addEvents', function () { + it('should call .addEvents', function() { expect(BindInOut.prototype.addEvents).toHaveBeenCalled(); }); - it('should call .updateOut', function () { + it('should call .updateOut', function() { expect(BindInOut.prototype.updateOut).toHaveBeenCalled(); }); - describe('if no anOut is provided', function () { - beforeEach(function () { + describe('if no anOut is provided', function() { + beforeEach(function() { this.anIn = { dataset: { bindIn: 'the-data-bind-in' } }; spyOn(document, 'querySelector'); @@ -180,9 +182,10 @@ describe('BindInOut', function () { BindInOut.init(this.anIn); }); - it('should call .querySelector', function () { - expect(document.querySelector) - .toHaveBeenCalledWith(`*[data-bind-out="${this.anIn.dataset.bindIn}"]`); + it('should call .querySelector', function() { + expect(document.querySelector).toHaveBeenCalledWith( + `*[data-bind-out="${this.anIn.dataset.bindIn}"]`, + ); }); }); }); diff --git a/spec/javascripts/behaviors/copy_as_gfm_spec.js b/spec/javascripts/behaviors/copy_as_gfm_spec.js index c2db81c6ce4..cf8c1b77861 100644 --- a/spec/javascripts/behaviors/copy_as_gfm_spec.js +++ b/spec/javascripts/behaviors/copy_as_gfm_spec.js @@ -29,6 +29,7 @@ describe('CopyAsGFM', () => { it('wraps pasted code when not already in code tags', () => { spyOn(window.gl.utils, 'insertText').and.callFake((el, textFunc) => { const insertedText = textFunc('This is code: ', ''); + expect(insertedText).toEqual('`code`'); }); @@ -38,6 +39,7 @@ describe('CopyAsGFM', () => { it('does not wrap pasted code when already in code tags', () => { spyOn(window.gl.utils, 'insertText').and.callFake((el, textFunc) => { const insertedText = textFunc('This is code: `', '`'); + expect(insertedText).toEqual('code'); }); @@ -54,7 +56,7 @@ describe('CopyAsGFM', () => { const fragment = document.createDocumentFragment(); const node = document.createElement('div'); node.innerHTML = html; - Array.from(node.childNodes).forEach((item) => fragment.appendChild(item)); + Array.from(node.childNodes).forEach(item => fragment.appendChild(item)); return fragment; }, }), @@ -86,6 +88,7 @@ describe('CopyAsGFM', () => { simulateCopy(); const expectedGFM = '- List Item1\n- List Item2'; + expect(clipboardData.setData).toHaveBeenCalledWith('text/x-gfm', expectedGFM); }); @@ -95,6 +98,7 @@ describe('CopyAsGFM', () => { simulateCopy(); const expectedGFM = '1. List Item1\n1. List Item2'; + expect(clipboardData.setData).toHaveBeenCalledWith('text/x-gfm', expectedGFM); }); }); diff --git a/spec/javascripts/behaviors/gl_emoji/unicode_support_map_spec.js b/spec/javascripts/behaviors/gl_emoji/unicode_support_map_spec.js index f96f20ed4a5..f656b97fec2 100644 --- a/spec/javascripts/behaviors/gl_emoji/unicode_support_map_spec.js +++ b/spec/javascripts/behaviors/gl_emoji/unicode_support_map_spec.js @@ -13,7 +13,7 @@ describe('Unicode Support Map', () => { spyOn(JSON, 'stringify').and.returnValue(stringSupportMap); }); - describe('if isLocalStorageAvailable is `true`', function () { + describe('if isLocalStorageAvailable is `true`', function() { beforeEach(() => { AccessorUtilities.isLocalStorageAccessSafe.and.returnValue(true); @@ -36,7 +36,7 @@ describe('Unicode Support Map', () => { }); }); - describe('if isLocalStorageAvailable is `false`', function () { + describe('if isLocalStorageAvailable is `false`', function() { beforeEach(() => { AccessorUtilities.isLocalStorageAccessSafe.and.returnValue(false); diff --git a/spec/javascripts/behaviors/quick_submit_spec.js b/spec/javascripts/behaviors/quick_submit_spec.js index b3a5eac8982..681463aab66 100644 --- a/spec/javascripts/behaviors/quick_submit_spec.js +++ b/spec/javascripts/behaviors/quick_submit_spec.js @@ -1,7 +1,7 @@ import $ from 'jquery'; import '~/behaviors/quick_submit'; -describe('Quick Submit behavior', function () { +describe('Quick Submit behavior', function() { const keydownEvent = (options = { keyCode: 13, metaKey: true }) => $.Event('keydown', options); preloadFixtures('snippets/show.html.raw'); @@ -30,6 +30,7 @@ describe('Quick Submit behavior', function () { keyCode: 32, }), ); + expect(this.spies.submit).not.toHaveBeenTriggered(); }); @@ -40,6 +41,7 @@ describe('Quick Submit behavior', function () { metaKey: false, }), ); + expect(this.spies.submit).not.toHaveBeenTriggered(); }); @@ -49,6 +51,7 @@ describe('Quick Submit behavior', function () { repeat: true, }), ); + expect(this.spies.submit).not.toHaveBeenTriggered(); }); @@ -86,7 +89,8 @@ describe('Quick Submit behavior', function () { describe('In Macintosh', () => { it('responds to Meta+Enter', () => { this.textarea.trigger(keydownEvent()); - return expect(this.spies.submit).toHaveBeenTriggered(); + + expect(this.spies.submit).toHaveBeenTriggered(); }); it('excludes other modifier keys', () => { @@ -105,13 +109,15 @@ describe('Quick Submit behavior', function () { shiftKey: true, }), ); - return expect(this.spies.submit).not.toHaveBeenTriggered(); + + expect(this.spies.submit).not.toHaveBeenTriggered(); }); }); } else { it('responds to Ctrl+Enter', () => { this.textarea.trigger(keydownEvent()); - return expect(this.spies.submit).toHaveBeenTriggered(); + + expect(this.spies.submit).toHaveBeenTriggered(); }); it('excludes other modifier keys', () => { @@ -130,7 +136,8 @@ describe('Quick Submit behavior', function () { shiftKey: true, }), ); - return expect(this.spies.submit).not.toHaveBeenTriggered(); + + expect(this.spies.submit).not.toHaveBeenTriggered(); }); } }); diff --git a/spec/javascripts/behaviors/requires_input_spec.js b/spec/javascripts/behaviors/requires_input_spec.js index a434949b9da..1bde2bb3024 100644 --- a/spec/javascripts/behaviors/requires_input_spec.js +++ b/spec/javascripts/behaviors/requires_input_spec.js @@ -12,33 +12,51 @@ describe('requiresInput', () => { it('disables submit when any field is required', () => { $('.js-requires-input').requiresInput(); + expect(submitButton).toBeDisabled(); }); it('enables submit when no field is required', () => { $('*[required=required]').prop('required', false); $('.js-requires-input').requiresInput(); + expect(submitButton).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(); + $('#required1') + .val('input1') + .change(); + expect(submitButton).toBeDisabled(); - $('#optional1').val('input1').change(); + $('#optional1') + .val('input1') + .change(); + expect(submitButton).toBeDisabled(); - $('#required2').val('input2').change(); - $('#required3').val('input3').change(); - $('#required4').val('input4').change(); - $('#required5').val('1').change(); + $('#required2') + .val('input2') + .change(); + $('#required3') + .val('input3') + .change(); + $('#required4') + .val('input4') + .change(); + $('#required5') + .val('1') + .change(); + expect($('.submit')).not.toBeDisabled(); }); }); diff --git a/spec/javascripts/behaviors/secret_values_spec.js b/spec/javascripts/behaviors/secret_values_spec.js index 95122fcf30f..5aaab093c0c 100644 --- a/spec/javascripts/behaviors/secret_values_spec.js +++ b/spec/javascripts/behaviors/secret_values_spec.js @@ -36,12 +36,7 @@ function setupSecretFixture( placeholderClass = 'js-secret-value-placeholder', ) { const wrapper = document.createElement('div'); - wrapper.innerHTML = generateFixtureMarkup( - secrets, - isRevealed, - valueClass, - placeholderClass, - ); + wrapper.innerHTML = generateFixtureMarkup(secrets, isRevealed, valueClass, placeholderClass); const secretValues = new SecretValues({ container: wrapper.querySelector('.js-secret-container'), @@ -127,11 +122,12 @@ describe('setupSecretValues', () => { const placeholders = wrapper.querySelectorAll('.js-secret-value-placeholder'); expect(values.length).toEqual(3); - values.forEach((value) => { + values.forEach(value => { expect(value.classList.contains('hide')).toEqual(true); }); + expect(placeholders.length).toEqual(3); - placeholders.forEach((placeholder) => { + placeholders.forEach(placeholder => { expect(placeholder.classList.contains('hide')).toEqual(false); }); }); @@ -145,22 +141,24 @@ describe('setupSecretValues', () => { revealButton.click(); expect(values.length).toEqual(3); - values.forEach((value) => { + values.forEach(value => { expect(value.classList.contains('hide')).toEqual(false); }); + expect(placeholders.length).toEqual(3); - placeholders.forEach((placeholder) => { + placeholders.forEach(placeholder => { expect(placeholder.classList.contains('hide')).toEqual(true); }); revealButton.click(); expect(values.length).toEqual(3); - values.forEach((value) => { + values.forEach(value => { expect(value.classList.contains('hide')).toEqual(true); }); + expect(placeholders.length).toEqual(3); - placeholders.forEach((placeholder) => { + placeholders.forEach(placeholder => { expect(placeholder.classList.contains('hide')).toEqual(false); }); }); @@ -172,7 +170,9 @@ describe('setupSecretValues', () => { it('should toggle values and placeholders', () => { const wrapper = setupSecretFixture(secrets, false); // Insert the new dynamic row - wrapper.querySelector('.js-secret-container').insertAdjacentHTML('afterbegin', generateValueMarkup('foobarbazdynamic')); + wrapper + .querySelector('.js-secret-container') + .insertAdjacentHTML('afterbegin', generateValueMarkup('foobarbazdynamic')); const revealButton = wrapper.querySelector('.js-secret-value-reveal-button'); const values = wrapper.querySelectorAll('.js-secret-value'); @@ -181,22 +181,24 @@ describe('setupSecretValues', () => { revealButton.click(); expect(values.length).toEqual(4); - values.forEach((value) => { + values.forEach(value => { expect(value.classList.contains('hide')).toEqual(false); }); + expect(placeholders.length).toEqual(4); - placeholders.forEach((placeholder) => { + placeholders.forEach(placeholder => { expect(placeholder.classList.contains('hide')).toEqual(true); }); revealButton.click(); expect(values.length).toEqual(4); - values.forEach((value) => { + values.forEach(value => { expect(value.classList.contains('hide')).toEqual(true); }); + expect(placeholders.length).toEqual(4); - placeholders.forEach((placeholder) => { + placeholders.forEach(placeholder => { expect(placeholder.classList.contains('hide')).toEqual(false); }); }); diff --git a/spec/javascripts/behaviors/shortcuts/shortcuts_issuable_spec.js b/spec/javascripts/behaviors/shortcuts/shortcuts_issuable_spec.js index 01b5bc112b2..bc25549cbed 100644 --- a/spec/javascripts/behaviors/shortcuts/shortcuts_issuable_spec.js +++ b/spec/javascripts/behaviors/shortcuts/shortcuts_issuable_spec.js @@ -57,9 +57,11 @@ describe('ShortcutsIssuable', function() { it('leaves existing input intact', () => { $(FORM_SELECTOR).val('This text was already here.'); + expect($(FORM_SELECTOR).val()).toBe('This text was already here.'); ShortcutsIssuable.replyWithSelectedText(true); + expect($(FORM_SELECTOR).val()).toBe('This text was already here.\n\n> Selected text.\n\n'); }); @@ -70,6 +72,7 @@ describe('ShortcutsIssuable', function() { }); ShortcutsIssuable.replyWithSelectedText(true); + expect(triggered).toBe(true); }); diff --git a/spec/javascripts/blob/3d_viewer/mesh_object_spec.js b/spec/javascripts/blob/3d_viewer/mesh_object_spec.js index 7651792be2e..60be285039f 100644 --- a/spec/javascripts/blob/3d_viewer/mesh_object_spec.js +++ b/spec/javascripts/blob/3d_viewer/mesh_object_spec.js @@ -1,21 +1,15 @@ -import { - BoxGeometry, -} from 'three/build/three.module'; +import { BoxGeometry } from 'three/build/three.module'; import MeshObject from '~/blob/3d_viewer/mesh_object'; describe('Mesh object', () => { it('defaults to non-wireframe material', () => { - const object = new MeshObject( - new BoxGeometry(10, 10, 10), - ); + const object = new MeshObject(new BoxGeometry(10, 10, 10)); expect(object.material.wireframe).toBeFalsy(); }); it('changes to wirefame material', () => { - const object = new MeshObject( - new BoxGeometry(10, 10, 10), - ); + const object = new MeshObject(new BoxGeometry(10, 10, 10)); object.changeMaterial('wireframe'); @@ -23,18 +17,14 @@ describe('Mesh object', () => { }); it('scales object down', () => { - const object = new MeshObject( - new BoxGeometry(10, 10, 10), - ); + const object = new MeshObject(new BoxGeometry(10, 10, 10)); const { radius } = object.geometry.boundingSphere; expect(radius).not.toBeGreaterThan(4); }); it('does not scale object down', () => { - const object = new MeshObject( - new BoxGeometry(1, 1, 1), - ); + const object = new MeshObject(new BoxGeometry(1, 1, 1)); const { radius } = object.geometry.boundingSphere; expect(radius).toBeLessThan(1); diff --git a/spec/javascripts/blob/balsamiq/balsamiq_viewer_integration_spec.js b/spec/javascripts/blob/balsamiq/balsamiq_viewer_integration_spec.js index c726fa8e428..5f027f59fcf 100644 --- a/spec/javascripts/blob/balsamiq/balsamiq_viewer_integration_spec.js +++ b/spec/javascripts/blob/balsamiq/balsamiq_viewer_integration_spec.js @@ -16,10 +16,13 @@ describe('Balsamiq integration spec', () => { }); describe('successful response', () => { - beforeEach((done) => { + beforeEach(done => { endpoint = bmprPath; - balsamiqViewer.loadFile(endpoint).then(done).catch(done.fail); + balsamiqViewer + .loadFile(endpoint) + .then(done) + .catch(done.fail); }); it('does not show loading icon', () => { @@ -32,10 +35,13 @@ describe('Balsamiq integration spec', () => { }); describe('error getting file', () => { - beforeEach((done) => { + beforeEach(done => { endpoint = 'invalid/path/to/file.bmpr'; - balsamiqViewer.loadFile(endpoint).then(done.fail, null).catch(done); + balsamiqViewer + .loadFile(endpoint) + .then(done.fail, null) + .catch(done); }); it('does not show loading icon', () => { diff --git a/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js b/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js index cb0f2ba686d..fd73fb4bfcc 100644 --- a/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js +++ b/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js @@ -18,10 +18,6 @@ describe('BalsamiqViewer', () => { }); }); - describe('fileLoaded', () => { - - }); - describe('loadFile', () => { let xhr; let loadFile; @@ -64,12 +60,16 @@ describe('BalsamiqViewer', () => { viewer = jasmine.createSpyObj('viewer', ['appendChild']); previews = [document.createElement('ul'), document.createElement('ul')]; - balsamiqViewer = jasmine.createSpyObj('balsamiqViewer', ['initDatabase', 'getPreviews', 'renderPreview']); + balsamiqViewer = jasmine.createSpyObj('balsamiqViewer', [ + 'initDatabase', + 'getPreviews', + 'renderPreview', + ]); balsamiqViewer.viewer = viewer; balsamiqViewer.getPreviews.and.returnValue(previews); balsamiqViewer.renderPreview.and.callFake(preview => preview); - viewer.appendChild.and.callFake((containerElement) => { + viewer.appendChild.and.callFake(containerElement => { container = containerElement; }); @@ -198,7 +198,9 @@ describe('BalsamiqViewer', () => { }); it('should call database.exec', () => { - expect(database.exec).toHaveBeenCalledWith(`SELECT * FROM resources WHERE id = '${resourceID}'`); + expect(database.exec).toHaveBeenCalledWith( + `SELECT * FROM resources WHERE id = '${resourceID}'`, + ); }); it('should return the selected resource', () => { @@ -281,7 +283,7 @@ describe('BalsamiqViewer', () => { expect(BalsamiqViewer.parseTitle).toHaveBeenCalledWith(resource); }); - it('should return the template string', function () { + it('should return the template string', function() { expect(renderTemplate.replace(/\s/g, '')).toEqual(template.replace(/\s/g, '')); }); }); diff --git a/spec/javascripts/blob/blob_file_dropzone_spec.js b/spec/javascripts/blob/blob_file_dropzone_spec.js index 346f795c3f5..432d8a65b0a 100644 --- a/spec/javascripts/blob/blob_file_dropzone_spec.js +++ b/spec/javascripts/blob/blob_file_dropzone_spec.js @@ -1,7 +1,7 @@ import $ from 'jquery'; import BlobFileDropzone from '~/blob/blob_file_dropzone'; -describe('BlobFileDropzone', function () { +describe('BlobFileDropzone', function() { preloadFixtures('blob/show.html.raw'); beforeEach(() => { diff --git a/spec/javascripts/blob/blob_fork_suggestion_spec.js b/spec/javascripts/blob/blob_fork_suggestion_spec.js index d1ab0a32f85..9b81b7e6f92 100644 --- a/spec/javascripts/blob/blob_fork_suggestion_spec.js +++ b/spec/javascripts/blob/blob_fork_suggestion_spec.js @@ -16,8 +16,7 @@ describe('BlobForkSuggestion', () => { cancelButtons: cancelButton, suggestionSections: suggestionSection, actionTextPieces: actionTextPiece, - }) - .init(); + }).init(); }); afterEach(() => { @@ -26,6 +25,7 @@ describe('BlobForkSuggestion', () => { it('showSuggestionSection', () => { blobForkSuggestion.showSuggestionSection('/foo', 'foo'); + expect(suggestionSection.classList.contains('hidden')).toEqual(false); expect(forkButton.getAttribute('href')).toEqual('/foo'); expect(actionTextPiece.textContent).toEqual('foo'); @@ -33,6 +33,7 @@ describe('BlobForkSuggestion', () => { it('hideSuggestionSection', () => { blobForkSuggestion.hideSuggestionSection(); + expect(suggestionSection.classList.contains('hidden')).toEqual(true); }); }); diff --git a/spec/javascripts/blob/notebook/index_spec.js b/spec/javascripts/blob/notebook/index_spec.js index 80c09a544d6..28d3b2f5ea3 100644 --- a/spec/javascripts/blob/notebook/index_spec.js +++ b/spec/javascripts/blob/notebook/index_spec.js @@ -12,29 +12,27 @@ describe('iPython notebook renderer', () => { it('shows loading icon', () => { renderNotebook(); - expect( - document.querySelector('.loading'), - ).not.toBeNull(); + expect(document.querySelector('.loading')).not.toBeNull(); }); describe('successful response', () => { let mock; - beforeEach((done) => { + beforeEach(done => { mock = new MockAdapter(axios); mock.onGet('/test').reply(200, { - cells: [{ - cell_type: 'markdown', - source: ['# test'], - }, { - cell_type: 'code', - execution_count: 1, - source: [ - 'def test(str)', - ' return str', - ], - outputs: [], - }], + cells: [ + { + cell_type: 'markdown', + source: ['# test'], + }, + { + cell_type: 'code', + execution_count: 1, + source: ['def test(str)', ' return str'], + outputs: [], + }, + ], }); renderNotebook(); @@ -49,35 +47,23 @@ describe('iPython notebook renderer', () => { }); it('does not show loading icon', () => { - expect( - document.querySelector('.loading'), - ).toBeNull(); + expect(document.querySelector('.loading')).toBeNull(); }); it('renders the notebook', () => { - expect( - document.querySelector('.md'), - ).not.toBeNull(); + expect(document.querySelector('.md')).not.toBeNull(); }); it('renders the markdown cell', () => { - expect( - document.querySelector('h1'), - ).not.toBeNull(); + expect(document.querySelector('h1')).not.toBeNull(); - expect( - document.querySelector('h1').textContent.trim(), - ).toBe('test'); + expect(document.querySelector('h1').textContent.trim()).toBe('test'); }); it('highlights code', () => { - expect( - document.querySelector('.token'), - ).not.toBeNull(); + expect(document.querySelector('.token')).not.toBeNull(); - expect( - document.querySelector('.language-python'), - ).not.toBeNull(); + expect(document.querySelector('.language-python')).not.toBeNull(); }); }); @@ -86,12 +72,10 @@ describe('iPython notebook renderer', () => { beforeEach(done => { mock = new MockAdapter(axios); - mock - .onGet('/test') - .reply(() => - // eslint-disable-next-line prefer-promise-reject-errors - Promise.reject({ status: 200, data: '{ "cells": [{"cell_type": "markdown"} }' }), - ); + mock.onGet('/test').reply(() => + // eslint-disable-next-line prefer-promise-reject-errors + Promise.reject({ status: 200, data: '{ "cells": [{"cell_type": "markdown"} }' }), + ); renderNotebook(); @@ -105,22 +89,20 @@ describe('iPython notebook renderer', () => { }); it('does not show loading icon', () => { - expect( - document.querySelector('.loading'), - ).toBeNull(); + expect(document.querySelector('.loading')).toBeNull(); }); it('shows error message', () => { - expect( - document.querySelector('.md').textContent.trim(), - ).toBe('An error occurred whilst parsing the file.'); + expect(document.querySelector('.md').textContent.trim()).toBe( + 'An error occurred whilst parsing the file.', + ); }); }); describe('error getting file', () => { let mock; - beforeEach((done) => { + beforeEach(done => { mock = new MockAdapter(axios); mock.onGet('/test').reply(500, ''); @@ -136,15 +118,13 @@ describe('iPython notebook renderer', () => { }); it('does not show loading icon', () => { - expect( - document.querySelector('.loading'), - ).toBeNull(); + expect(document.querySelector('.loading')).toBeNull(); }); it('shows error message', () => { - expect( - document.querySelector('.md').textContent.trim(), - ).toBe('An error occurred whilst loading the file. Please try again later.'); + expect(document.querySelector('.md').textContent.trim()).toBe( + 'An error occurred whilst loading the file. Please try again later.', + ); }); }); }); diff --git a/spec/javascripts/blob/pdf/index_spec.js b/spec/javascripts/blob/pdf/index_spec.js index bbe2500f8e3..be917a0613f 100644 --- a/spec/javascripts/blob/pdf/index_spec.js +++ b/spec/javascripts/blob/pdf/index_spec.js @@ -5,7 +5,7 @@ describe('PDF renderer', () => { let viewer; let app; - const checkLoaded = (done) => { + const checkLoaded = done => { if (app.loading) { setTimeout(() => { checkLoaded(done); @@ -26,39 +26,31 @@ describe('PDF renderer', () => { it('shows loading icon', () => { renderPDF(); - expect( - document.querySelector('.loading'), - ).not.toBeNull(); + expect(document.querySelector('.loading')).not.toBeNull(); }); describe('successful response', () => { - beforeEach((done) => { + beforeEach(done => { app = renderPDF(); checkLoaded(done); }); it('does not show loading icon', () => { - expect( - document.querySelector('.loading'), - ).toBeNull(); + expect(document.querySelector('.loading')).toBeNull(); }); it('renders the PDF', () => { - expect( - document.querySelector('.pdf-viewer'), - ).not.toBeNull(); + expect(document.querySelector('.pdf-viewer')).not.toBeNull(); }); it('renders the PDF page', () => { - expect( - document.querySelector('.pdf-page'), - ).not.toBeNull(); + expect(document.querySelector('.pdf-page')).not.toBeNull(); }); }); describe('error getting file', () => { - beforeEach((done) => { + beforeEach(done => { viewer.dataset.endpoint = 'invalid/path/to/file.pdf'; app = renderPDF(); @@ -66,15 +58,13 @@ describe('PDF renderer', () => { }); it('does not show loading icon', () => { - expect( - document.querySelector('.loading'), - ).toBeNull(); + expect(document.querySelector('.loading')).toBeNull(); }); it('shows error message', () => { - expect( - document.querySelector('.md').textContent.trim(), - ).toBe('An error occurred whilst loading the file. Please try again later.'); + expect(document.querySelector('.md').textContent.trim()).toBe( + 'An error occurred whilst loading the file. Please try again later.', + ); }); }); }); diff --git a/spec/javascripts/blob/sketch/index_spec.js b/spec/javascripts/blob/sketch/index_spec.js index e062a068a92..2b1e81e9cbc 100644 --- a/spec/javascripts/blob/sketch/index_spec.js +++ b/spec/javascripts/blob/sketch/index_spec.js @@ -4,15 +4,13 @@ import SketchLoader from '~/blob/sketch'; describe('Sketch viewer', () => { const generateZipFileArrayBuffer = (zipFile, resolve, done) => { - zipFile - .generateAsync({ type: 'arrayBuffer' }) - .then((content) => { - resolve(content); - - setTimeout(() => { - done(); - }, 100); - }); + zipFile.generateAsync({ type: 'arrayBuffer' }).then(content => { + resolve(content); + + setTimeout(() => { + done(); + }, 100); + }); }; preloadFixtures('static/sketch_viewer.html.raw'); @@ -22,60 +20,63 @@ describe('Sketch viewer', () => { }); describe('with error message', () => { - beforeEach((done) => { - spyOn(SketchLoader.prototype, 'getZipFile').and.callFake(() => new Promise((resolve, reject) => { - reject(); - - setTimeout(() => { - done(); - }); - })); + beforeEach(done => { + spyOn(SketchLoader.prototype, 'getZipFile').and.callFake( + () => + new Promise((resolve, reject) => { + reject(); + + setTimeout(() => { + done(); + }); + }), + ); new SketchLoader(document.getElementById('js-sketch-viewer')); }); it('renders error message', () => { - expect( - document.querySelector('#js-sketch-viewer p'), - ).not.toBeNull(); + expect(document.querySelector('#js-sketch-viewer p')).not.toBeNull(); - expect( - document.querySelector('#js-sketch-viewer p').textContent.trim(), - ).toContain('Cannot show preview.'); + expect(document.querySelector('#js-sketch-viewer p').textContent.trim()).toContain( + 'Cannot show preview.', + ); }); it('removes render the loading icon', () => { - expect( - document.querySelector('.js-loading-icon'), - ).toBeNull(); + expect(document.querySelector('.js-loading-icon')).toBeNull(); }); }); describe('success', () => { - beforeEach((done) => { - spyOn(SketchLoader.prototype, 'getZipFile').and.callFake(() => new Promise((resolve) => { - const zipFile = new JSZip(); - zipFile.folder('previews') - .file('preview.png', 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAMAAAAoyzS7AAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAA1JREFUeNoBAgD9/wAAAAIAAVMrnDAAAAAASUVORK5CYII=', { - base64: true, - }); - - generateZipFileArrayBuffer(zipFile, resolve, done); - })); + beforeEach(done => { + spyOn(SketchLoader.prototype, 'getZipFile').and.callFake( + () => + new Promise(resolve => { + const zipFile = new JSZip(); + zipFile + .folder('previews') + .file( + 'preview.png', + 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAMAAAAoyzS7AAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAA1JREFUeNoBAgD9/wAAAAIAAVMrnDAAAAAASUVORK5CYII=', + { + base64: true, + }, + ); + + generateZipFileArrayBuffer(zipFile, resolve, done); + }), + ); new SketchLoader(document.getElementById('js-sketch-viewer')); }); it('does not render error message', () => { - expect( - document.querySelector('#js-sketch-viewer p'), - ).toBeNull(); + expect(document.querySelector('#js-sketch-viewer p')).toBeNull(); }); it('removes render the loading icon', () => { - expect( - document.querySelector('.js-loading-icon'), - ).toBeNull(); + expect(document.querySelector('.js-loading-icon')).toBeNull(); }); it('renders preview img', () => { @@ -95,24 +96,25 @@ describe('Sketch viewer', () => { }); describe('incorrect file', () => { - beforeEach((done) => { - spyOn(SketchLoader.prototype, 'getZipFile').and.callFake(() => new Promise((resolve) => { - const zipFile = new JSZip(); + beforeEach(done => { + spyOn(SketchLoader.prototype, 'getZipFile').and.callFake( + () => + new Promise(resolve => { + const zipFile = new JSZip(); - generateZipFileArrayBuffer(zipFile, resolve, done); - })); + generateZipFileArrayBuffer(zipFile, resolve, done); + }), + ); new SketchLoader(document.getElementById('js-sketch-viewer')); }); it('renders error message', () => { - expect( - document.querySelector('#js-sketch-viewer p'), - ).not.toBeNull(); + expect(document.querySelector('#js-sketch-viewer p')).not.toBeNull(); - expect( - document.querySelector('#js-sketch-viewer p').textContent.trim(), - ).toContain('Cannot show preview.'); + expect(document.querySelector('#js-sketch-viewer p').textContent.trim()).toContain( + 'Cannot show preview.', + ); }); }); }); diff --git a/spec/javascripts/blob/viewer/index_spec.js b/spec/javascripts/blob/viewer/index_spec.js index 8b79624d9f4..93a942fe8d4 100644 --- a/spec/javascripts/blob/viewer/index_spec.js +++ b/spec/javascripts/blob/viewer/index_spec.js @@ -35,12 +35,13 @@ describe('Blob viewer', () => { window.location.hash = ''; }); - it('loads source file after switching views', (done) => { + it('loads source file after switching views', done => { document.querySelector('.js-blob-viewer-switch-btn[data-viewer="simple"]').click(); setTimeout(() => { expect( - document.querySelector('.js-blob-viewer-switch-btn[data-viewer="simple"]') + document + .querySelector('.js-blob-viewer-switch-btn[data-viewer="simple"]') .classList.contains('hidden'), ).toBeFalsy(); @@ -48,14 +49,15 @@ describe('Blob viewer', () => { }); }); - it('loads source file when line number is in hash', (done) => { + it('loads source file when line number is in hash', done => { window.location.hash = '#L1'; new BlobViewer(); setTimeout(() => { expect( - document.querySelector('.js-blob-viewer-switch-btn[data-viewer="simple"]') + document + .querySelector('.js-blob-viewer-switch-btn[data-viewer="simple"]') .classList.contains('hidden'), ).toBeFalsy(); @@ -63,12 +65,13 @@ describe('Blob viewer', () => { }); }); - it('doesnt reload file if already loaded', (done) => { - const asyncClick = () => new Promise((resolve) => { - document.querySelector('.js-blob-viewer-switch-btn[data-viewer="simple"]').click(); + it('doesnt reload file if already loaded', done => { + const asyncClick = () => + new Promise(resolve => { + document.querySelector('.js-blob-viewer-switch-btn[data-viewer="simple"]').click(); - setTimeout(resolve); - }); + setTimeout(resolve); + }); asyncClick() .then(() => asyncClick()) @@ -93,15 +96,13 @@ describe('Blob viewer', () => { }); it('disabled on load', () => { - expect( - copyButton.classList.contains('disabled'), - ).toBeTruthy(); + expect(copyButton.classList.contains('disabled')).toBeTruthy(); }); it('has tooltip when disabled', () => { - expect( - copyButton.getAttribute('data-original-title'), - ).toBe('Switch to the source to copy it to the clipboard'); + expect(copyButton.getAttribute('data-original-title')).toBe( + 'Switch to the source to copy it to the clipboard', + ); }); it('is blurred when clicked and disabled', () => { @@ -121,25 +122,21 @@ describe('Blob viewer', () => { expect(copyButton.blur).not.toHaveBeenCalled(); }); - it('enables after switching to simple view', (done) => { + it('enables after switching to simple view', done => { document.querySelector('.js-blob-viewer-switch-btn[data-viewer="simple"]').click(); setTimeout(() => { - expect( - copyButton.classList.contains('disabled'), - ).toBeFalsy(); + expect(copyButton.classList.contains('disabled')).toBeFalsy(); done(); }); }); - it('updates tooltip after switching to simple view', (done) => { + it('updates tooltip after switching to simple view', done => { document.querySelector('.js-blob-viewer-switch-btn[data-viewer="simple"]').click(); setTimeout(() => { - expect( - copyButton.getAttribute('data-original-title'), - ).toBe('Copy source to clipboard'); + expect(copyButton.getAttribute('data-original-title')).toBe('Copy source to clipboard'); done(); }); @@ -162,9 +159,8 @@ describe('Blob viewer', () => { blob.switchToViewer('simple'); - expect( - simpleBtn.classList.contains('active'), - ).toBeTruthy(); + expect(simpleBtn.classList.contains('active')).toBeTruthy(); + expect(simpleBtn.blur).toHaveBeenCalled(); }); diff --git a/spec/javascripts/boards/board_blank_state_spec.js b/spec/javascripts/boards/board_blank_state_spec.js index 50505b41313..8e2a947b0dd 100644 --- a/spec/javascripts/boards/board_blank_state_spec.js +++ b/spec/javascripts/boards/board_blank_state_spec.js @@ -7,29 +7,35 @@ describe('Boards blank state', () => { let vm; let fail = false; - beforeEach((done) => { + beforeEach(done => { const Comp = Vue.extend(BoardBlankState); boardsStore.create(); gl.boardService = mockBoardService(); - spyOn(gl.boardService, 'generateDefaultLists').and.callFake(() => new Promise((resolve, reject) => { - if (fail) { - reject(); - } else { - resolve({ - data: [{ - id: 1, - title: 'To Do', - label: { id: 1 }, - }, { - id: 2, - title: 'Doing', - label: { id: 2 }, - }], - }); - } - })); + spyOn(gl.boardService, 'generateDefaultLists').and.callFake( + () => + new Promise((resolve, reject) => { + if (fail) { + reject(); + } else { + resolve({ + data: [ + { + id: 1, + title: 'To Do', + label: { id: 1 }, + }, + { + id: 2, + title: 'Doing', + label: { id: 2 }, + }, + ], + }); + } + }), + ); vm = new Comp(); @@ -40,20 +46,18 @@ describe('Boards blank state', () => { }); it('renders pre-defined labels', () => { - expect( - vm.$el.querySelectorAll('.board-blank-state-list li').length, - ).toBe(2); + expect(vm.$el.querySelectorAll('.board-blank-state-list li').length).toBe(2); - expect( - vm.$el.querySelectorAll('.board-blank-state-list li')[0].textContent.trim(), - ).toEqual('To Do'); + expect(vm.$el.querySelectorAll('.board-blank-state-list li')[0].textContent.trim()).toEqual( + 'To Do', + ); - expect( - vm.$el.querySelectorAll('.board-blank-state-list li')[1].textContent.trim(), - ).toEqual('Doing'); + expect(vm.$el.querySelectorAll('.board-blank-state-list li')[1].textContent.trim()).toEqual( + 'Doing', + ); }); - it('clears blank state', (done) => { + it('clears blank state', done => { vm.$el.querySelector('.btn-default').click(); setTimeout(() => { @@ -63,7 +67,7 @@ describe('Boards blank state', () => { }); }); - it('creates pre-defined labels', (done) => { + it('creates pre-defined labels', done => { vm.$el.querySelector('.btn-success').click(); setTimeout(() => { @@ -75,7 +79,7 @@ describe('Boards blank state', () => { }); }); - it('resets the store if request fails', (done) => { + it('resets the store if request fails', done => { fail = true; vm.$el.querySelector('.btn-success').click(); diff --git a/spec/javascripts/boards/board_card_spec.js b/spec/javascripts/boards/board_card_spec.js index 20cfe426807..e1017130bed 100644 --- a/spec/javascripts/boards/board_card_spec.js +++ b/spec/javascripts/boards/board_card_spec.js @@ -18,7 +18,7 @@ describe('Board card', () => { let vm; let mock; - beforeEach((done) => { + beforeEach(done => { mock = new MockAdapter(axios); mock.onAny().reply(boardsMockInterceptor); @@ -71,7 +71,7 @@ describe('Board card', () => { expect(vm.$el.classList.contains('user-can-drag')).toBe(true); }); - it('does not add user-can-drag class disabled', (done) => { + it('does not add user-can-drag class disabled', done => { vm.disabled = true; setTimeout(() => { @@ -84,7 +84,7 @@ describe('Board card', () => { expect(vm.$el.classList.contains('is-disabled')).toBe(false); }); - it('adds disabled class is disabled is true', (done) => { + it('adds disabled class is disabled is true', done => { vm.disabled = true; setTimeout(() => { @@ -96,8 +96,23 @@ describe('Board card', () => { describe('mouse events', () => { const triggerEvent = (eventName, el = vm.$el) => { const event = document.createEvent('MouseEvents'); - event.initMouseEvent(eventName, true, true, window, 1, 0, 0, 0, 0, false, false, - false, false, 0, null); + event.initMouseEvent( + eventName, + true, + true, + window, + 1, + 0, + 0, + 0, + 0, + false, + false, + false, + false, + 0, + null, + ); el.dispatchEvent(event); }; @@ -134,13 +149,15 @@ describe('Board card', () => { expect(boardsStore.detail.issue).toEqual({}); }); - it('does not set detail issue if img is clicked', (done) => { - vm.issue.assignees = [new ListAssignee({ - id: 1, - name: 'testing 123', - username: 'test', - avatar: 'test_image', - })]; + it('does not set detail issue if img is clicked', done => { + vm.issue.assignees = [ + new ListAssignee({ + id: 1, + name: 'testing 123', + username: 'test', + avatar: 'test_image', + }), + ]; Vue.nextTick(() => { triggerEvent('mouseup', vm.$el.querySelector('img')); @@ -167,7 +184,7 @@ describe('Board card', () => { expect(boardsStore.detail.list).toEqual(vm.list); }); - it('adds active class if detail issue is set', (done) => { + it('adds active class if detail issue is set', done => { vm.detailIssue.issue = vm.issue; Vue.nextTick() diff --git a/spec/javascripts/boards/board_new_issue_spec.js b/spec/javascripts/boards/board_new_issue_spec.js index 9fea625a4ac..721d0b8172d 100644 --- a/spec/javascripts/boards/board_new_issue_spec.js +++ b/spec/javascripts/boards/board_new_issue_spec.js @@ -28,7 +28,7 @@ describe('Issue boards new issue form', () => { return vm.submit(dummySubmitEvent); }; - beforeEach((done) => { + beforeEach(done => { setFixtures('<div class="test-container"></div>'); const BoardNewIssueComp = Vue.extend(boardNewIssue); @@ -60,7 +60,7 @@ describe('Issue boards new issue form', () => { mock.restore(); }); - it('calls submit if submit button is clicked', (done) => { + it('calls submit if submit button is clicked', done => { spyOn(vm, 'submit').and.callFake(e => e.preventDefault()); vm.title = 'Testing Title'; @@ -78,7 +78,7 @@ describe('Issue boards new issue form', () => { expect(vm.$el.querySelector('.btn-success').disabled).toBe(true); }); - it('enables submit button if title is not empty', (done) => { + it('enables submit button if title is not empty', done => { vm.title = 'Testing Title'; Vue.nextTick() @@ -90,7 +90,7 @@ describe('Issue boards new issue form', () => { .catch(done.fail); }); - it('clears title after clicking cancel', (done) => { + it('clears title after clicking cancel', done => { vm.$el.querySelector('.btn-default').click(); Vue.nextTick() @@ -101,7 +101,7 @@ describe('Issue boards new issue form', () => { .catch(done.fail); }); - it('does not create new issue if title is empty', (done) => { + it('does not create new issue if title is empty', done => { submitIssue() .then(() => { expect(list.newIssue).not.toHaveBeenCalled(); @@ -111,7 +111,7 @@ describe('Issue boards new issue form', () => { }); describe('submit success', () => { - it('creates new issue', (done) => { + it('creates new issue', done => { vm.title = 'submit title'; Vue.nextTick() @@ -123,7 +123,7 @@ describe('Issue boards new issue form', () => { .catch(done.fail); }); - it('enables button after submit', (done) => { + it('enables button after submit', done => { vm.title = 'submit issue'; Vue.nextTick() @@ -135,7 +135,7 @@ describe('Issue boards new issue form', () => { .catch(done.fail); }); - it('clears title after submit', (done) => { + it('clears title after submit', done => { vm.title = 'submit issue'; Vue.nextTick() @@ -147,7 +147,7 @@ describe('Issue boards new issue form', () => { .catch(done.fail); }); - it('sets detail issue after submit', (done) => { + it('sets detail issue after submit', done => { expect(boardsStore.detail.issue.title).toBe(undefined); vm.title = 'submit issue'; @@ -160,7 +160,7 @@ describe('Issue boards new issue form', () => { .catch(done.fail); }); - it('sets detail list after submit', (done) => { + it('sets detail list after submit', done => { vm.title = 'submit issue'; Vue.nextTick() @@ -179,7 +179,7 @@ describe('Issue boards new issue form', () => { vm.title = 'error'; }); - it('removes issue', (done) => { + it('removes issue', done => { Vue.nextTick() .then(submitIssue) .then(() => { @@ -189,7 +189,7 @@ describe('Issue boards new issue form', () => { .catch(done.fail); }); - it('shows error', (done) => { + it('shows error', done => { Vue.nextTick() .then(submitIssue) .then(() => { diff --git a/spec/javascripts/boards/boards_store_spec.js b/spec/javascripts/boards/boards_store_spec.js index dfd3ea0db66..54f1edfb1f9 100644 --- a/spec/javascripts/boards/boards_store_spec.js +++ b/spec/javascripts/boards/boards_store_spec.js @@ -23,13 +23,16 @@ describe('Store', () => { gl.boardService = mockBoardService(); boardsStore.create(); - spyOn(gl.boardService, 'moveIssue').and.callFake(() => new Promise((resolve) => { - resolve(); - })); + spyOn(gl.boardService, 'moveIssue').and.callFake( + () => + new Promise(resolve => { + resolve(); + }), + ); Cookies.set('issue_board_welcome_hidden', 'false', { expires: 365 * 10, - path: '' + path: '', }); }); @@ -62,7 +65,7 @@ describe('Store', () => { expect(list).toBeDefined(); }); - it('gets issue when new list added', (done) => { + it('gets issue when new list added', done => { boardsStore.addList(listObj); const list = boardsStore.findList('id', listObj.id); @@ -75,7 +78,7 @@ describe('Store', () => { }, 0); }); - it('persists new list', (done) => { + it('persists new list', done => { boardsStore.new({ title: 'Test', list_type: 'label', @@ -83,13 +86,15 @@ describe('Store', () => { id: 1, title: 'Testing', color: 'red', - description: 'testing;' - } + description: 'testing;', + }, }); + expect(boardsStore.state.lists.length).toBe(1); setTimeout(() => { const list = boardsStore.findList('id', listObj.id); + expect(list).toBeDefined(); expect(list.id).toBe(listObj.id); expect(list.position).toBe(0); @@ -103,12 +108,13 @@ describe('Store', () => { it('check for blank state not adding', () => { boardsStore.addList(listObj); + expect(boardsStore.shouldAddBlankState()).toBe(false); }); it('check for blank state adding when closed list exist', () => { boardsStore.addList({ - list_type: 'closed' + list_type: 'closed', }); expect(boardsStore.shouldAddBlankState()).toBe(true); @@ -118,6 +124,7 @@ describe('Store', () => { boardsStore.addBlankState(); const list = boardsStore.findList('type', 'blank', 'blank'); + expect(list).toBeDefined(); }); @@ -142,7 +149,7 @@ describe('Store', () => { expect(listOne.position).toBe(1); }); - it('moves an issue from one list to another', (done) => { + it('moves an issue from one list to another', done => { const listOne = boardsStore.addList(listObj); const listTwo = boardsStore.addList(listObjDuplicate); @@ -161,7 +168,7 @@ describe('Store', () => { }, 0); }); - it('moves an issue from backlog to a list', (done) => { + it('moves an issue from backlog to a list', done => { const backlog = boardsStore.addList({ ...listObj, list_type: 'backlog', @@ -183,7 +190,7 @@ describe('Store', () => { }, 0); }); - it('moves issue to top of another list', (done) => { + it('moves issue to top of another list', done => { const listOne = boardsStore.addList(listObj); const listTwo = boardsStore.addList(listObjDuplicate); @@ -206,7 +213,7 @@ describe('Store', () => { }, 0); }); - it('moves issue to bottom of another list', (done) => { + it('moves issue to bottom of another list', done => { const listOne = boardsStore.addList(listObj); const listTwo = boardsStore.addList(listObjDuplicate); @@ -229,7 +236,7 @@ describe('Store', () => { }, 0); }); - it('moves issue in list', (done) => { + it('moves issue in list', done => { const issue = new ListIssue({ title: 'Testing', id: 2, diff --git a/spec/javascripts/boards/components/board_spec.js b/spec/javascripts/boards/components/board_spec.js index 4ebd4cecc08..d4c53bd5a7d 100644 --- a/spec/javascripts/boards/components/board_spec.js +++ b/spec/javascripts/boards/components/board_spec.js @@ -112,6 +112,6 @@ describe('Board component', () => { ).toBe(true); done(); - }); + }).catch(done.fail); }); }); diff --git a/spec/javascripts/boards/issue_spec.js b/spec/javascripts/boards/issue_spec.js index e8387068831..437ab4bb3df 100644 --- a/spec/javascripts/boards/issue_spec.js +++ b/spec/javascripts/boards/issue_spec.js @@ -21,18 +21,22 @@ describe('Issue model', () => { id: 1, iid: 1, confidential: false, - labels: [{ - id: 1, - title: 'test', - color: 'red', - description: 'testing' - }], - assignees: [{ - id: 1, - name: 'name', - username: 'username', - avatar_url: 'http://avatar_url', - }], + labels: [ + { + id: 1, + title: 'test', + color: 'red', + description: 'testing', + }, + ], + assignees: [ + { + id: 1, + name: 'name', + username: 'username', + avatar_url: 'http://avatar_url', + }, + ], }); }); @@ -45,8 +49,9 @@ describe('Issue model', () => { id: 2, title: 'bug', color: 'blue', - description: 'bugs!' + description: 'bugs!', }); + expect(issue.labels.length).toBe(2); }); @@ -55,7 +60,7 @@ describe('Issue model', () => { id: 2, title: 'test', color: 'blue', - description: 'bugs!' + description: 'bugs!', }); expect(issue.labels.length).toBe(1); @@ -63,12 +68,14 @@ describe('Issue model', () => { it('finds label', () => { const label = issue.findLabel(issue.labels[0]); + expect(label).toBeDefined(); }); it('removes label', () => { const label = issue.findLabel(issue.labels[0]); issue.removeLabel(label); + expect(issue.labels.length).toBe(0); }); @@ -77,11 +84,13 @@ describe('Issue model', () => { id: 2, title: 'bug', color: 'blue', - description: 'bugs!' + description: 'bugs!', }); + expect(issue.labels.length).toBe(2); issue.removeLabels([issue.labels[0], issue.labels[1]]); + expect(issue.labels.length).toBe(0); }); @@ -98,17 +107,20 @@ describe('Issue model', () => { it('finds assignee', () => { const assignee = issue.findAssignee(issue.assignees[0]); + expect(assignee).toBeDefined(); }); it('removes assignee', () => { const assignee = issue.findAssignee(issue.assignees[0]); issue.removeAssignee(assignee); + expect(issue.assignees.length).toBe(0); }); it('removes all assignees', () => { issue.removeAllAssignees(); + expect(issue.assignees.length).toBe(0); }); @@ -131,6 +143,7 @@ describe('Issue model', () => { it('updates data', () => { issue.updateData({ subscribed: true }); + expect(issue.subscribed).toBe(true); }); @@ -149,7 +162,7 @@ describe('Issue model', () => { }); describe('update', () => { - it('passes assignee ids when there are assignees', (done) => { + it('passes assignee ids when there are assignees', done => { spyOn(Vue.http, 'patch').and.callFake((url, data) => { expect(data.issue.assignee_ids).toEqual([1]); done(); @@ -158,7 +171,7 @@ describe('Issue model', () => { issue.update('url'); }); - it('passes assignee ids of [0] when there are no assignees', (done) => { + it('passes assignee ids of [0] when there are no assignees', done => { spyOn(Vue.http, 'patch').and.callFake((url, data) => { expect(data.issue.assignee_ids).toEqual([0]); done(); diff --git a/spec/javascripts/boards/list_spec.js b/spec/javascripts/boards/list_spec.js index ba6e81a29a9..0d462a6f872 100644 --- a/spec/javascripts/boards/list_spec.js +++ b/spec/javascripts/boards/list_spec.js @@ -31,21 +31,21 @@ describe('List model', () => { mock.restore(); }); - it('gets issues when created', (done) => { + it('gets issues when created', done => { setTimeout(() => { expect(list.issues.length).toBe(1); done(); }, 0); }); - it('saves list and returns ID', (done) => { + it('saves list and returns ID', done => { list = new List({ title: 'test', label: { id: _.random(10000), title: 'test', - color: 'red' - } + color: 'red', + }, }); list.save(); @@ -57,9 +57,10 @@ describe('List model', () => { }, 0); }); - it('destroys the list', (done) => { + it('destroys the list', done => { boardsStore.addList(listObj); list = boardsStore.findList('id', listObj.id); + expect(boardsStore.state.lists.length).toBe(1); list.destroy(); @@ -69,19 +70,22 @@ describe('List model', () => { }, 0); }); - it('gets issue from list', (done) => { + it('gets issue from list', done => { setTimeout(() => { const issue = list.findIssue(1); + expect(issue).toBeDefined(); done(); }, 0); }); - it('removes issue', (done) => { + it('removes issue', done => { setTimeout(() => { const issue = list.findIssue(1); + expect(list.issues.length).toBe(1); list.removeIssue(issue); + expect(list.issues.length).toBe(0); done(); }, 0); @@ -105,8 +109,13 @@ describe('List model', () => { listDup.updateIssueLabel(issue, list); - expect(gl.boardService.moveIssue) - .toHaveBeenCalledWith(issue.id, list.id, listDup.id, undefined, undefined); + expect(gl.boardService.moveIssue).toHaveBeenCalledWith( + issue.id, + list.id, + listDup.id, + undefined, + undefined, + ); }); describe('page number', () => { @@ -116,14 +125,16 @@ describe('List model', () => { it('increase page number if current issue count is more than the page size', () => { for (let i = 0; i < 30; i += 1) { - list.issues.push(new ListIssue({ - title: 'Testing', - id: _.random(10000) + i, - iid: _.random(10000) + i, - confidential: false, - labels: [list.label], - assignees: [], - })); + list.issues.push( + new ListIssue({ + title: 'Testing', + id: _.random(10000) + i, + iid: _.random(10000) + i, + confidential: false, + labels: [list.label], + assignees: [], + }), + ); } list.issuesSize = 50; @@ -136,13 +147,15 @@ describe('List model', () => { }); it('does not increase page number if issue count is less than the page size', () => { - list.issues.push(new ListIssue({ - title: 'Testing', - id: _.random(10000), - confidential: false, - labels: [list.label], - assignees: [], - })); + list.issues.push( + new ListIssue({ + title: 'Testing', + id: _.random(10000), + confidential: false, + labels: [list.label], + assignees: [], + }), + ); list.issuesSize = 2; list.nextPage(); @@ -154,21 +167,25 @@ describe('List model', () => { describe('newIssue', () => { beforeEach(() => { - spyOn(gl.boardService, 'newIssue').and.returnValue(Promise.resolve({ - data: { - id: 42, - }, - })); + spyOn(gl.boardService, 'newIssue').and.returnValue( + Promise.resolve({ + data: { + id: 42, + }, + }), + ); }); - it('adds new issue to top of list', (done) => { - list.issues.push(new ListIssue({ - title: 'Testing', - id: _.random(10000), - confidential: false, - labels: [list.label], - assignees: [], - })); + it('adds new issue to top of list', done => { + list.issues.push( + new ListIssue({ + title: 'Testing', + id: _.random(10000), + confidential: false, + labels: [list.label], + assignees: [], + }), + ); const dummyIssue = new ListIssue({ title: 'new issue', id: _.random(10000), @@ -177,7 +194,8 @@ describe('List model', () => { assignees: [], }); - list.newIssue(dummyIssue) + list + .newIssue(dummyIssue) .then(() => { expect(list.issues.length).toBe(2); expect(list.issues[0]).toBe(dummyIssue); diff --git a/spec/javascripts/bootstrap_jquery_spec.js b/spec/javascripts/bootstrap_jquery_spec.js index cd61d920fa0..35340a3bc42 100644 --- a/spec/javascripts/bootstrap_jquery_spec.js +++ b/spec/javascripts/bootstrap_jquery_spec.js @@ -3,43 +3,45 @@ import $ from 'jquery'; import '~/commons/bootstrap'; -(function() { - describe('Bootstrap jQuery extensions', function() { - describe('disable', function() { - beforeEach(function() { - return setFixtures('<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'); - }); +describe('Bootstrap jQuery extensions', function() { + describe('disable', function() { + beforeEach(function() { + return setFixtures('<input type="text" />'); }); - return describe('enable', function() { - beforeEach(function() { - return setFixtures('<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'); - }); + + it('adds the disabled attribute', function() { + var $input; + $input = $('input').first(); + $input.disable(); + + expect($input).toHaveAttr('disabled', 'disabled'); + }); + return it('adds the disabled class', function() { + var $input; + $input = $('input').first(); + $input.disable(); + + expect($input).toHaveClass('disabled'); + }); + }); + return describe('enable', function() { + beforeEach(function() { + return setFixtures('<input type="text" disabled="disabled" class="disabled" />'); + }); + + it('removes the disabled attribute', function() { + var $input; + $input = $('input').first(); + $input.enable(); + + expect($input).not.toHaveAttr('disabled'); + }); + return it('removes the disabled class', function() { + var $input; + $input = $('input').first(); + $input.enable(); + + expect($input).not.toHaveClass('disabled'); }); }); -}).call(window); +}); diff --git a/spec/javascripts/bootstrap_linked_tabs_spec.js b/spec/javascripts/bootstrap_linked_tabs_spec.js index 6f679369289..c3e3d78ff63 100644 --- a/spec/javascripts/bootstrap_linked_tabs_spec.js +++ b/spec/javascripts/bootstrap_linked_tabs_spec.js @@ -1,60 +1,67 @@ import LinkedTabs from '~/lib/utils/bootstrap_linked_tabs'; -(() => { - describe('Linked Tabs', () => { - preloadFixtures('static/linked_tabs.html.raw'); +describe('Linked Tabs', () => { + preloadFixtures('static/linked_tabs.html.raw'); + beforeEach(() => { + loadFixtures('static/linked_tabs.html.raw'); + }); + + describe('when is initialized', () => { beforeEach(() => { - loadFixtures('static/linked_tabs.html.raw'); + spyOn(window.history, 'replaceState').and.callFake(function() {}); }); - describe('when is initialized', () => { - beforeEach(() => { - spyOn(window.history, 'replaceState').and.callFake(function () {}); + it('should activate the tab correspondent to the given action', () => { + // eslint-disable-next-line no-new + new LinkedTabs({ + action: 'tab1', + defaultAction: 'tab1', + parentEl: '.linked-tabs', }); - it('should activate the tab correspondent to the given action', () => { - const linkedTabs = new LinkedTabs({ // eslint-disable-line - action: 'tab1', - defaultAction: 'tab1', - parentEl: '.linked-tabs', - }); + expect(document.querySelector('#tab1').classList).toContain('active'); + }); - expect(document.querySelector('#tab1').classList).toContain('active'); + it('should active the default tab action when the action is show', () => { + // eslint-disable-next-line no-new + new LinkedTabs({ + action: 'show', + defaultAction: 'tab1', + parentEl: '.linked-tabs', }); - it('should active the default tab action when the action is show', () => { - const linkedTabs = new LinkedTabs({ // eslint-disable-line - action: 'show', - defaultAction: 'tab1', - parentEl: '.linked-tabs', - }); - - expect(document.querySelector('#tab1').classList).toContain('active'); - }); + expect(document.querySelector('#tab1').classList).toContain('active'); }); + }); - describe('on click', () => { - it('should change the url according to the clicked tab', () => { - const historySpy = spyOn(window.history, 'replaceState').and.callFake(() => {}); + describe('on click', () => { + it('should change the url according to the clicked tab', () => { + const historySpy = spyOn(window.history, 'replaceState').and.callFake(() => {}); - const linkedTabs = new LinkedTabs({ - action: 'show', - defaultAction: 'tab1', - parentEl: '.linked-tabs', - }); + const linkedTabs = new LinkedTabs({ + action: 'show', + defaultAction: 'tab1', + parentEl: '.linked-tabs', + }); - const secondTab = document.querySelector('.linked-tabs li:nth-child(2) a'); - const newState = secondTab.getAttribute('href') + linkedTabs.currentLocation.search + linkedTabs.currentLocation.hash; + const secondTab = document.querySelector('.linked-tabs li:nth-child(2) a'); + const newState = + secondTab.getAttribute('href') + + linkedTabs.currentLocation.search + + linkedTabs.currentLocation.hash; - secondTab.click(); + secondTab.click(); - if (historySpy) { - expect(historySpy).toHaveBeenCalledWith({ + if (historySpy) { + expect(historySpy).toHaveBeenCalledWith( + { url: newState, - }, document.title, newState); - } - }); + }, + document.title, + newState, + ); + } }); }); -})(); +}); diff --git a/spec/javascripts/breakpoints_spec.js b/spec/javascripts/breakpoints_spec.js index b1b5d36c1fb..5ee777fee3f 100644 --- a/spec/javascripts/breakpoints_spec.js +++ b/spec/javascripts/breakpoints_spec.js @@ -1,9 +1,7 @@ -import bp, { - breakpoints, -} from '~/breakpoints'; +import bp, { breakpoints } from '~/breakpoints'; describe('breakpoints', () => { - Object.keys(breakpoints).forEach((key) => { + Object.keys(breakpoints).forEach(key => { const size = breakpoints[key]; it(`returns ${key} when larger than ${size}`, () => { diff --git a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js index ee457a9c48c..4f8701bae01 100644 --- a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js +++ b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js @@ -43,7 +43,7 @@ describe('AjaxFormVariableList', () => { }); describe('onSaveClicked', () => { - it('shows loading spinner while waiting for the request', (done) => { + it('shows loading spinner while waiting for the request', done => { const loadingIcon = saveButton.querySelector('.js-secret-variables-save-loading-icon'); mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(() => { @@ -54,7 +54,8 @@ describe('AjaxFormVariableList', () => { expect(loadingIcon.classList.contains(HIDE_CLASS)).toEqual(true); - ajaxVariableList.onSaveClicked() + ajaxVariableList + .onSaveClicked() .then(() => { expect(loadingIcon.classList.contains(HIDE_CLASS)).toEqual(true); }) @@ -62,27 +63,30 @@ describe('AjaxFormVariableList', () => { .catch(done.fail); }); - it('calls `updateRowsWithPersistedVariables` with the persisted variables', (done) => { + it('calls `updateRowsWithPersistedVariables` with the persisted variables', done => { const variablesResponse = [{ id: 1, key: 'foo', value: 'bar' }]; mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(200, { variables: variablesResponse, }); - ajaxVariableList.onSaveClicked() + ajaxVariableList + .onSaveClicked() .then(() => { - expect(ajaxVariableList.updateRowsWithPersistedVariables) - .toHaveBeenCalledWith(variablesResponse); + expect(ajaxVariableList.updateRowsWithPersistedVariables).toHaveBeenCalledWith( + variablesResponse, + ); }) .then(done) .catch(done.fail); }); - it('hides any previous error box', (done) => { + it('hides any previous error box', done => { mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(200); expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true); - ajaxVariableList.onSaveClicked() + ajaxVariableList + .onSaveClicked() .then(() => { expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true); }) @@ -90,14 +94,15 @@ describe('AjaxFormVariableList', () => { .catch(done.fail); }); - it('disables remove buttons while waiting for the request', (done) => { + it('disables remove buttons while waiting for the request', done => { mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(() => { expect(ajaxVariableList.variableList.toggleEnableRow).toHaveBeenCalledWith(false); return [200, {}]; }); - ajaxVariableList.onSaveClicked() + ajaxVariableList + .onSaveClicked() .then(() => { expect(ajaxVariableList.variableList.toggleEnableRow).toHaveBeenCalledWith(true); }) @@ -105,7 +110,7 @@ describe('AjaxFormVariableList', () => { .catch(done.fail); }); - it('hides secret values', (done) => { + it('hides secret values', done => { mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(200, {}); const row = container.querySelector('.js-row:first-child'); @@ -118,7 +123,8 @@ describe('AjaxFormVariableList', () => { expect(valuePlaceholder.classList.contains(HIDE_CLASS)).toBe(true); expect(valueInput.classList.contains(HIDE_CLASS)).toBe(false); - ajaxVariableList.onSaveClicked() + ajaxVariableList + .onSaveClicked() .then(() => { expect(valuePlaceholder.classList.contains(HIDE_CLASS)).toBe(false); expect(valueInput.classList.contains(HIDE_CLASS)).toBe(true); @@ -127,29 +133,31 @@ describe('AjaxFormVariableList', () => { .catch(done.fail); }); - it('shows error box with validation errors', (done) => { + it('shows error box with validation errors', done => { const validationError = 'some validation error'; - mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(400, [ - validationError, - ]); + mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(400, [validationError]); expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true); - ajaxVariableList.onSaveClicked() + ajaxVariableList + .onSaveClicked() .then(() => { expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(false); - expect(errorBox.textContent.trim().replace(/\n+\s+/m, ' ')).toEqual(`Validation failed ${validationError}`); + expect(errorBox.textContent.trim().replace(/\n+\s+/m, ' ')).toEqual( + `Validation failed ${validationError}`, + ); }) .then(done) .catch(done.fail); }); - it('shows flash message when request fails', (done) => { + it('shows flash message when request fails', done => { mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(500); expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true); - ajaxVariableList.onSaveClicked() + ajaxVariableList + .onSaveClicked() .then(() => { expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true); }) @@ -200,11 +208,13 @@ describe('AjaxFormVariableList', () => { expect(idInput.value).toEqual(''); - ajaxVariableList.updateRowsWithPersistedVariables([{ - id: 3, - key: 'foo', - value: 'bar', - }]); + ajaxVariableList.updateRowsWithPersistedVariables([ + { + id: 3, + key: 'foo', + value: 'bar', + }, + ]); expect(idInput.value).toEqual('3'); expect(row.dataset.isPersisted).toEqual('true'); diff --git a/spec/javascripts/ci_variable_list/ci_variable_list_spec.js b/spec/javascripts/ci_variable_list/ci_variable_list_spec.js index 2fa50975f0f..30b15011def 100644 --- a/spec/javascripts/ci_variable_list/ci_variable_list_spec.js +++ b/spec/javascripts/ci_variable_list/ci_variable_list_spec.js @@ -33,7 +33,8 @@ describe('VariableList', () => { it('should add another row when editing the last rows key input', () => { const $row = $wrapper.find('.js-row'); - $row.find('.js-ci-variable-input-key') + $row + .find('.js-ci-variable-input-key') .val('foo') .trigger('input'); @@ -41,12 +42,14 @@ describe('VariableList', () => { // Check for the correct default in the new row const $keyInput = $wrapper.find('.js-row:last-child').find('.js-ci-variable-input-key'); + expect($keyInput.val()).toBe(''); }); it('should add another row when editing the last rows value textarea', () => { const $row = $wrapper.find('.js-row'); - $row.find('.js-ci-variable-input-value') + $row + .find('.js-ci-variable-input-value') .val('foo') .trigger('input'); @@ -54,18 +57,21 @@ describe('VariableList', () => { // Check for the correct default in the new row const $valueInput = $wrapper.find('.js-row:last-child').find('.js-ci-variable-input-key'); + expect($valueInput.val()).toBe(''); }); it('should remove empty row after blurring', () => { const $row = $wrapper.find('.js-row'); - $row.find('.js-ci-variable-input-key') + $row + .find('.js-ci-variable-input-key') .val('foo') .trigger('input'); expect($wrapper.find('.js-row').length).toBe(2); - $row.find('.js-ci-variable-input-key') + $row + .find('.js-ci-variable-input-key') .val('') .trigger('input') .trigger('blur'); @@ -119,7 +125,7 @@ describe('VariableList', () => { variableList.init(); }); - it('should add another row when editing the last rows protected checkbox', (done) => { + it('should add another row when editing the last rows protected checkbox', done => { const $row = $wrapper.find('.js-row:last-child'); $row.find('.ci-variable-protected-item .js-project-feature-toggle').click(); @@ -128,7 +134,10 @@ describe('VariableList', () => { expect($wrapper.find('.js-row').length).toBe(2); // Check for the correct default in the new row - const $protectedInput = $wrapper.find('.js-row:last-child').find('.js-ci-variable-input-protected'); + const $protectedInput = $wrapper + .find('.js-row:last-child') + .find('.js-ci-variable-input-protected'); + expect($protectedInput.val()).toBe('false'); }) .then(done) @@ -166,6 +175,7 @@ describe('VariableList', () => { it('should enable all remove buttons', () => { variableList.toggleEnableRow(false); + expect($wrapper.find('.js-row-remove-button[disabled]').length).toBe(3); variableList.toggleEnableRow(true); @@ -175,6 +185,7 @@ describe('VariableList', () => { it('should enable all key inputs', () => { variableList.toggleEnableRow(false); + expect($wrapper.find('.js-ci-variable-input-key[disabled]').length).toBe(3); variableList.toggleEnableRow(true); @@ -200,7 +211,8 @@ describe('VariableList', () => { const $inputValue = $row.find('.js-ci-variable-input-value'); const $placeholder = $row.find('.js-secret-value-placeholder'); - $row.find('.js-ci-variable-input-value') + $row + .find('.js-ci-variable-input-value') .val('foo') .trigger('input'); diff --git a/spec/javascripts/ci_variable_list/native_form_variable_list_spec.js b/spec/javascripts/ci_variable_list/native_form_variable_list_spec.js index 94a0c999d66..997d0d54d79 100644 --- a/spec/javascripts/ci_variable_list/native_form_variable_list_spec.js +++ b/spec/javascripts/ci_variable_list/native_form_variable_list_spec.js @@ -19,8 +19,14 @@ describe('NativeFormVariableList', () => { describe('onFormSubmit', () => { it('should clear out the `name` attribute on the inputs for the last empty row on form submission (avoid BE validation)', () => { const $row = $wrapper.find('.js-row'); - expect($row.find('.js-ci-variable-input-key').attr('name')).toBe('schedule[variables_attributes][][key]'); - expect($row.find('.js-ci-variable-input-value').attr('name')).toBe('schedule[variables_attributes][][secret_value]'); + + expect($row.find('.js-ci-variable-input-key').attr('name')).toBe( + 'schedule[variables_attributes][][key]', + ); + + expect($row.find('.js-ci-variable-input-value').attr('name')).toBe( + 'schedule[variables_attributes][][secret_value]', + ); $wrapper.closest('form').trigger('trigger-submit'); diff --git a/spec/javascripts/close_reopen_report_toggle_spec.js b/spec/javascripts/close_reopen_report_toggle_spec.js index 412abe2cbf8..04a7ae7f429 100644 --- a/spec/javascripts/close_reopen_report_toggle_spec.js +++ b/spec/javascripts/close_reopen_report_toggle_spec.js @@ -10,7 +10,7 @@ describe('CloseReopenReportToggle', () => { const button = {}; let commentTypeToggle; - beforeEach(function () { + beforeEach(function() { commentTypeToggle = new CloseReopenReportToggle({ dropdownTrigger, dropdownList, @@ -18,15 +18,15 @@ describe('CloseReopenReportToggle', () => { }); }); - it('sets .dropdownTrigger', function () { + it('sets .dropdownTrigger', function() { expect(commentTypeToggle.dropdownTrigger).toBe(dropdownTrigger); }); - it('sets .dropdownList', function () { + it('sets .dropdownList', function() { expect(commentTypeToggle.dropdownList).toBe(dropdownList); }); - it('sets .button', function () { + it('sets .button', function() { expect(commentTypeToggle.button).toBe(button); }); }); diff --git a/spec/javascripts/clusters/clusters_bundle_spec.js b/spec/javascripts/clusters/clusters_bundle_spec.js index d0e0b214509..880b469284b 100644 --- a/spec/javascripts/clusters/clusters_bundle_spec.js +++ b/spec/javascripts/clusters/clusters_bundle_spec.js @@ -21,21 +21,21 @@ describe('Clusters', () => { }); describe('toggle', () => { - it('should update the button and the input field on click', (done) => { - const toggleButton = document.querySelector('.js-cluster-enable-toggle-area .js-project-feature-toggle'); - const toggleInput = document.querySelector('.js-cluster-enable-toggle-area .js-project-feature-toggle-input'); + it('should update the button and the input field on click', done => { + const toggleButton = document.querySelector( + '.js-cluster-enable-toggle-area .js-project-feature-toggle', + ); + const toggleInput = document.querySelector( + '.js-cluster-enable-toggle-area .js-project-feature-toggle-input', + ); toggleButton.click(); getSetTimeoutPromise() .then(() => { - expect( - toggleButton.classList, - ).not.toContain('is-checked'); + expect(toggleButton.classList).not.toContain('is-checked'); - expect( - toggleInput.getAttribute('value'), - ).toEqual('false'); + expect(toggleInput.getAttribute('value')).toEqual('false'); }) .then(done) .catch(done.fail); @@ -46,29 +46,21 @@ describe('Clusters', () => { it('should update token field type', () => { cluster.showTokenButton.click(); - expect( - cluster.tokenField.getAttribute('type'), - ).toEqual('text'); + expect(cluster.tokenField.getAttribute('type')).toEqual('text'); cluster.showTokenButton.click(); - expect( - cluster.tokenField.getAttribute('type'), - ).toEqual('password'); + expect(cluster.tokenField.getAttribute('type')).toEqual('password'); }); it('should update show token button text', () => { cluster.showTokenButton.click(); - expect( - cluster.showTokenButton.textContent, - ).toEqual('Hide'); + expect(cluster.showTokenButton.textContent).toEqual('Hide'); cluster.showTokenButton.click(); - expect( - cluster.showTokenButton.textContent, - ).toEqual('Show'); + expect(cluster.showTokenButton.textContent).toEqual('Show'); }); }); @@ -86,37 +78,50 @@ describe('Clusters', () => { }); const flashMessage = document.querySelector('.js-cluster-application-notice .flash-text'); + expect(flashMessage).toBeNull(); }); it('shows an alert when something gets newly installed', () => { - cluster.checkForNewInstalls({ - ...INITIAL_APP_MAP, - helm: { status: APPLICATION_STATUS.INSTALLING, title: 'Helm Tiller' }, - }, { - ...INITIAL_APP_MAP, - helm: { status: APPLICATION_STATUS.INSTALLED, title: 'Helm Tiller' }, - }); + cluster.checkForNewInstalls( + { + ...INITIAL_APP_MAP, + helm: { status: APPLICATION_STATUS.INSTALLING, title: 'Helm Tiller' }, + }, + { + ...INITIAL_APP_MAP, + helm: { status: APPLICATION_STATUS.INSTALLED, title: 'Helm Tiller' }, + }, + ); const flashMessage = document.querySelector('.js-cluster-application-notice .flash-text'); + expect(flashMessage).not.toBeNull(); - expect(flashMessage.textContent.trim()).toEqual('Helm Tiller was successfully installed on your Kubernetes cluster'); + expect(flashMessage.textContent.trim()).toEqual( + 'Helm Tiller was successfully installed on your Kubernetes cluster', + ); }); it('shows an alert when multiple things gets newly installed', () => { - cluster.checkForNewInstalls({ - ...INITIAL_APP_MAP, - helm: { status: APPLICATION_STATUS.INSTALLING, title: 'Helm Tiller' }, - ingress: { status: APPLICATION_STATUS.INSTALLABLE, title: 'Ingress' }, - }, { - ...INITIAL_APP_MAP, - helm: { status: APPLICATION_STATUS.INSTALLED, title: 'Helm Tiller' }, - ingress: { status: APPLICATION_STATUS.INSTALLED, title: 'Ingress' }, - }); + cluster.checkForNewInstalls( + { + ...INITIAL_APP_MAP, + helm: { status: APPLICATION_STATUS.INSTALLING, title: 'Helm Tiller' }, + ingress: { status: APPLICATION_STATUS.INSTALLABLE, title: 'Ingress' }, + }, + { + ...INITIAL_APP_MAP, + helm: { status: APPLICATION_STATUS.INSTALLED, title: 'Helm Tiller' }, + ingress: { status: APPLICATION_STATUS.INSTALLED, title: 'Ingress' }, + }, + ); const flashMessage = document.querySelector('.js-cluster-application-notice .flash-text'); + expect(flashMessage).not.toBeNull(); - expect(flashMessage.textContent.trim()).toEqual('Helm Tiller, Ingress was successfully installed on your Kubernetes cluster'); + expect(flashMessage.textContent.trim()).toEqual( + 'Helm Tiller, Ingress was successfully installed on your Kubernetes cluster', + ); }); }); @@ -125,29 +130,21 @@ describe('Clusters', () => { it('should show the creating container', () => { cluster.updateContainer(null, 'creating'); - expect( - cluster.creatingContainer.classList.contains('hidden'), - ).toBeFalsy(); - expect( - cluster.successContainer.classList.contains('hidden'), - ).toBeTruthy(); - expect( - cluster.errorContainer.classList.contains('hidden'), - ).toBeTruthy(); + expect(cluster.creatingContainer.classList.contains('hidden')).toBeFalsy(); + + expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); + + expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy(); }); it('should continue to show `creating` banner with subsequent updates of the same status', () => { cluster.updateContainer('creating', 'creating'); - expect( - cluster.creatingContainer.classList.contains('hidden'), - ).toBeFalsy(); - expect( - cluster.successContainer.classList.contains('hidden'), - ).toBeTruthy(); - expect( - cluster.errorContainer.classList.contains('hidden'), - ).toBeTruthy(); + expect(cluster.creatingContainer.classList.contains('hidden')).toBeFalsy(); + + expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); + + expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy(); }); }); @@ -155,29 +152,21 @@ describe('Clusters', () => { it('should show the success container and fresh the page', () => { cluster.updateContainer(null, 'created'); - expect( - cluster.creatingContainer.classList.contains('hidden'), - ).toBeTruthy(); - expect( - cluster.successContainer.classList.contains('hidden'), - ).toBeFalsy(); - expect( - cluster.errorContainer.classList.contains('hidden'), - ).toBeTruthy(); + expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy(); + + expect(cluster.successContainer.classList.contains('hidden')).toBeFalsy(); + + expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy(); }); it('should not show a banner when status is already `created`', () => { cluster.updateContainer('created', 'created'); - expect( - cluster.creatingContainer.classList.contains('hidden'), - ).toBeTruthy(); - expect( - cluster.successContainer.classList.contains('hidden'), - ).toBeTruthy(); - expect( - cluster.errorContainer.classList.contains('hidden'), - ).toBeTruthy(); + expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy(); + + expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); + + expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy(); }); }); @@ -185,40 +174,31 @@ describe('Clusters', () => { it('should show the error container', () => { cluster.updateContainer(null, 'errored', 'this is an error'); - expect( - cluster.creatingContainer.classList.contains('hidden'), - ).toBeTruthy(); - expect( - cluster.successContainer.classList.contains('hidden'), - ).toBeTruthy(); - expect( - cluster.errorContainer.classList.contains('hidden'), - ).toBeFalsy(); - - expect( - cluster.errorReasonContainer.textContent, - ).toContain('this is an error'); + expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy(); + + expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); + + expect(cluster.errorContainer.classList.contains('hidden')).toBeFalsy(); + + expect(cluster.errorReasonContainer.textContent).toContain('this is an error'); }); it('should show `error` banner when previously `creating`', () => { cluster.updateContainer('creating', 'errored'); - expect( - cluster.creatingContainer.classList.contains('hidden'), - ).toBeTruthy(); - expect( - cluster.successContainer.classList.contains('hidden'), - ).toBeTruthy(); - expect( - cluster.errorContainer.classList.contains('hidden'), - ).toBeFalsy(); + expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy(); + + expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); + + expect(cluster.errorContainer.classList.contains('hidden')).toBeFalsy(); }); }); }); describe('installApplication', () => { - it('tries to install helm', (done) => { + it('tries to install helm', done => { spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve()); + expect(cluster.store.state.applications.helm.requestStatus).toEqual(null); cluster.installApplication({ id: 'helm' }); @@ -236,8 +216,9 @@ describe('Clusters', () => { .catch(done.fail); }); - it('tries to install ingress', (done) => { + it('tries to install ingress', done => { spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve()); + expect(cluster.store.state.applications.ingress.requestStatus).toEqual(null); cluster.installApplication({ id: 'ingress' }); @@ -255,8 +236,9 @@ describe('Clusters', () => { .catch(done.fail); }); - it('tries to install runner', (done) => { + it('tries to install runner', done => { spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve()); + expect(cluster.store.state.applications.runner.requestStatus).toEqual(null); cluster.installApplication({ id: 'runner' }); @@ -274,26 +256,35 @@ describe('Clusters', () => { .catch(done.fail); }); - it('tries to install jupyter', (done) => { + it('tries to install jupyter', done => { spyOn(cluster.service, 'installApplication').and.returnValue(Promise.resolve()); + expect(cluster.store.state.applications.jupyter.requestStatus).toEqual(null); - cluster.installApplication({ id: 'jupyter', params: { hostname: cluster.store.state.applications.jupyter.hostname } }); + cluster.installApplication({ + id: 'jupyter', + params: { hostname: cluster.store.state.applications.jupyter.hostname }, + }); expect(cluster.store.state.applications.jupyter.requestStatus).toEqual(REQUEST_LOADING); expect(cluster.store.state.applications.jupyter.requestReason).toEqual(null); - expect(cluster.service.installApplication).toHaveBeenCalledWith('jupyter', { hostname: cluster.store.state.applications.jupyter.hostname }); + expect(cluster.service.installApplication).toHaveBeenCalledWith('jupyter', { + hostname: cluster.store.state.applications.jupyter.hostname, + }); getSetTimeoutPromise() - .then(() => { - expect(cluster.store.state.applications.jupyter.requestStatus).toEqual(REQUEST_SUCCESS); - expect(cluster.store.state.applications.jupyter.requestReason).toEqual(null); - }) - .then(done) - .catch(done.fail); + .then(() => { + expect(cluster.store.state.applications.jupyter.requestStatus).toEqual(REQUEST_SUCCESS); + expect(cluster.store.state.applications.jupyter.requestReason).toEqual(null); + }) + .then(done) + .catch(done.fail); }); - it('sets error request status when the request fails', (done) => { - spyOn(cluster.service, 'installApplication').and.returnValue(Promise.reject(new Error('STUBBED ERROR'))); + it('sets error request status when the request fails', done => { + spyOn(cluster.service, 'installApplication').and.returnValue( + Promise.reject(new Error('STUBBED ERROR')), + ); + expect(cluster.store.state.applications.helm.requestStatus).toEqual(null); cluster.installApplication({ id: 'helm' }); diff --git a/spec/javascripts/clusters/components/application_row_spec.js b/spec/javascripts/clusters/components/application_row_spec.js index 9da5c248371..45d56514930 100644 --- a/spec/javascripts/clusters/components/application_row_spec.js +++ b/spec/javascripts/clusters/components/application_row_spec.js @@ -112,6 +112,17 @@ describe('Application Row', () => { expect(vm.installButtonDisabled).toEqual(true); }); + it('has disabled "Installed" when APPLICATION_STATUS.UPDATING', () => { + vm = mountComponent(ApplicationRow, { + ...DEFAULT_APPLICATION_STATE, + status: APPLICATION_STATUS.UPDATING, + }); + + expect(vm.installButtonLabel).toEqual('Installed'); + expect(vm.installButtonLoading).toEqual(false); + expect(vm.installButtonDisabled).toEqual(true); + }); + it('has enabled "Install" when APPLICATION_STATUS.ERROR', () => { vm = mountComponent(ApplicationRow, { ...DEFAULT_APPLICATION_STATE, @@ -215,7 +226,9 @@ describe('Application Row', () => { status: null, requestStatus: null, }); - const generalErrorMessage = vm.$el.querySelector('.js-cluster-application-general-error-message'); + const generalErrorMessage = vm.$el.querySelector( + '.js-cluster-application-general-error-message', + ); expect(generalErrorMessage).toBeNull(); }); @@ -227,10 +240,17 @@ describe('Application Row', () => { status: APPLICATION_STATUS.ERROR, statusReason, }); - const generalErrorMessage = vm.$el.querySelector('.js-cluster-application-general-error-message'); - const statusErrorMessage = vm.$el.querySelector('.js-cluster-application-status-error-message'); + const generalErrorMessage = vm.$el.querySelector( + '.js-cluster-application-general-error-message', + ); + const statusErrorMessage = vm.$el.querySelector( + '.js-cluster-application-status-error-message', + ); + + expect(generalErrorMessage.textContent.trim()).toEqual( + `Something went wrong while installing ${DEFAULT_APPLICATION_STATE.title}`, + ); - expect(generalErrorMessage.textContent.trim()).toEqual(`Something went wrong while installing ${DEFAULT_APPLICATION_STATE.title}`); expect(statusErrorMessage.textContent.trim()).toEqual(statusReason); }); @@ -242,10 +262,17 @@ describe('Application Row', () => { requestStatus: REQUEST_FAILURE, requestReason, }); - const generalErrorMessage = vm.$el.querySelector('.js-cluster-application-general-error-message'); - const requestErrorMessage = vm.$el.querySelector('.js-cluster-application-request-error-message'); + const generalErrorMessage = vm.$el.querySelector( + '.js-cluster-application-general-error-message', + ); + const requestErrorMessage = vm.$el.querySelector( + '.js-cluster-application-request-error-message', + ); + + expect(generalErrorMessage.textContent.trim()).toEqual( + `Something went wrong while installing ${DEFAULT_APPLICATION_STATE.title}`, + ); - expect(generalErrorMessage.textContent.trim()).toEqual(`Something went wrong while installing ${DEFAULT_APPLICATION_STATE.title}`); expect(requestErrorMessage.textContent.trim()).toEqual(requestReason); }); }); diff --git a/spec/javascripts/clusters/services/mock_data.js b/spec/javascripts/clusters/services/mock_data.js index c7c1412e1c6..4e6ad11cd92 100644 --- a/spec/javascripts/clusters/services/mock_data.js +++ b/spec/javascripts/clusters/services/mock_data.js @@ -6,67 +6,77 @@ const CLUSTERS_MOCK_DATA = { data: { status: 'errored', status_reason: 'Failed to request to CloudPlatform.', - applications: [{ - name: 'helm', - status: APPLICATION_STATUS.INSTALLABLE, - status_reason: null, - }, { - name: 'ingress', - status: APPLICATION_STATUS.ERROR, - status_reason: 'Cannot connect', - external_ip: null, - }, { - name: 'runner', - status: APPLICATION_STATUS.INSTALLING, - status_reason: null, - }, - { - name: 'prometheus', - status: APPLICATION_STATUS.ERROR, - status_reason: 'Cannot connect', - }, { - name: 'jupyter', - status: APPLICATION_STATUS.INSTALLING, - status_reason: 'Cannot connect', - }], + applications: [ + { + name: 'helm', + status: APPLICATION_STATUS.INSTALLABLE, + status_reason: null, + }, + { + name: 'ingress', + status: APPLICATION_STATUS.ERROR, + status_reason: 'Cannot connect', + external_ip: null, + }, + { + name: 'runner', + status: APPLICATION_STATUS.INSTALLING, + status_reason: null, + }, + { + name: 'prometheus', + status: APPLICATION_STATUS.ERROR, + status_reason: 'Cannot connect', + }, + { + name: 'jupyter', + status: APPLICATION_STATUS.INSTALLING, + status_reason: 'Cannot connect', + }, + ], }, }, '/gitlab-org/gitlab-shell/clusters/2/status.json': { data: { status: 'errored', status_reason: 'Failed to request to CloudPlatform.', - applications: [{ - name: 'helm', - status: APPLICATION_STATUS.INSTALLED, - status_reason: null, - }, { - name: 'ingress', - status: APPLICATION_STATUS.INSTALLED, - status_reason: 'Cannot connect', - external_ip: '1.1.1.1', - }, { - name: 'runner', - status: APPLICATION_STATUS.INSTALLING, - status_reason: null, - }, - { - name: 'prometheus', - status: APPLICATION_STATUS.ERROR, - status_reason: 'Cannot connect', - }, { - name: 'jupyter', - status: APPLICATION_STATUS.INSTALLABLE, - status_reason: 'Cannot connect', - }], + applications: [ + { + name: 'helm', + status: APPLICATION_STATUS.INSTALLED, + status_reason: null, + }, + { + name: 'ingress', + status: APPLICATION_STATUS.INSTALLED, + status_reason: 'Cannot connect', + external_ip: '1.1.1.1', + }, + { + name: 'runner', + status: APPLICATION_STATUS.INSTALLING, + status_reason: null, + }, + { + name: 'prometheus', + status: APPLICATION_STATUS.ERROR, + status_reason: 'Cannot connect', + }, + { + name: 'jupyter', + status: APPLICATION_STATUS.INSTALLABLE, + status_reason: 'Cannot connect', + }, + ], }, }, }, POST: { - '/gitlab-org/gitlab-shell/clusters/1/applications/helm': { }, - '/gitlab-org/gitlab-shell/clusters/1/applications/ingress': { }, - '/gitlab-org/gitlab-shell/clusters/1/applications/runner': { }, - '/gitlab-org/gitlab-shell/clusters/1/applications/prometheus': { }, - '/gitlab-org/gitlab-shell/clusters/1/applications/jupyter': { }, + '/gitlab-org/gitlab-shell/clusters/1/applications/helm': {}, + '/gitlab-org/gitlab-shell/clusters/1/applications/ingress': {}, + '/gitlab-org/gitlab-shell/clusters/1/applications/runner': {}, + '/gitlab-org/gitlab-shell/clusters/1/applications/prometheus': {}, + '/gitlab-org/gitlab-shell/clusters/1/applications/jupyter': {}, }, }; @@ -81,7 +91,4 @@ const DEFAULT_APPLICATION_STATE = { requestReason: null, }; -export { - CLUSTERS_MOCK_DATA, - DEFAULT_APPLICATION_STATE, -}; +export { CLUSTERS_MOCK_DATA, DEFAULT_APPLICATION_STATE }; diff --git a/spec/javascripts/clusters/stores/clusters_store_spec.js b/spec/javascripts/clusters/stores/clusters_store_spec.js index 104a064bdd3..e0f55a12fca 100644 --- a/spec/javascripts/clusters/stores/clusters_store_spec.js +++ b/spec/javascripts/clusters/stores/clusters_store_spec.js @@ -53,7 +53,8 @@ describe('Clusters Store', () => { describe('updateStateFromServer', () => { it('should store new polling data from server', () => { - const mockResponseData = CLUSTERS_MOCK_DATA.GET['/gitlab-org/gitlab-shell/clusters/1/status.json'].data; + const mockResponseData = + CLUSTERS_MOCK_DATA.GET['/gitlab-org/gitlab-shell/clusters/1/status.json'].data; store.updateStateFromServer(mockResponseData); expect(store.state).toEqual({ @@ -104,13 +105,14 @@ describe('Clusters Store', () => { }); it('sets default hostname for jupyter when ingress has a ip address', () => { - const mockResponseData = CLUSTERS_MOCK_DATA.GET['/gitlab-org/gitlab-shell/clusters/2/status.json'].data; + const mockResponseData = + CLUSTERS_MOCK_DATA.GET['/gitlab-org/gitlab-shell/clusters/2/status.json'].data; store.updateStateFromServer(mockResponseData); - expect( - store.state.applications.jupyter.hostname, - ).toEqual(`jupyter.${store.state.applications.ingress.externalIp}.nip.io`); + expect(store.state.applications.jupyter.hostname).toEqual( + `jupyter.${store.state.applications.ingress.externalIp}.nip.io`, + ); }); }); }); diff --git a/spec/javascripts/collapsed_sidebar_todo_spec.js b/spec/javascripts/collapsed_sidebar_todo_spec.js index 8427e8a0ba7..bdee85f90b1 100644 --- a/spec/javascripts/collapsed_sidebar_todo_spec.js +++ b/spec/javascripts/collapsed_sidebar_todo_spec.js @@ -18,10 +18,8 @@ describe('Issuable right sidebar collapsed todo toggle', () => { new Sidebar(); loadFixtures(fixtureName); - document.querySelector('.js-right-sidebar') - .classList.toggle('right-sidebar-expanded'); - document.querySelector('.js-right-sidebar') - .classList.toggle('right-sidebar-collapsed'); + document.querySelector('.js-right-sidebar').classList.toggle('right-sidebar-expanded'); + document.querySelector('.js-right-sidebar').classList.toggle('right-sidebar-collapsed'); mock = new MockAdapter(axios); @@ -44,9 +42,7 @@ describe('Issuable right sidebar collapsed todo toggle', () => { }); it('shows add todo button', () => { - expect( - document.querySelector('.js-issuable-todo.sidebar-collapsed-icon'), - ).not.toBeNull(); + expect(document.querySelector('.js-issuable-todo.sidebar-collapsed-icon')).not.toBeNull(); expect( document.querySelector('.js-issuable-todo.sidebar-collapsed-icon .fa-plus-square'), @@ -63,7 +59,7 @@ describe('Issuable right sidebar collapsed todo toggle', () => { ).toBe('Add todo'); }); - it('toggle todo state', (done) => { + it('toggle todo state', done => { document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click(); setTimeout(() => { @@ -79,7 +75,7 @@ describe('Issuable right sidebar collapsed todo toggle', () => { }); }); - it('toggle todo state of expanded todo toggle', (done) => { + it('toggle todo state of expanded todo toggle', done => { document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click(); setTimeout(() => { @@ -91,19 +87,21 @@ describe('Issuable right sidebar collapsed todo toggle', () => { }); }); - it('toggles todo button tooltip', (done) => { + it('toggles todo button tooltip', done => { document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click(); setTimeout(() => { expect( - document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('data-original-title'), + document + .querySelector('.js-issuable-todo.sidebar-collapsed-icon') + .getAttribute('data-original-title'), ).toBe('Mark todo as done'); done(); }); }); - it('marks todo as done', (done) => { + it('marks todo as done', done => { document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click(); timeoutPromise() @@ -128,25 +126,29 @@ describe('Issuable right sidebar collapsed todo toggle', () => { .catch(done.fail); }); - it('updates aria-label to mark todo as done', (done) => { + it('updates aria-label to mark todo as done', done => { document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click(); setTimeout(() => { expect( - document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('aria-label'), + document + .querySelector('.js-issuable-todo.sidebar-collapsed-icon') + .getAttribute('aria-label'), ).toBe('Mark todo as done'); done(); }); }); - it('updates aria-label to add todo', (done) => { + it('updates aria-label to add todo', done => { document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click(); timeoutPromise() .then(() => { expect( - document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('aria-label'), + document + .querySelector('.js-issuable-todo.sidebar-collapsed-icon') + .getAttribute('aria-label'), ).toBe('Mark todo as done'); document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').click(); @@ -154,7 +156,9 @@ describe('Issuable right sidebar collapsed todo toggle', () => { .then(timeoutPromise) .then(() => { expect( - document.querySelector('.js-issuable-todo.sidebar-collapsed-icon').getAttribute('aria-label'), + document + .querySelector('.js-issuable-todo.sidebar-collapsed-icon') + .getAttribute('aria-label'), ).toBe('Add todo'); }) .then(done) diff --git a/spec/javascripts/comment_type_toggle_spec.js b/spec/javascripts/comment_type_toggle_spec.js index 0ba709298c5..8b1217a000f 100644 --- a/spec/javascripts/comment_type_toggle_spec.js +++ b/spec/javascripts/comment_type_toggle_spec.js @@ -1,9 +1,9 @@ import CommentTypeToggle from '~/comment_type_toggle'; import InputSetter from '~/droplab/plugins/input_setter'; -describe('CommentTypeToggle', function () { - describe('class constructor', function () { - beforeEach(function () { +describe('CommentTypeToggle', function() { + describe('class constructor', function() { + beforeEach(function() { this.dropdownTrigger = {}; this.dropdownList = {}; this.noteTypeInput = {}; @@ -19,33 +19,33 @@ describe('CommentTypeToggle', function () { }); }); - it('should set .dropdownTrigger', function () { + it('should set .dropdownTrigger', function() { expect(this.commentTypeToggle.dropdownTrigger).toBe(this.dropdownTrigger); }); - it('should set .dropdownList', function () { + it('should set .dropdownList', function() { expect(this.commentTypeToggle.dropdownList).toBe(this.dropdownList); }); - it('should set .noteTypeInput', function () { + it('should set .noteTypeInput', function() { expect(this.commentTypeToggle.noteTypeInput).toBe(this.noteTypeInput); }); - it('should set .submitButton', function () { + it('should set .submitButton', function() { expect(this.commentTypeToggle.submitButton).toBe(this.submitButton); }); - it('should set .closeButton', function () { + it('should set .closeButton', function() { expect(this.commentTypeToggle.closeButton).toBe(this.closeButton); }); - it('should set .reopenButton', function () { + it('should set .reopenButton', function() { expect(this.commentTypeToggle.reopenButton).toBe(this.reopenButton); }); }); - describe('initDroplab', function () { - beforeEach(function () { + describe('initDroplab', function() { + beforeEach(function() { this.commentTypeToggle = { dropdownTrigger: {}, dropdownList: {}, @@ -58,25 +58,27 @@ describe('CommentTypeToggle', function () { this.droplab = jasmine.createSpyObj('droplab', ['init']); - this.droplabConstructor = spyOnDependency(CommentTypeToggle, 'DropLab').and.returnValue(this.droplab); + this.droplabConstructor = spyOnDependency(CommentTypeToggle, 'DropLab').and.returnValue( + this.droplab, + ); spyOn(this.commentTypeToggle, 'setConfig').and.returnValue(this.config); CommentTypeToggle.prototype.initDroplab.call(this.commentTypeToggle); }); - it('should instantiate a DropLab instance', function () { + it('should instantiate a DropLab instance', function() { expect(this.droplabConstructor).toHaveBeenCalled(); }); - it('should set .droplab', function () { + it('should set .droplab', function() { expect(this.commentTypeToggle.droplab).toBe(this.droplab); }); - it('should call .setConfig', function () { + it('should call .setConfig', function() { expect(this.commentTypeToggle.setConfig).toHaveBeenCalled(); }); - it('should call DropLab.prototype.init', function () { + it('should call DropLab.prototype.init', function() { expect(this.droplab.init).toHaveBeenCalledWith( this.commentTypeToggle.dropdownTrigger, this.commentTypeToggle.dropdownList, @@ -86,9 +88,9 @@ describe('CommentTypeToggle', function () { }); }); - describe('setConfig', function () { - describe('if no .closeButton is provided', function () { - beforeEach(function () { + describe('setConfig', function() { + describe('if no .closeButton is provided', function() { + beforeEach(function() { this.commentTypeToggle = { dropdownTrigger: {}, dropdownList: {}, @@ -100,28 +102,33 @@ describe('CommentTypeToggle', function () { this.setConfig = CommentTypeToggle.prototype.setConfig.call(this.commentTypeToggle); }); - it('should not add .closeButton related InputSetter config', function () { + it('should not add .closeButton related InputSetter config', function() { expect(this.setConfig).toEqual({ - InputSetter: [{ - input: this.commentTypeToggle.noteTypeInput, - valueAttribute: 'data-value', - }, { - input: this.commentTypeToggle.submitButton, - valueAttribute: 'data-submit-text', - }, { - input: this.commentTypeToggle.reopenButton, - valueAttribute: 'data-reopen-text', - }, { - input: this.commentTypeToggle.reopenButton, - valueAttribute: 'data-reopen-text', - inputAttribute: 'data-alternative-text', - }], + InputSetter: [ + { + input: this.commentTypeToggle.noteTypeInput, + valueAttribute: 'data-value', + }, + { + input: this.commentTypeToggle.submitButton, + valueAttribute: 'data-submit-text', + }, + { + input: this.commentTypeToggle.reopenButton, + valueAttribute: 'data-reopen-text', + }, + { + input: this.commentTypeToggle.reopenButton, + valueAttribute: 'data-reopen-text', + inputAttribute: 'data-alternative-text', + }, + ], }); }); }); - describe('if no .reopenButton is provided', function () { - beforeEach(function () { + describe('if no .reopenButton is provided', function() { + beforeEach(function() { this.commentTypeToggle = { dropdownTrigger: {}, dropdownList: {}, @@ -133,22 +140,27 @@ describe('CommentTypeToggle', function () { this.setConfig = CommentTypeToggle.prototype.setConfig.call(this.commentTypeToggle); }); - it('should not add .reopenButton related InputSetter config', function () { + it('should not add .reopenButton related InputSetter config', function() { expect(this.setConfig).toEqual({ - InputSetter: [{ - input: this.commentTypeToggle.noteTypeInput, - valueAttribute: 'data-value', - }, { - input: this.commentTypeToggle.submitButton, - valueAttribute: 'data-submit-text', - }, { - input: this.commentTypeToggle.closeButton, - valueAttribute: 'data-close-text', - }, { - input: this.commentTypeToggle.closeButton, - valueAttribute: 'data-close-text', - inputAttribute: 'data-alternative-text', - }], + InputSetter: [ + { + input: this.commentTypeToggle.noteTypeInput, + valueAttribute: 'data-value', + }, + { + input: this.commentTypeToggle.submitButton, + valueAttribute: 'data-submit-text', + }, + { + input: this.commentTypeToggle.closeButton, + valueAttribute: 'data-close-text', + }, + { + input: this.commentTypeToggle.closeButton, + valueAttribute: 'data-close-text', + inputAttribute: 'data-alternative-text', + }, + ], }); }); }); diff --git a/spec/javascripts/commit/commit_pipeline_status_component_spec.js b/spec/javascripts/commit/commit_pipeline_status_component_spec.js index d3776d0c3cf..4fc56fd9a27 100644 --- a/spec/javascripts/commit/commit_pipeline_status_component_spec.js +++ b/spec/javascripts/commit/commit_pipeline_status_component_spec.js @@ -26,15 +26,18 @@ describe('Commit pipeline status component', () => { beforeEach(() => { mock = new MockAdapter(axios); mock.onGet('/dummy/endpoint').reply(() => { - const res = Promise.resolve([200, { - pipelines: [ - { - details: { - status: mockCiStatus, + const res = Promise.resolve([ + 200, + { + pipelines: [ + { + details: { + status: mockCiStatus, + }, }, - }, - ], - }]); + ], + }, + ]); return res; }); vm = mountComponent(Component, { @@ -48,7 +51,7 @@ describe('Commit pipeline status component', () => { mock.restore(); }); - it('shows the loading icon when polling is starting', (done) => { + it('shows the loading icon when polling is starting', done => { expect(vm.$el.querySelector('.loading-container')).not.toBe(null); setTimeout(() => { expect(vm.$el.querySelector('.loading-container')).toBe(null); @@ -56,17 +59,19 @@ describe('Commit pipeline status component', () => { }); }); - it('contains a ciStatus when the polling is succesful ', (done) => { + it('contains a ciStatus when the polling is succesful ', done => { setTimeout(() => { expect(vm.ciStatus).toEqual(mockCiStatus); done(); }); }); - it('contains a ci-status icon when polling is succesful', (done) => { + it('contains a ci-status icon when polling is succesful', done => { setTimeout(() => { expect(vm.$el.querySelector('.ci-status-icon')).not.toBe(null); - expect(vm.$el.querySelector('.ci-status-icon').classList).toContain(`ci-status-icon-${mockCiStatus.group}`); + expect(vm.$el.querySelector('.ci-status-icon').classList).toContain( + `ci-status-icon-${mockCiStatus.group}`, + ); done(); }); }); @@ -89,7 +94,7 @@ describe('Commit pipeline status component', () => { mock.restore(); }); - it('calls an errorCallback', (done) => { + it('calls an errorCallback', done => { spyOn(vm, 'errorCallback').and.callThrough(); vm.$mount(); setTimeout(() => { diff --git a/spec/javascripts/commit/pipelines/pipelines_spec.js b/spec/javascripts/commit/pipelines/pipelines_spec.js index a18e09da50a..b797cc44ae7 100644 --- a/spec/javascripts/commit/pipelines/pipelines_spec.js +++ b/spec/javascripts/commit/pipelines/pipelines_spec.js @@ -4,7 +4,7 @@ import axios from '~/lib/utils/axios_utils'; import pipelinesTable from '~/commit/pipelines/pipelines_table.vue'; import mountComponent from 'spec/helpers/vue_mount_component_helper'; -describe('Pipelines table in Commits and Merge requests', function () { +describe('Pipelines table in Commits and Merge requests', function() { const jsonFixtureName = 'pipelines/pipelines.json'; let pipeline; let PipelinesTable; @@ -29,7 +29,7 @@ describe('Pipelines table in Commits and Merge requests', function () { describe('successful request', () => { describe('without pipelines', () => { - beforeEach(function () { + beforeEach(function() { mock.onGet('endpoint.json').reply(200, []); vm = mountComponent(PipelinesTable, { @@ -41,7 +41,7 @@ describe('Pipelines table in Commits and Merge requests', function () { }); }); - it('should render the empty state', function (done) { + it('should render the empty state', function(done) { setTimeout(() => { expect(vm.$el.querySelector('.empty-state')).toBeDefined(); expect(vm.$el.querySelector('.realtime-loading')).toBe(null); @@ -63,7 +63,7 @@ describe('Pipelines table in Commits and Merge requests', function () { }); }); - it('should render a table with the received pipelines', (done) => { + it('should render a table with the received pipelines', done => { setTimeout(() => { expect(vm.$el.querySelectorAll('.ci-table .commit').length).toEqual(1); expect(vm.$el.querySelector('.realtime-loading')).toBe(null); @@ -79,11 +79,11 @@ describe('Pipelines table in Commits and Merge requests', function () { mock.onGet('endpoint.json').reply(200, [pipeline]); }); - it('should receive update-pipelines-count event', (done) => { + it('should receive update-pipelines-count event', done => { const element = document.createElement('div'); document.body.appendChild(element); - element.addEventListener('update-pipelines-count', (event) => { + element.addEventListener('update-pipelines-count', event => { expect(event.detail.pipelines).toEqual([pipeline]); done(); }); @@ -114,7 +114,7 @@ describe('Pipelines table in Commits and Merge requests', function () { }); }); - it('should render error state', function (done) { + it('should render error state', function(done) { setTimeout(() => { expect(vm.$el.querySelector('.js-pipelines-error-state')).toBeDefined(); expect(vm.$el.querySelector('.realtime-loading')).toBe(null); diff --git a/spec/javascripts/commit_merge_requests_spec.js b/spec/javascripts/commit_merge_requests_spec.js index 3466ef51ea8..82968e028d1 100644 --- a/spec/javascripts/commit_merge_requests_spec.js +++ b/spec/javascripts/commit_merge_requests_spec.js @@ -3,11 +3,16 @@ import * as CommitMergeRequests from '~/commit_merge_requests'; describe('CommitMergeRequests', () => { describe('createContent', () => { it('should return created content', () => { - const content1 = CommitMergeRequests.createContent([{ iid: 1, path: '/path1', title: 'foo' }, { iid: 2, path: '/path2', title: 'baz' }])[0]; + const content1 = CommitMergeRequests.createContent([ + { iid: 1, path: '/path1', title: 'foo' }, + { iid: 2, path: '/path2', title: 'baz' }, + ])[0]; + expect(content1.tagName).toEqual('SPAN'); expect(content1.childElementCount).toEqual(4); const content2 = CommitMergeRequests.createContent([])[0]; + expect(content2.tagName).toEqual('SPAN'); expect(content2.childElementCount).toEqual(0); expect(content2.innerText).toEqual('No related merge requests found'); @@ -26,6 +31,7 @@ describe('CommitMergeRequests', () => { describe('createHeader', () => { it('should return created header', () => { const header = CommitMergeRequests.createHeader(0, 1)[0]; + expect(header.tagName).toEqual('SPAN'); expect(header.innerText).toEqual('1 merge request'); }); @@ -34,6 +40,7 @@ describe('CommitMergeRequests', () => { describe('createItem', () => { it('should return created item', () => { const item = CommitMergeRequests.createItem({ iid: 1, path: '/path', title: 'foo' })[0]; + expect(item.tagName).toEqual('SPAN'); expect(item.childElementCount).toEqual(2); expect(item.children[0].tagName).toEqual('A'); @@ -44,6 +51,7 @@ describe('CommitMergeRequests', () => { describe('createLink', () => { it('should return created link', () => { const link = CommitMergeRequests.createLink({ iid: 1, path: '/path', title: 'foo' })[0]; + expect(link.tagName).toEqual('A'); expect(link.href).toMatch(/\/path$/); expect(link.innerText).toEqual('!1'); @@ -53,6 +61,7 @@ describe('CommitMergeRequests', () => { describe('createTitle', () => { it('should return created title', () => { const title = CommitMergeRequests.createTitle({ iid: 1, path: '/path', title: 'foo' })[0]; + expect(title.tagName).toEqual('SPAN'); expect(title.innerText).toEqual('foo'); }); diff --git a/spec/javascripts/create_item_dropdown_spec.js b/spec/javascripts/create_item_dropdown_spec.js index ee26122be12..9cf72d7c55b 100644 --- a/spec/javascripts/create_item_dropdown_spec.js +++ b/spec/javascripts/create_item_dropdown_spec.js @@ -1,19 +1,23 @@ import $ from 'jquery'; import CreateItemDropdown from '~/create_item_dropdown'; -const DROPDOWN_ITEM_DATA = [{ - title: 'one', - id: 'one', - text: 'one', -}, { - title: 'two', - id: 'two', - text: 'two', -}, { - title: 'three', - id: 'three', - text: 'three', -}]; +const DROPDOWN_ITEM_DATA = [ + { + title: 'one', + id: 'one', + text: 'one', + }, + { + title: 'two', + id: 'two', + text: 'two', + }, + { + title: 'three', + id: 'three', + text: 'three', + }, +]; describe('CreateItemDropdown', () => { preloadFixtures('static/create_item_dropdown.html.raw'); @@ -23,7 +27,8 @@ describe('CreateItemDropdown', () => { function createItemAndClearInput(text) { // Filter for the new item - $wrapperEl.find('.dropdown-input-field') + $wrapperEl + .find('.dropdown-input-field') .val(text) .trigger('input'); @@ -32,7 +37,8 @@ describe('CreateItemDropdown', () => { $createButton.click(); // Clear out the filter - $wrapperEl.find('.dropdown-input-field') + $wrapperEl + .find('.dropdown-input-field') .val('') .trigger('input'); } @@ -63,6 +69,7 @@ describe('CreateItemDropdown', () => { $('.js-dropdown-menu-toggle').click(); const $itemEls = $wrapperEl.find('.js-dropdown-content a'); + expect($itemEls.length).toEqual(DROPDOWN_ITEM_DATA.length); }); }); @@ -84,7 +91,8 @@ describe('CreateItemDropdown', () => { $('.js-dropdown-menu-toggle').click(); // Filter for the new item - $wrapperEl.find('.dropdown-input-field') + $wrapperEl + .find('.dropdown-input-field') .val(NEW_ITEM_TEXT) .trigger('input'); }); @@ -106,6 +114,7 @@ describe('CreateItemDropdown', () => { createItemAndClearInput(NEW_ITEM_TEXT); const $itemEls = $wrapperEl.find('.js-dropdown-content a'); + expect($itemEls.length).toEqual(1 + DROPDOWN_ITEM_DATA.length); expect($($itemEls.get(DROPDOWN_ITEM_DATA.length)).text()).toEqual(NEW_ITEM_TEXT); }); @@ -114,6 +123,7 @@ describe('CreateItemDropdown', () => { createItemAndClearInput(DROPDOWN_ITEM_DATA[0].text); const $itemEls = $wrapperEl.find('.js-dropdown-content a'); + expect($itemEls.length).toEqual(DROPDOWN_ITEM_DATA.length); }); }); @@ -137,16 +147,16 @@ describe('CreateItemDropdown', () => { $('.js-dropdown-menu-toggle').click(); // Filter for an item - filterInput - .val('one') - .trigger('input'); + filterInput.val('one').trigger('input'); const $itemElsAfterFilter = $wrapperEl.find('.js-dropdown-content a'); + expect($itemElsAfterFilter.length).toEqual(1); createItemDropdown.clearDropdown(); const $itemElsAfterClear = $wrapperEl.find('.js-dropdown-content a'); + expect($itemElsAfterClear.length).toEqual(0); expect(filterInput.val()).toEqual(''); }); @@ -176,6 +186,7 @@ describe('CreateItemDropdown', () => { createItemAndClearInput('new-item'); const $itemEls = $wrapperEl.find('.js-dropdown-content a'); + expect($itemEls.length).toEqual(1 + DROPDOWN_ITEM_DATA.length); expect($($itemEls[3]).text()).toEqual('new-item-text'); expect($wrapperEl.find('.dropdown-toggle-text').text()).toEqual('new-item-title'); diff --git a/spec/javascripts/create_merge_request_dropdown_spec.js b/spec/javascripts/create_merge_request_dropdown_spec.js index b229765a8c5..00fe3f451f5 100644 --- a/spec/javascripts/create_merge_request_dropdown_spec.js +++ b/spec/javascripts/create_merge_request_dropdown_spec.js @@ -59,6 +59,7 @@ describe('CreateMergeRequestDropdown', () => { expect(dropdown.createBranchPath).toBe( `${TEST_HOST}/branches?branch_name=contains%23hash&issue=42`, ); + expect(dropdown.createMrPath).toBe( `${TEST_HOST}/create_merge_request?branch_name=contains%23hash&ref=master`, ); diff --git a/spec/javascripts/cycle_analytics/banner_spec.js b/spec/javascripts/cycle_analytics/banner_spec.js index 2815bdba0c2..3ce2c3c4f06 100644 --- a/spec/javascripts/cycle_analytics/banner_spec.js +++ b/spec/javascripts/cycle_analytics/banner_spec.js @@ -17,19 +17,20 @@ describe('Cycle analytics banner', () => { }); it('should render cycle analytics information', () => { - expect( - vm.$el.querySelector('h4').textContent.trim(), - ).toEqual('Introducing Cycle Analytics'); + expect(vm.$el.querySelector('h4').textContent.trim()).toEqual('Introducing Cycle Analytics'); expect( - vm.$el.querySelector('p').textContent.trim().replace(/[\r\n]+/g, ' '), - ).toContain('Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.'); - expect( - vm.$el.querySelector('a').textContent.trim(), - ).toEqual('Read more'); - expect( - vm.$el.querySelector('a').getAttribute('href'), - ).toEqual('path'); + vm.$el + .querySelector('p') + .textContent.trim() + .replace(/[\r\n]+/g, ' '), + ).toContain( + 'Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.', + ); + + expect(vm.$el.querySelector('a').textContent.trim()).toEqual('Read more'); + + expect(vm.$el.querySelector('a').getAttribute('href')).toEqual('path'); }); it('should emit an event when close button is clicked', () => { diff --git a/spec/javascripts/datetime_utility_spec.js b/spec/javascripts/datetime_utility_spec.js index 24e4871ce44..9fedbcc4c25 100644 --- a/spec/javascripts/datetime_utility_spec.js +++ b/spec/javascripts/datetime_utility_spec.js @@ -24,36 +24,43 @@ describe('Date time utils', () => { describe('get day name', () => { it('should return Sunday', () => { const day = datetimeUtility.getDayName(new Date('07/17/2016')); + expect(day).toBe('Sunday'); }); it('should return Monday', () => { const day = datetimeUtility.getDayName(new Date('07/18/2016')); + expect(day).toBe('Monday'); }); it('should return Tuesday', () => { const day = datetimeUtility.getDayName(new Date('07/19/2016')); + expect(day).toBe('Tuesday'); }); it('should return Wednesday', () => { const day = datetimeUtility.getDayName(new Date('07/20/2016')); + expect(day).toBe('Wednesday'); }); it('should return Thursday', () => { const day = datetimeUtility.getDayName(new Date('07/21/2016')); + expect(day).toBe('Thursday'); }); it('should return Friday', () => { const day = datetimeUtility.getDayName(new Date('07/22/2016')); + expect(day).toBe('Friday'); }); it('should return Saturday', () => { const day = datetimeUtility.getDayName(new Date('07/23/2016')); + expect(day).toBe('Saturday'); }); }); @@ -63,6 +70,7 @@ describe('Date time utils', () => { const firstDay = new Date('07/01/2016'); const secondDay = new Date('07/08/2016'); const difference = datetimeUtility.getDayDifference(firstDay, secondDay); + expect(difference).toBe(7); }); @@ -70,6 +78,7 @@ describe('Date time utils', () => { const firstDay = new Date('07/01/2016'); const secondDay = new Date('08/01/2016'); const difference = datetimeUtility.getDayDifference(firstDay, secondDay); + expect(difference).toBe(31); }); @@ -77,6 +86,7 @@ describe('Date time utils', () => { const firstDay = new Date('07/02/2015'); const secondDay = new Date('07/01/2016'); const difference = datetimeUtility.getDayDifference(firstDay, secondDay); + expect(difference).toBe(365); }); }); @@ -156,6 +166,7 @@ describe('getTimeframeWindowFrom', () => { new Date(2018, 4, 31), ]; const timeframe = datetimeUtility.getTimeframeWindowFrom(startDate, 5); + expect(timeframe.length).toBe(5); timeframe.forEach((timeframeItem, index) => { expect(timeframeItem.getFullYear()).toBe(mockTimeframe[index].getFullYear()); diff --git a/spec/javascripts/deploy_keys/components/app_spec.js b/spec/javascripts/deploy_keys/components/app_spec.js index cd147bb2935..f81c0cb7124 100644 --- a/spec/javascripts/deploy_keys/components/app_spec.js +++ b/spec/javascripts/deploy_keys/components/app_spec.js @@ -10,7 +10,7 @@ describe('Deploy keys app component', () => { let vm; let mock; - beforeEach((done) => { + beforeEach(done => { // set up axios mock before component mock = new MockAdapter(axios); mock.onGet(`${TEST_HOST}/dummy/`).replyOnce(200, data); @@ -60,6 +60,7 @@ describe('Deploy keys app component', () => { expect(textContent('.js-deployKeys-tab-available_project_keys')).toContain( 'Privately accessible deploy keys', ); + expect(textContent('.js-deployKeys-tab-public_keys')).toContain( 'Publicly accessible deploy keys', ); @@ -67,9 +68,11 @@ describe('Deploy keys app component', () => { expect(textContent('.js-deployKeys-tab-enabled_keys .badge')).toBe( `${vm.store.keys.enabled_keys.length}`, ); + expect(textContent('.js-deployKeys-tab-available_project_keys .badge')).toBe( `${vm.store.keys.available_project_keys.length}`, ); + expect(textContent('.js-deployKeys-tab-public_keys .badge')).toBe( `${vm.store.keys.public_keys.length}`, ); diff --git a/spec/javascripts/deploy_keys/components/key_spec.js b/spec/javascripts/deploy_keys/components/key_spec.js index d1de9d132b8..7117dc4a9ee 100644 --- a/spec/javascripts/deploy_keys/components/key_spec.js +++ b/spec/javascripts/deploy_keys/components/key_spec.js @@ -82,6 +82,7 @@ describe('Deploy keys key', () => { it('shows expandable button if more than two projects', () => { const labels = vm.$el.querySelectorAll('.deploy-project-label'); + expect(labels.length).toBe(2); expect(labels[1].textContent).toContain('others'); expect(labels[1].getAttribute('data-original-title')).toContain('Expand'); @@ -93,6 +94,7 @@ describe('Deploy keys key', () => { Vue.nextTick(() => { const labels = vm.$el.querySelectorAll('.deploy-project-label'); + expect(labels.length).toBe(length); expect(labels[1].textContent).not.toContain(`+${length} others`); expect(labels[1].getAttribute('data-original-title')).not.toContain('Expand'); @@ -105,6 +107,7 @@ describe('Deploy keys key', () => { Vue.nextTick(() => { const labels = vm.$el.querySelectorAll('.deploy-project-label'); + expect(labels.length).toBe(2); expect(labels[1].textContent).toContain( vm.deployKey.deploy_keys_projects[1].project.full_name, diff --git a/spec/javascripts/diff_comments_store_spec.js b/spec/javascripts/diff_comments_store_spec.js index c6f2e66cebd..a6d363ce88e 100644 --- a/spec/javascripts/diff_comments_store_spec.js +++ b/spec/javascripts/diff_comments_store_spec.js @@ -26,6 +26,7 @@ describe('New discussion', () => { it('creates new discussion', () => { expect(Object.keys(CommentsStore.state).length).toBe(0); createDiscussion(); + expect(Object.keys(CommentsStore.state).length).toBe(1); }); @@ -34,6 +35,7 @@ describe('New discussion', () => { createDiscussion(2); const discussion = CommentsStore.state['a']; + expect(Object.keys(discussion.notes).length).toBe(2); }); }); @@ -46,6 +48,7 @@ describe('Get note', () => { it('gets note by ID', () => { const note = CommentsStore.get('a', 1); + expect(note).toBeDefined(); expect(note.id).toBe(1); }); @@ -59,17 +62,20 @@ describe('Delete discussion', () => { it('deletes discussion by ID', () => { CommentsStore.delete('a', 1); + expect(Object.keys(CommentsStore.state).length).toBe(0); }); it('deletes discussion when no more notes', () => { createDiscussion(); createDiscussion(2); + expect(Object.keys(CommentsStore.state).length).toBe(1); expect(Object.keys(CommentsStore.state['a'].notes).length).toBe(2); CommentsStore.delete('a', 1); CommentsStore.delete('a', 2); + expect(Object.keys(CommentsStore.state).length).toBe(0); }); }); @@ -84,6 +90,7 @@ describe('Update note', () => { CommentsStore.update('a', 1, false, 'test'); const note = CommentsStore.get('a', 1); + expect(note.resolved).toBe(false); }); }); @@ -96,6 +103,7 @@ describe('Discussion resolved', () => { it('is resolved with single note', () => { const discussion = CommentsStore.state['a']; + expect(discussion.isResolved()).toBe(true); }); @@ -118,6 +126,7 @@ describe('Discussion resolved', () => { createDiscussion(2, false); discussion.resolveAllNotes(); + expect(discussion.isResolved()).toBe(true); }); @@ -126,6 +135,7 @@ describe('Discussion resolved', () => { createDiscussion(2); discussion.unResolveAllNotes(); + expect(discussion.isResolved()).toBe(false); }); }); diff --git a/spec/javascripts/diffs/components/app_spec.js b/spec/javascripts/diffs/components/app_spec.js index a3a714678af..3c9b5ee0176 100644 --- a/spec/javascripts/diffs/components/app_spec.js +++ b/spec/javascripts/diffs/components/app_spec.js @@ -44,8 +44,7 @@ describe('diffs/components/app', () => { it('shows comments message, with commit', done => { vm.$store.state.diffs.commit = getDiffWithCommit().commit; - vm - .$nextTick() + vm.$nextTick() .then(() => { expect(vm.$el).toContainText('Only comments from the following commit are shown below'); expect(vm.$el).toContainElement('.blob-commit-info'); @@ -58,8 +57,7 @@ describe('diffs/components/app', () => { vm.$store.state.diffs.mergeRequestDiff = { latest: false }; vm.$store.state.diffs.targetBranch = 'master'; - vm - .$nextTick() + vm.$nextTick() .then(() => { expect(vm.$el).toContainText( "Not all comments are displayed because you're viewing an old version of the diff.", @@ -72,8 +70,7 @@ describe('diffs/components/app', () => { it('shows comments message, with startVersion', done => { vm.$store.state.diffs.startVersion = 'test'; - vm - .$nextTick() + vm.$nextTick() .then(() => { expect(vm.$el).toContainText( "Not all comments are displayed because you're comparing two versions of the diff.", diff --git a/spec/javascripts/diffs/components/commit_item_spec.js b/spec/javascripts/diffs/components/commit_item_spec.js index 8c3376c0eb3..7606847ada9 100644 --- a/spec/javascripts/diffs/components/commit_item_spec.js +++ b/spec/javascripts/diffs/components/commit_item_spec.js @@ -14,7 +14,8 @@ const TEST_PIPELINE_STATUS_PATH = `${TEST_HOST}/pipeline/status`; const getTitleElement = vm => vm.$el.querySelector('.commit-row-message.item-title'); const getDescElement = vm => vm.$el.querySelector('pre.commit-row-description'); -const getDescExpandElement = vm => vm.$el.querySelector('.commit-content .text-expander.js-toggle-button'); +const getDescExpandElement = vm => + vm.$el.querySelector('.commit-content .text-expander.js-toggle-button'); const getShaElement = vm => vm.$el.querySelector('.commit-sha-group'); const getAvatarElement = vm => vm.$el.querySelector('.user-avatar-link'); const getCommitterElement = vm => vm.$el.querySelector('.commiter'); diff --git a/spec/javascripts/diffs/components/diff_file_header_spec.js b/spec/javascripts/diffs/components/diff_file_header_spec.js index 1f7d5f42322..0192d583c6c 100644 --- a/spec/javascripts/diffs/components/diff_file_header_spec.js +++ b/spec/javascripts/diffs/components/diff_file_header_spec.js @@ -98,6 +98,7 @@ describe('diff_file_header', () => { props.discussionPath = 'link://to/discussion'; vm = mountComponentWithStore(Component, { props, store }); const href = vm.$el.querySelector('.js-title-wrapper').getAttribute('href'); + expect(href).toBe(vm.discussionPath); }); }); @@ -270,6 +271,7 @@ describe('diff_file_header', () => { it('displays an file icon in the title', () => { vm = mountComponentWithStore(Component, { props, store }); + expect(vm.$el.querySelector('svg.js-file-icon use').getAttribute('xlink:href')).toContain( 'ruby', ); @@ -312,8 +314,11 @@ describe('diff_file_header', () => { vm = mountComponentWithStore(Component, { props, store }); const button = vm.$el.querySelector('.btn-clipboard'); + expect(button).not.toBe(null); - expect(button.dataset.clipboardText).toBe('{"text":"files/ruby/popen.rb","gfm":"`files/ruby/popen.rb`"}'); + expect(button.dataset.clipboardText).toBe( + '{"text":"files/ruby/popen.rb","gfm":"`files/ruby/popen.rb`"}', + ); }); describe('file mode', () => { @@ -323,6 +328,7 @@ describe('diff_file_header', () => { vm = mountComponentWithStore(Component, { props, store }); const { fileMode } = vm.$refs; + expect(fileMode).not.toBe(undefined); expect(fileMode).toContainText(props.diffFile.aMode); expect(fileMode).toContainText(props.diffFile.bMode); @@ -334,6 +340,7 @@ describe('diff_file_header', () => { vm = mountComponentWithStore(Component, { props, store }); const { fileMode } = vm.$refs; + expect(fileMode).toBe(undefined); }); }); diff --git a/spec/javascripts/diffs/components/diff_file_spec.js b/spec/javascripts/diffs/components/diff_file_spec.js index b8d4b31ee04..882ad3685a2 100644 --- a/spec/javascripts/diffs/components/diff_file_spec.js +++ b/spec/javascripts/diffs/components/diff_file_spec.js @@ -97,8 +97,11 @@ describe('DiffFile', () => { expect(vm.$el.innerText).toContain( 'This source diff could not be displayed because it is too large', ); + expect(vm.$el.querySelector('.js-too-large-diff')).toBeDefined(); - expect(vm.$el.querySelector('.js-too-large-diff a').href.indexOf(BLOB_LINK)).toBeGreaterThan(-1); + expect( + vm.$el.querySelector('.js-too-large-diff a').href.indexOf(BLOB_LINK), + ).toBeGreaterThan(-1); done(); }); diff --git a/spec/javascripts/diffs/components/diff_gutter_avatars_spec.js b/spec/javascripts/diffs/components/diff_gutter_avatars_spec.js index 0085a16815a..ad2605a5c5c 100644 --- a/spec/javascripts/diffs/components/diff_gutter_avatars_spec.js +++ b/spec/javascripts/diffs/components/diff_gutter_avatars_spec.js @@ -23,6 +23,7 @@ describe('DiffGutterAvatars', () => { it('should return false when all discussions are not expanded', () => { component.discussions[0].expanded = false; + expect(component.discussionsExpanded).toEqual(false); }); }); @@ -56,6 +57,7 @@ describe('DiffGutterAvatars', () => { it('should return empty string if there is no discussion', () => { component.discussions = []; + expect(component.moreText).toEqual(''); }); }); diff --git a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js index f36454cc23e..6972e0ee913 100644 --- a/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js +++ b/spec/javascripts/diffs/components/diff_line_gutter_content_spec.js @@ -31,12 +31,14 @@ describe('DiffLineGutterContent', () => { it('should prepend # to lineCode', () => { const lineCode = 'LC_42'; const component = createComponent(); + expect(component.lineHref).toEqual(`#${lineCode}`); }); it('should return # if there is no lineCode', () => { const component = createComponent(); component.line.lineCode = ''; + expect(component.lineHref).toEqual('#'); }); }); @@ -44,6 +46,7 @@ describe('DiffLineGutterContent', () => { describe('discussions, hasDiscussions, shouldShowAvatarsOnGutter', () => { it('should return empty array when there is no discussion', () => { const component = createComponent(); + expect(component.hasDiscussions).toEqual(false); expect(component.shouldShowAvatarsOnGutter).toEqual(false); }); diff --git a/spec/javascripts/diffs/components/diff_line_note_form_spec.js b/spec/javascripts/diffs/components/diff_line_note_form_spec.js index f31fc1f0e2b..c39b54d9cc9 100644 --- a/spec/javascripts/diffs/components/diff_line_note_form_spec.js +++ b/spec/javascripts/diffs/components/diff_line_note_form_spec.js @@ -36,6 +36,7 @@ describe('DiffLineNoteForm', () => { spyOn(window, 'confirm').and.returnValue(false); component.handleCancelCommentForm(true, true); + expect(window.confirm).toHaveBeenCalled(); }); @@ -43,9 +44,11 @@ describe('DiffLineNoteForm', () => { spyOn(window, 'confirm').and.returnValue(false); component.handleCancelCommentForm(true, false); + expect(window.confirm).not.toHaveBeenCalled(); component.handleCancelCommentForm(false, true); + expect(window.confirm).not.toHaveBeenCalled(); }); @@ -60,6 +63,7 @@ describe('DiffLineNoteForm', () => { expect(component.cancelCommentForm).toHaveBeenCalledWith({ lineCode: diffLines[0].lineCode, }); + expect(component.resetAutoSave).toHaveBeenCalled(); done(); diff --git a/spec/javascripts/diffs/mock_data/diff_with_commit.js b/spec/javascripts/diffs/mock_data/diff_with_commit.js index 98393a20583..bee04fa4932 100644 --- a/spec/javascripts/diffs/mock_data/diff_with_commit.js +++ b/spec/javascripts/diffs/mock_data/diff_with_commit.js @@ -5,8 +5,5 @@ const FIXTURE = 'merge_request_diffs/with_commit.json'; preloadFixtures(FIXTURE); export default function getDiffWithCommit() { - return convertObjectPropsToCamelCase( - getJSONFixture(FIXTURE), - { deep: true }, - ); + return convertObjectPropsToCamelCase(getJSONFixture(FIXTURE), { deep: true }); } diff --git a/spec/javascripts/diffs/store/getters_spec.js b/spec/javascripts/diffs/store/getters_spec.js index cfeaaec6980..807a9e3baf0 100644 --- a/spec/javascripts/diffs/store/getters_spec.js +++ b/spec/javascripts/diffs/store/getters_spec.js @@ -52,11 +52,13 @@ describe('Diffs Module Getters', () => { describe('areAllFilesCollapsed', () => { it('returns true when all files are collapsed', () => { localState.diffFiles = [{ collapsed: true }, { collapsed: true }]; + expect(getters.areAllFilesCollapsed(localState)).toEqual(true); }); it('returns false when at least one file is not collapsed', () => { localState.diffFiles = [{ collapsed: false }, { collapsed: true }]; + expect(getters.areAllFilesCollapsed(localState)).toEqual(false); }); }); @@ -244,6 +246,7 @@ describe('Diffs Module Getters', () => { it('returns false when no line discussions were found', () => { line.discussions = []; + expect(getters.shouldRenderInlineCommentRow(localState)(line)).toEqual(false); }); @@ -288,6 +291,7 @@ describe('Diffs Module Getters', () => { it('returns null if no matching file is found', () => { localState.diffFiles = []; + expect(getters.getDiffFileByHash(localState)('123')).toBeUndefined(); }); }); diff --git a/spec/javascripts/diffs/store/mutations_spec.js b/spec/javascripts/diffs/store/mutations_spec.js index 0b712055956..b7e28391419 100644 --- a/spec/javascripts/diffs/store/mutations_spec.js +++ b/spec/javascripts/diffs/store/mutations_spec.js @@ -12,6 +12,7 @@ describe('DiffsStoreMutations', () => { const projectPath = '/root/project'; mutations[types.SET_BASE_CONFIG](state, { endpoint, projectPath }); + expect(state.endpoint).toEqual(endpoint); expect(state.projectPath).toEqual(projectPath); }); @@ -22,6 +23,7 @@ describe('DiffsStoreMutations', () => { const state = {}; mutations[types.SET_LOADING](state, false); + expect(state.isLoading).toEqual(false); }); }); @@ -48,6 +50,7 @@ describe('DiffsStoreMutations', () => { const state = {}; mutations[types.SET_DIFF_VIEW_TYPE](state, INLINE_DIFF_VIEW_TYPE); + expect(state.diffViewType).toEqual(INLINE_DIFF_VIEW_TYPE); }); }); @@ -58,6 +61,7 @@ describe('DiffsStoreMutations', () => { const lineCode = 'FDE'; mutations[types.ADD_COMMENT_FORM_LINE](state, { lineCode }); + expect(state.diffLineCommentForms[lineCode]).toBeTruthy(); }); }); @@ -68,9 +72,11 @@ describe('DiffsStoreMutations', () => { const lineCode = 'FDE'; mutations[types.ADD_COMMENT_FORM_LINE](state, { lineCode }); + expect(state.diffLineCommentForms[lineCode]).toBeTruthy(); mutations[types.REMOVE_COMMENT_FORM_LINE](state, { lineCode }); + expect(state.diffLineCommentForms[lineCode]).toBeUndefined(); }); }); @@ -83,6 +89,7 @@ describe('DiffsStoreMutations', () => { const state = { expandAllFiles: true, diffFiles: [diffFile] }; mutations[types.EXPAND_ALL_FILES](state); + expect(state.diffFiles[0].collapsed).toEqual(false); }); }); @@ -118,11 +125,13 @@ describe('DiffsStoreMutations', () => { options.lineNumbers, options.params.bottom, ); + expect(lineRefSpy).toHaveBeenCalledWith( options.contextLines, options.lineNumbers, options.params.bottom, ); + expect(addContextLinesSpy).toHaveBeenCalledWith({ inlineLines: diffFile.highlightedDiffLines, parallelLines: diffFile.parallelDiffLines, @@ -142,6 +151,7 @@ describe('DiffsStoreMutations', () => { const data = { diff_files: [{ file_hash: fileHash, extra_field: 1, existingField: 1 }] }; mutations[types.ADD_COLLAPSED_DIFFS](state, { file: state.diffFiles[1], data }); + expect(spy).toHaveBeenCalledWith(data, { deep: true }); expect(state.diffFiles[1].fileHash).toEqual(fileHash); @@ -345,6 +355,7 @@ describe('DiffsStoreMutations', () => { fileHash: 'ABC', lineCode: 'ABC_1', }); + expect(state.diffFiles[0].parallelDiffLines[0].left.discussions.length).toEqual(0); expect(state.diffFiles[0].highlightedDiffLines[0].discussions.length).toEqual(0); }); diff --git a/spec/javascripts/diffs/store/utils_spec.js b/spec/javascripts/diffs/store/utils_spec.js index 257270a91ec..ef367fc09fa 100644 --- a/spec/javascripts/diffs/store/utils_spec.js +++ b/spec/javascripts/diffs/store/utils_spec.js @@ -62,10 +62,12 @@ describe('DiffsStoreUtils', () => { const atParallelIndex = diffFile.parallelDiffLines[parallelIndex]; utils.removeMatchLine(diffFile, lineNumbers, false); + expect(diffFile.highlightedDiffLines[inlineIndex]).not.toEqual(atInlineIndex); expect(diffFile.parallelDiffLines[parallelIndex]).not.toEqual(atParallelIndex); utils.removeMatchLine(diffFile, lineNumbers, true); + expect(diffFile.highlightedDiffLines[inlineIndex + 1]).not.toEqual(atInlineIndex); expect(diffFile.parallelDiffLines[parallelIndex + 1]).not.toEqual(atParallelIndex); }); @@ -87,11 +89,13 @@ describe('DiffsStoreUtils', () => { }; utils.addContextLines(options); + expect(inlineLines[inlineLines.length - 1]).toEqual(contextLines[0]); expect(parallelLines[parallelLines.length - 1]).toEqual(normalizedParallelLine); delete options.bottom; utils.addContextLines(options); + expect(inlineLines[inlineIndex]).toEqual(contextLines[0]); expect(parallelLines[parallelIndex]).toEqual(normalizedParallelLine); }); @@ -274,6 +278,7 @@ describe('DiffsStoreUtils', () => { }; utils.trimFirstCharOfLineContent(lineObj); + expect(lineObj).toEqual({ discussions: [], richText: ' diff' }); }); @@ -288,19 +293,23 @@ describe('DiffsStoreUtils', () => { utils.prepareDiffData(preparedDiff); const firstParallelDiffLine = preparedDiff.diffFiles[0].parallelDiffLines[2]; + expect(firstParallelDiffLine.left.discussions.length).toBe(0); expect(firstParallelDiffLine.left).not.toHaveAttr('text'); expect(firstParallelDiffLine.right.discussions.length).toBe(0); expect(firstParallelDiffLine.right).not.toHaveAttr('text'); const firstParallelChar = firstParallelDiffLine.right.richText.charAt(0); + expect(firstParallelChar).not.toBe(' '); expect(firstParallelChar).not.toBe('+'); expect(firstParallelChar).not.toBe('-'); const checkLine = preparedDiff.diffFiles[0].highlightedDiffLines[0]; + expect(checkLine.discussions.length).toBe(0); expect(checkLine).not.toHaveAttr('text'); const firstChar = checkLine.richText.charAt(0); + expect(firstChar).not.toBe(' '); expect(firstChar).not.toBe('+'); expect(firstChar).not.toBe('-'); diff --git a/spec/javascripts/dirty_submit/dirty_submit_collection_spec.js b/spec/javascripts/dirty_submit/dirty_submit_collection_spec.js index 87a26183b63..08ffc44605f 100644 --- a/spec/javascripts/dirty_submit/dirty_submit_collection_spec.js +++ b/spec/javascripts/dirty_submit/dirty_submit_collection_spec.js @@ -15,9 +15,13 @@ describe('DirtySubmitCollection', () => { expect(submit.disabled).toBe(true); return setInput(input, `${originalValue} changes`) - .then(() => expect(submit.disabled).toBe(false)) + .then(() => { + expect(submit.disabled).toBe(false); + }) .then(() => setInput(input, originalValue)) - .then(() => expect(submit.disabled).toBe(true)) + .then(() => { + expect(submit.disabled).toBe(true); + }) .then(done) .catch(done.fail); }); diff --git a/spec/javascripts/dirty_submit/dirty_submit_form_spec.js b/spec/javascripts/dirty_submit/dirty_submit_form_spec.js index 86d53fa984a..b7b29190c31 100644 --- a/spec/javascripts/dirty_submit/dirty_submit_form_spec.js +++ b/spec/javascripts/dirty_submit/dirty_submit_form_spec.js @@ -1,4 +1,3 @@ - import DirtySubmitForm from '~/dirty_submit/dirty_submit_form'; import { setInput, createForm } from './helper'; @@ -12,9 +11,13 @@ describe('DirtySubmitForm', () => { expect(submit.disabled).toBe(true); return setInput(input, `${originalValue} changes`) - .then(() => expect(submit.disabled).toBe(false)) + .then(() => { + expect(submit.disabled).toBe(false); + }) .then(() => setInput(input, originalValue)) - .then(() => expect(submit.disabled).toBe(true)) + .then(() => { + expect(submit.disabled).toBe(true); + }) .then(done) .catch(done.fail); }); diff --git a/spec/javascripts/droplab/drop_down_spec.js b/spec/javascripts/droplab/drop_down_spec.js index 896a04a1a07..18ab03653f4 100644 --- a/spec/javascripts/droplab/drop_down_spec.js +++ b/spec/javascripts/droplab/drop_down_spec.js @@ -2,9 +2,9 @@ import DropDown from '~/droplab/drop_down'; import utils from '~/droplab/utils'; import { SELECTED_CLASS } from '~/droplab/constants'; -describe('DropLab DropDown', function () { - describe('class constructor', function () { - beforeEach(function () { +describe('DropLab DropDown', function() { + describe('class constructor', function() { + beforeEach(function() { spyOn(DropDown.prototype, 'getItems'); spyOn(DropDown.prototype, 'initTemplateString'); spyOn(DropDown.prototype, 'addEvents'); @@ -13,32 +13,32 @@ describe('DropLab DropDown', function () { this.dropdown = new DropDown(this.list); }); - it('sets the .hidden property to true', function () { + it('sets the .hidden property to true', function() { expect(this.dropdown.hidden).toBe(true); }); - it('sets the .list property', function () { + it('sets the .list property', function() { expect(this.dropdown.list).toBe(this.list); }); - it('calls .getItems', function () { + it('calls .getItems', function() { expect(DropDown.prototype.getItems).toHaveBeenCalled(); }); - it('calls .initTemplateString', function () { + it('calls .initTemplateString', function() { expect(DropDown.prototype.initTemplateString).toHaveBeenCalled(); }); - it('calls .addEvents', function () { + it('calls .addEvents', function() { expect(DropDown.prototype.addEvents).toHaveBeenCalled(); }); - it('sets the .initialState property to the .list.innerHTML', function () { + it('sets the .initialState property to the .list.innerHTML', function() { expect(this.dropdown.initialState).toBe(this.list.innerHTML); }); - describe('if the list argument is a string', function () { - beforeEach(function () { + describe('if the list argument is a string', function() { + beforeEach(function() { this.element = {}; this.selector = '.selector'; @@ -47,18 +47,18 @@ describe('DropLab DropDown', function () { this.dropdown = new DropDown(this.selector); }); - it('calls .querySelector with the selector string', function () { + it('calls .querySelector with the selector string', function() { expect(Document.prototype.querySelector).toHaveBeenCalledWith(this.selector); }); - it('sets the .list property element', function () { + it('sets the .list property element', function() { expect(this.dropdown.list).toBe(this.element); }); }); }); - describe('getItems', function () { - beforeEach(function () { + describe('getItems', function() { + beforeEach(function() { this.list = { querySelectorAll: () => {} }; this.dropdown = { list: this.list }; this.nodeList = []; @@ -68,37 +68,37 @@ describe('DropLab DropDown', function () { this.getItems = DropDown.prototype.getItems.call(this.dropdown); }); - it('calls .querySelectorAll with a list item query', function () { + it('calls .querySelectorAll with a list item query', function() { expect(this.list.querySelectorAll).toHaveBeenCalledWith('li'); }); - it('sets the .items property to the returned list items', function () { + it('sets the .items property to the returned list items', function() { expect(this.dropdown.items).toEqual(jasmine.any(Array)); }); - it('returns the .items', function () { + it('returns the .items', function() { expect(this.getItems).toEqual(jasmine.any(Array)); }); }); - describe('initTemplateString', function () { - beforeEach(function () { + describe('initTemplateString', function() { + beforeEach(function() { this.items = [{ outerHTML: '<a></a>' }, { outerHTML: '<img>' }]; this.dropdown = { items: this.items }; DropDown.prototype.initTemplateString.call(this.dropdown); }); - it('should set .templateString to the last items .outerHTML', function () { + it('should set .templateString to the last items .outerHTML', function() { expect(this.dropdown.templateString).toBe(this.items[1].outerHTML); }); - it('should not set .templateString to a non-last items .outerHTML', function () { + it('should not set .templateString to a non-last items .outerHTML', function() { expect(this.dropdown.templateString).not.toBe(this.items[0].outerHTML); }); - describe('if .items is not set', function () { - beforeEach(function () { + describe('if .items is not set', function() { + beforeEach(function() { this.dropdown = { getItems: () => {} }; spyOn(this.dropdown, 'getItems').and.returnValue([]); @@ -106,26 +106,26 @@ describe('DropLab DropDown', function () { DropDown.prototype.initTemplateString.call(this.dropdown); }); - it('should call .getItems', function () { + it('should call .getItems', function() { expect(this.dropdown.getItems).toHaveBeenCalled(); }); }); - describe('if items array is empty', function () { - beforeEach(function () { + describe('if items array is empty', function() { + beforeEach(function() { this.dropdown = { items: [] }; DropDown.prototype.initTemplateString.call(this.dropdown); }); - it('should set .templateString to an empty string', function () { + it('should set .templateString to an empty string', function() { expect(this.dropdown.templateString).toBe(''); }); }); }); - describe('clickEvent', function () { - beforeEach(function () { + describe('clickEvent', function() { + beforeEach(function() { this.classList = jasmine.createSpyObj('classList', ['contains']); this.list = { dispatchEvent: () => {} }; this.dropdown = { @@ -143,7 +143,7 @@ describe('DropLab DropDown', function () { }; this.customEvent = {}; this.dummyListItem = document.createElement('li'); - spyOn(this.event.target, 'closest').and.callFake((selector) => { + spyOn(this.event.target, 'closest').and.callFake(selector => { if (selector === 'li') { return this.dummyListItem; } @@ -159,51 +159,51 @@ describe('DropLab DropDown', function () { this.classList.contains.and.returnValue(false); }); - it('should call event.target.closest', function () { + it('should call event.target.closest', function() { DropDown.prototype.clickEvent.call(this.dropdown, this.event); expect(this.event.target.closest).toHaveBeenCalledWith('.droplab-item-ignore'); expect(this.event.target.closest).toHaveBeenCalledWith('li'); }); - it('should call addSelectedClass', function () { + it('should call addSelectedClass', function() { DropDown.prototype.clickEvent.call(this.dropdown, this.event); expect(this.dropdown.addSelectedClass).toHaveBeenCalledWith(this.dummyListItem); }); - it('should call .preventDefault', function () { + it('should call .preventDefault', function() { DropDown.prototype.clickEvent.call(this.dropdown, this.event); expect(this.event.preventDefault).toHaveBeenCalled(); }); - it('should call .hide', function () { + it('should call .hide', function() { DropDown.prototype.clickEvent.call(this.dropdown, this.event); expect(this.dropdown.hide).toHaveBeenCalled(); }); - it('should construct CustomEvent', function () { + it('should construct CustomEvent', function() { DropDown.prototype.clickEvent.call(this.dropdown, this.event); expect(window.CustomEvent).toHaveBeenCalledWith('click.dl', jasmine.any(Object)); }); - it('should call .dispatchEvent with the customEvent', function () { + it('should call .dispatchEvent with the customEvent', function() { DropDown.prototype.clickEvent.call(this.dropdown, this.event); expect(this.list.dispatchEvent).toHaveBeenCalledWith(this.customEvent); }); - describe('if the target is a UL element', function () { - beforeEach(function () { + describe('if the target is a UL element', function() { + beforeEach(function() { this.event.target = document.createElement('ul'); spyOn(this.event.target, 'closest'); }); - it('should return immediately', function () { + it('should return immediately', function() { DropDown.prototype.clickEvent.call(this.dropdown, this.event); expect(this.event.target.closest).not.toHaveBeenCalled(); @@ -211,8 +211,8 @@ describe('DropLab DropDown', function () { }); }); - describe('if the target has the droplab-item-ignore class', function () { - beforeEach(function () { + describe('if the target has the droplab-item-ignore class', function() { + beforeEach(function() { this.ignoredButton = document.createElement('button'); this.ignoredButton.classList.add('droplab-item-ignore'); this.event.target = this.ignoredButton; @@ -220,7 +220,7 @@ describe('DropLab DropDown', function () { spyOn(this.ignoredButton, 'closest').and.callThrough(); }); - it('does not select element', function () { + it('does not select element', function() { DropDown.prototype.clickEvent.call(this.dropdown, this.event); expect(this.ignoredButton.closest.calls.count()).toBe(1); @@ -229,13 +229,13 @@ describe('DropLab DropDown', function () { }); }); - describe('if no selected element exists', function () { - beforeEach(function () { + describe('if no selected element exists', function() { + beforeEach(function() { this.event.preventDefault.calls.reset(); this.dummyListItem = null; }); - it('should return before .preventDefault is called', function () { + it('should return before .preventDefault is called', function() { DropDown.prototype.clickEvent.call(this.dropdown, this.event); expect(this.event.preventDefault).not.toHaveBeenCalled(); @@ -244,12 +244,12 @@ describe('DropLab DropDown', function () { }); describe('if hideOnClick is false', () => { - beforeEach(function () { + beforeEach(function() { this.dropdown.hideOnClick = false; this.dropdown.hide.calls.reset(); }); - it('should not call .hide', function () { + it('should not call .hide', function() { DropDown.prototype.clickEvent.call(this.dropdown, this.event); expect(this.dropdown.hide).not.toHaveBeenCalled(); @@ -257,8 +257,8 @@ describe('DropLab DropDown', function () { }); }); - describe('addSelectedClass', function () { - beforeEach(function () { + describe('addSelectedClass', function() { + beforeEach(function() { this.items = Array(4).forEach((item, i) => { this.items[i] = { classList: { add: () => {} } }; spyOn(this.items[i].classList, 'add'); @@ -272,17 +272,17 @@ describe('DropLab DropDown', function () { DropDown.prototype.addSelectedClass.call(this.dropdown, this.selected); }); - it('should call .removeSelectedClasses', function () { + it('should call .removeSelectedClasses', function() { expect(this.dropdown.removeSelectedClasses).toHaveBeenCalled(); }); - it('should call .classList.add', function () { + it('should call .classList.add', function() { expect(this.selected.classList.add).toHaveBeenCalledWith(SELECTED_CLASS); }); }); - describe('removeSelectedClasses', function () { - beforeEach(function () { + describe('removeSelectedClasses', function() { + beforeEach(function() { this.items = Array(4); this.items.forEach((item, i) => { this.items[i] = { classList: { add: () => {} } }; @@ -293,14 +293,14 @@ describe('DropLab DropDown', function () { DropDown.prototype.removeSelectedClasses.call(this.dropdown); }); - it('should call .classList.remove for all items', function () { + it('should call .classList.remove for all items', function() { this.items.forEach((item, i) => { expect(this.items[i].classList.add).toHaveBeenCalledWith(SELECTED_CLASS); }); }); - describe('if .items is not set', function () { - beforeEach(function () { + describe('if .items is not set', function() { + beforeEach(function() { this.dropdown = { getItems: () => {} }; spyOn(this.dropdown, 'getItems').and.returnValue([]); @@ -308,14 +308,14 @@ describe('DropLab DropDown', function () { DropDown.prototype.removeSelectedClasses.call(this.dropdown); }); - it('should call .getItems', function () { + it('should call .getItems', function() { expect(this.dropdown.getItems).toHaveBeenCalled(); }); }); }); - describe('addEvents', function () { - beforeEach(function () { + describe('addEvents', function() { + beforeEach(function() { this.list = { addEventListener: () => {}, querySelectorAll: () => [], @@ -328,7 +328,7 @@ describe('DropLab DropDown', function () { }; }); - it('should call .addEventListener', function () { + it('should call .addEventListener', function() { spyOn(this.list, 'addEventListener'); DropDown.prototype.addEvents.call(this.dropdown); @@ -338,8 +338,8 @@ describe('DropLab DropDown', function () { }); }); - describe('setData', function () { - beforeEach(function () { + describe('setData', function() { + beforeEach(function() { this.dropdown = { render: () => {} }; this.data = ['data']; @@ -348,17 +348,17 @@ describe('DropLab DropDown', function () { DropDown.prototype.setData.call(this.dropdown, this.data); }); - it('should set .data', function () { + it('should set .data', function() { expect(this.dropdown.data).toBe(this.data); }); - it('should call .render with the .data', function () { + it('should call .render with the .data', function() { expect(this.dropdown.render).toHaveBeenCalledWith(this.data); }); }); - describe('addData', function () { - beforeEach(function () { + describe('addData', function() { + beforeEach(function() { this.dropdown = { render: () => {}, data: ['data1'] }; this.data = ['data2']; @@ -368,20 +368,20 @@ describe('DropLab DropDown', function () { DropDown.prototype.addData.call(this.dropdown, this.data); }); - it('should call .concat with data', function () { + it('should call .concat with data', function() { expect(Array.prototype.concat).toHaveBeenCalledWith(this.data); }); - it('should set .data with concatination', function () { + it('should set .data with concatination', function() { expect(this.dropdown.data).toEqual(['data1', 'data2']); }); - it('should call .render with the .data', function () { + it('should call .render with the .data', function() { expect(this.dropdown.render).toHaveBeenCalledWith(['data1', 'data2']); }); - describe('if .data is undefined', function () { - beforeEach(function () { + describe('if .data is undefined', function() { + beforeEach(function() { this.dropdown = { render: () => {}, data: undefined }; this.data = ['data2']; @@ -390,14 +390,14 @@ describe('DropLab DropDown', function () { DropDown.prototype.addData.call(this.dropdown, this.data); }); - it('should set .data with concatination', function () { + it('should set .data with concatination', function() { expect(this.dropdown.data).toEqual(['data2']); }); }); }); - describe('render', function () { - beforeEach(function () { + describe('render', function() { + beforeEach(function() { this.list = { querySelector: () => {}, dispatchEvent: () => {} }; this.dropdown = { renderChildren: () => {}, list: this.list }; this.renderableList = {}; @@ -413,45 +413,45 @@ describe('DropLab DropDown', function () { DropDown.prototype.render.call(this.dropdown, this.data); }); - it('should call .map', function () { + it('should call .map', function() { expect(this.data.map).toHaveBeenCalledWith(jasmine.any(Function)); }); - it('should call .renderChildren for each data item', function () { + it('should call .renderChildren for each data item', function() { expect(this.dropdown.renderChildren.calls.count()).toBe(this.data.length); }); - it('sets the renderableList .innerHTML', function () { + it('sets the renderableList .innerHTML', function() { expect(this.renderableList.innerHTML).toBe('01'); }); - it('should call render.dl', function () { + it('should call render.dl', function() { expect(window.CustomEvent).toHaveBeenCalledWith('render.dl', jasmine.any(Object)); }); - it('should call dispatchEvent with the customEvent', function () { + it('should call dispatchEvent with the customEvent', function() { expect(this.list.dispatchEvent).toHaveBeenCalledWith(this.customEvent); }); - describe('if no data argument is passed', function () { - beforeEach(function () { + describe('if no data argument is passed', function() { + beforeEach(function() { this.data.map.calls.reset(); this.dropdown.renderChildren.calls.reset(); DropDown.prototype.render.call(this.dropdown, undefined); }); - it('should not call .map', function () { + it('should not call .map', function() { expect(this.data.map).not.toHaveBeenCalled(); }); - it('should not call .renderChildren', function () { + it('should not call .renderChildren', function() { expect(this.dropdown.renderChildren).not.toHaveBeenCalled(); }); }); - describe('if no dynamic list is present', function () { - beforeEach(function () { + describe('if no dynamic list is present', function() { + beforeEach(function() { this.list = { querySelector: () => {}, dispatchEvent: () => {} }; this.dropdown = { renderChildren: () => {}, list: this.list }; this.data = [0, 1]; @@ -463,14 +463,14 @@ describe('DropLab DropDown', function () { DropDown.prototype.render.call(this.dropdown, this.data); }); - it('sets the .list .innerHTML', function () { + it('sets the .list .innerHTML', function() { expect(this.list.innerHTML).toBe('01'); }); }); }); - describe('renderChildren', function () { - beforeEach(function () { + describe('renderChildren', function() { + beforeEach(function() { this.templateString = 'templateString'; this.dropdown = { templateString: this.templateString }; this.data = { droplab_hidden: true }; @@ -484,44 +484,44 @@ describe('DropLab DropDown', function () { this.renderChildren = DropDown.prototype.renderChildren.call(this.dropdown, this.data); }); - it('should call utils.t with .templateString and data', function () { + it('should call utils.t with .templateString and data', function() { expect(utils.template).toHaveBeenCalledWith(this.templateString, this.data); }); - it('should call document.createElement', function () { + it('should call document.createElement', function() { expect(document.createElement).toHaveBeenCalledWith('div'); }); - it('should set the templates .innerHTML to the HTML', function () { + it('should set the templates .innerHTML to the HTML', function() { expect(this.template.innerHTML).toBe(this.html); }); - it('should call .setImagesSrc with the template', function () { + it('should call .setImagesSrc with the template', function() { expect(DropDown.setImagesSrc).toHaveBeenCalledWith(this.template); }); - it('should set the template display to none', function () { + it('should set the template display to none', function() { expect(this.template.firstChild.style.display).toBe('none'); }); - it('should return the templates .firstChild.outerHTML', function () { + it('should return the templates .firstChild.outerHTML', function() { expect(this.renderChildren).toBe(this.template.firstChild.outerHTML); }); - describe('if droplab_hidden is false', function () { - beforeEach(function () { + describe('if droplab_hidden is false', function() { + beforeEach(function() { this.data = { droplab_hidden: false }; this.renderChildren = DropDown.prototype.renderChildren.call(this.dropdown, this.data); }); - it('should set the template display to block', function () { + it('should set the template display to block', function() { expect(this.template.firstChild.style.display).toBe('block'); }); }); }); - describe('setImagesSrc', function () { - beforeEach(function () { + describe('setImagesSrc', function() { + beforeEach(function() { this.template = { querySelectorAll: () => {} }; spyOn(this.template, 'querySelectorAll').and.returnValue([]); @@ -529,64 +529,64 @@ describe('DropLab DropDown', function () { DropDown.setImagesSrc(this.template); }); - it('should call .querySelectorAll', function () { + it('should call .querySelectorAll', function() { expect(this.template.querySelectorAll).toHaveBeenCalledWith('img[data-src]'); }); }); - describe('show', function () { - beforeEach(function () { + describe('show', function() { + beforeEach(function() { this.list = { style: {} }; this.dropdown = { list: this.list, hidden: true }; DropDown.prototype.show.call(this.dropdown); }); - it('it should set .list display to block', function () { + it('it should set .list display to block', function() { expect(this.list.style.display).toBe('block'); }); - it('it should set .hidden to false', function () { + it('it should set .hidden to false', function() { expect(this.dropdown.hidden).toBe(false); }); - describe('if .hidden is false', function () { - beforeEach(function () { + describe('if .hidden is false', function() { + beforeEach(function() { this.list = { style: {} }; this.dropdown = { list: this.list, hidden: false }; this.show = DropDown.prototype.show.call(this.dropdown); }); - it('should return undefined', function () { + it('should return undefined', function() { expect(this.show).toEqual(undefined); }); - it('should not set .list display to block', function () { + it('should not set .list display to block', function() { expect(this.list.style.display).not.toEqual('block'); }); }); }); - describe('hide', function () { - beforeEach(function () { + describe('hide', function() { + beforeEach(function() { this.list = { style: {} }; this.dropdown = { list: this.list }; DropDown.prototype.hide.call(this.dropdown); }); - it('it should set .list display to none', function () { + it('it should set .list display to none', function() { expect(this.list.style.display).toBe('none'); }); - it('it should set .hidden to true', function () { + it('it should set .hidden to true', function() { expect(this.dropdown.hidden).toBe(true); }); }); - describe('toggle', function () { - beforeEach(function () { + describe('toggle', function() { + beforeEach(function() { this.hidden = true; this.dropdown = { hidden: this.hidden, show: () => {}, hide: () => {} }; @@ -596,12 +596,12 @@ describe('DropLab DropDown', function () { DropDown.prototype.toggle.call(this.dropdown); }); - it('should call .show', function () { + it('should call .show', function() { expect(this.dropdown.show).toHaveBeenCalled(); }); - describe('if .hidden is false', function () { - beforeEach(function () { + describe('if .hidden is false', function() { + beforeEach(function() { this.hidden = false; this.dropdown = { hidden: this.hidden, show: () => {}, hide: () => {} }; @@ -611,14 +611,14 @@ describe('DropLab DropDown', function () { DropDown.prototype.toggle.call(this.dropdown); }); - it('should call .hide', function () { + it('should call .hide', function() { expect(this.dropdown.hide).toHaveBeenCalled(); }); }); }); - describe('destroy', function () { - beforeEach(function () { + describe('destroy', function() { + beforeEach(function() { this.list = { removeEventListener: () => {} }; this.eventWrapper = { clickEvent: 'clickEvent' }; this.dropdown = { list: this.list, hide: () => {}, eventWrapper: this.eventWrapper }; @@ -629,12 +629,15 @@ describe('DropLab DropDown', function () { DropDown.prototype.destroy.call(this.dropdown); }); - it('it should call .hide', function () { + it('it should call .hide', function() { expect(this.dropdown.hide).toHaveBeenCalled(); }); - it('it should call .removeEventListener', function () { - expect(this.list.removeEventListener).toHaveBeenCalledWith('click', this.eventWrapper.clickEvent); + it('it should call .removeEventListener', function() { + expect(this.list.removeEventListener).toHaveBeenCalledWith( + 'click', + this.eventWrapper.clickEvent, + ); }); }); }); diff --git a/spec/javascripts/droplab/hook_spec.js b/spec/javascripts/droplab/hook_spec.js index 5eed1db2750..40470436f19 100644 --- a/spec/javascripts/droplab/hook_spec.js +++ b/spec/javascripts/droplab/hook_spec.js @@ -1,8 +1,8 @@ import Hook from '~/droplab/hook'; -describe('Hook', function () { - describe('class constructor', function () { - beforeEach(function () { +describe('Hook', function() { + describe('class constructor', function() { + beforeEach(function() { this.trigger = { id: 'id' }; this.list = {}; this.plugins = {}; @@ -14,58 +14,58 @@ describe('Hook', function () { this.hook = new Hook(this.trigger, this.list, this.plugins, this.config); }); - it('should set .trigger', function () { + it('should set .trigger', function() { expect(this.hook.trigger).toBe(this.trigger); }); - it('should set .list', function () { + it('should set .list', function() { expect(this.hook.list).toBe(this.dropdown); }); - it('should call DropDown constructor', function () { + it('should call DropDown constructor', function() { expect(this.dropdownConstructor).toHaveBeenCalledWith(this.list, this.config); }); - it('should set .type', function () { + it('should set .type', function() { expect(this.hook.type).toBe('Hook'); }); - it('should set .event', function () { + it('should set .event', function() { expect(this.hook.event).toBe('click'); }); - it('should set .plugins', function () { + it('should set .plugins', function() { expect(this.hook.plugins).toBe(this.plugins); }); - it('should set .config', function () { + it('should set .config', function() { expect(this.hook.config).toBe(this.config); }); - it('should set .id', function () { + it('should set .id', function() { expect(this.hook.id).toBe(this.trigger.id); }); - describe('if config argument is undefined', function () { - beforeEach(function () { + describe('if config argument is undefined', function() { + beforeEach(function() { this.config = undefined; this.hook = new Hook(this.trigger, this.list, this.plugins, this.config); }); - it('should set .config to an empty object', function () { + it('should set .config to an empty object', function() { expect(this.hook.config).toEqual({}); }); }); - describe('if plugins argument is undefined', function () { - beforeEach(function () { + describe('if plugins argument is undefined', function() { + beforeEach(function() { this.plugins = undefined; this.hook = new Hook(this.trigger, this.list, this.plugins, this.config); }); - it('should set .plugins to an empty array', function () { + it('should set .plugins to an empty array', function() { expect(this.hook.plugins).toEqual([]); }); }); diff --git a/spec/javascripts/droplab/plugins/ajax_filter_spec.js b/spec/javascripts/droplab/plugins/ajax_filter_spec.js index 8155d98b543..5dbe50af07f 100644 --- a/spec/javascripts/droplab/plugins/ajax_filter_spec.js +++ b/spec/javascripts/droplab/plugins/ajax_filter_spec.js @@ -38,8 +38,8 @@ describe('AjaxFilter', () => { dummyList.list.appendChild(dynamicList); }); - it('calls onLoadingFinished after loading data', (done) => { - ajaxSpy = (url) => { + it('calls onLoadingFinished after loading data', done => { + ajaxSpy = url => { expect(url).toBe('dummy endpoint?dummy search key='); return Promise.resolve(dummyData); }; @@ -52,16 +52,16 @@ describe('AjaxFilter', () => { .catch(done.fail); }); - it('does not call onLoadingFinished if Ajax call fails', (done) => { + it('does not call onLoadingFinished if Ajax call fails', done => { const dummyError = new Error('My dummy is sick! :-('); - ajaxSpy = (url) => { + ajaxSpy = url => { expect(url).toBe('dummy endpoint?dummy search key='); return Promise.reject(dummyError); }; AjaxFilter.trigger() .then(done.fail) - .catch((error) => { + .catch(error => { expect(error).toBe(dummyError); expect(dummyConfig.onLoadingFinished.calls.count()).toBe(0); }) diff --git a/spec/javascripts/droplab/plugins/ajax_spec.js b/spec/javascripts/droplab/plugins/ajax_spec.js index 085f25764fe..2f492d00c0a 100644 --- a/spec/javascripts/droplab/plugins/ajax_spec.js +++ b/spec/javascripts/droplab/plugins/ajax_spec.js @@ -8,6 +8,7 @@ describe('Ajax', () => { describe('is not configured', () => { it('passes the data through', () => { const data = ['data']; + expect(Ajax.preprocessing(config, data)).toEqual(data); }); }); @@ -22,13 +23,17 @@ describe('Ajax', () => { it('calls preprocessing', () => { Ajax.preprocessing(config, []); + expect(config.preprocessing.calls.count()).toBe(1); }); it('overrides AjaxCache', () => { - spyOn(AjaxCache, 'override').and.callFake((endpoint, results) => expect(results).toEqual(processedArray)); + spyOn(AjaxCache, 'override').and.callFake((endpoint, results) => { + expect(results).toEqual(processedArray); + }); Ajax.preprocessing(config, []); + expect(AjaxCache.override.calls.count()).toBe(1); }); }); diff --git a/spec/javascripts/droplab/plugins/input_setter_spec.js b/spec/javascripts/droplab/plugins/input_setter_spec.js index 1988811a305..711e0486bff 100644 --- a/spec/javascripts/droplab/plugins/input_setter_spec.js +++ b/spec/javascripts/droplab/plugins/input_setter_spec.js @@ -1,8 +1,8 @@ import InputSetter from '~/droplab/plugins/input_setter'; -describe('InputSetter', function () { - describe('init', function () { - beforeEach(function () { +describe('InputSetter', function() { + describe('init', function() { + beforeEach(function() { this.config = { InputSetter: {} }; this.hook = { config: this.config }; this.inputSetter = jasmine.createSpyObj('inputSetter', ['addEvents']); @@ -10,60 +10,62 @@ describe('InputSetter', function () { InputSetter.init.call(this.inputSetter, this.hook); }); - it('should set .hook', function () { + it('should set .hook', function() { expect(this.inputSetter.hook).toBe(this.hook); }); - it('should set .config', function () { + it('should set .config', function() { expect(this.inputSetter.config).toBe(this.config.InputSetter); }); - it('should set .eventWrapper', function () { + it('should set .eventWrapper', function() { expect(this.inputSetter.eventWrapper).toEqual({}); }); - it('should call .addEvents', function () { + it('should call .addEvents', function() { expect(this.inputSetter.addEvents).toHaveBeenCalled(); }); - describe('if config.InputSetter is not set', function () { - beforeEach(function () { + describe('if config.InputSetter is not set', function() { + beforeEach(function() { this.config = { InputSetter: undefined }; this.hook = { config: this.config }; InputSetter.init.call(this.inputSetter, this.hook); }); - it('should set .config to an empty object', function () { + it('should set .config to an empty object', function() { expect(this.inputSetter.config).toEqual({}); }); - it('should set hook.config to an empty object', function () { + it('should set hook.config to an empty object', function() { expect(this.hook.config.InputSetter).toEqual({}); }); - }) + }); }); - describe('addEvents', function () { - beforeEach(function () { + describe('addEvents', function() { + beforeEach(function() { this.hook = { list: { list: jasmine.createSpyObj('list', ['addEventListener']) } }; this.inputSetter = { eventWrapper: {}, hook: this.hook, setInputs: () => {} }; InputSetter.addEvents.call(this.inputSetter); }); - it('should set .eventWrapper.setInputs', function () { + it('should set .eventWrapper.setInputs', function() { expect(this.inputSetter.eventWrapper.setInputs).toEqual(jasmine.any(Function)); }); - it('should call .addEventListener', function () { - expect(this.hook.list.list.addEventListener) - .toHaveBeenCalledWith('click.dl', this.inputSetter.eventWrapper.setInputs); + it('should call .addEventListener', function() { + expect(this.hook.list.list.addEventListener).toHaveBeenCalledWith( + 'click.dl', + this.inputSetter.eventWrapper.setInputs, + ); }); }); - describe('removeEvents', function () { - beforeEach(function () { + describe('removeEvents', function() { + beforeEach(function() { this.hook = { list: { list: jasmine.createSpyObj('list', ['removeEventListener']) } }; this.eventWrapper = jasmine.createSpyObj('eventWrapper', ['setInputs']); this.inputSetter = { eventWrapper: this.eventWrapper, hook: this.hook }; @@ -71,14 +73,16 @@ describe('InputSetter', function () { InputSetter.removeEvents.call(this.inputSetter); }); - it('should call .removeEventListener', function () { - expect(this.hook.list.list.removeEventListener) - .toHaveBeenCalledWith('click.dl', this.eventWrapper.setInputs); + it('should call .removeEventListener', function() { + expect(this.hook.list.list.removeEventListener).toHaveBeenCalledWith( + 'click.dl', + this.eventWrapper.setInputs, + ); }); }); - describe('setInputs', function () { - beforeEach(function () { + describe('setInputs', function() { + beforeEach(function() { this.event = { detail: { selected: {} } }; this.config = [0, 1]; this.inputSetter = { config: this.config, setInput: () => {} }; @@ -88,7 +92,7 @@ describe('InputSetter', function () { InputSetter.setInputs.call(this.inputSetter, this.event); }); - it('should call .setInput for each config element', function () { + it('should call .setInput for each config element', function() { const allArgs = this.inputSetter.setInput.calls.allArgs(); expect(allArgs.length).toEqual(2); @@ -99,21 +103,21 @@ describe('InputSetter', function () { }); }); - describe('if config isnt an array', function () { - beforeEach(function () { + describe('if config isnt an array', function() { + beforeEach(function() { this.inputSetter = { config: {}, setInput: () => {} }; InputSetter.setInputs.call(this.inputSetter, this.event); }); - it('should set .config to an array with .config as the first element', function () { + it('should set .config to an array with .config as the first element', function() { expect(this.inputSetter.config).toEqual([{}]); }); }); }); - describe('setInput', function () { - beforeEach(function () { + describe('setInput', function() { + beforeEach(function() { this.selectedItem = { getAttribute: () => {} }; this.input = { value: 'oldValue', tagName: 'INPUT', hasAttribute: () => {} }; this.config = { valueAttribute: {}, input: this.input }; @@ -126,20 +130,20 @@ describe('InputSetter', function () { InputSetter.setInput.call(this.inputSetter, this.config, this.selectedItem); }); - it('should call .getAttribute', function () { + it('should call .getAttribute', function() { expect(this.selectedItem.getAttribute).toHaveBeenCalledWith(this.config.valueAttribute); }); - it('should call .hasAttribute', function () { + it('should call .hasAttribute', function() { expect(this.input.hasAttribute).toHaveBeenCalledWith(undefined); }); - it('should set the value of the input', function () { + it('should set the value of the input', function() { expect(this.input.value).toBe(this.newValue); }); - describe('if no config.input is provided', function () { - beforeEach(function () { + describe('if no config.input is provided', function() { + beforeEach(function() { this.config = { valueAttribute: {} }; this.trigger = { value: 'oldValue', tagName: 'INPUT', hasAttribute: () => {} }; this.inputSetter = { hook: { trigger: this.trigger } }; @@ -147,26 +151,26 @@ describe('InputSetter', function () { InputSetter.setInput.call(this.inputSetter, this.config, this.selectedItem); }); - it('should set the value of the hook.trigger', function () { + it('should set the value of the hook.trigger', function() { expect(this.trigger.value).toBe(this.newValue); }); }); - describe('if the input tag is not INPUT', function () { - beforeEach(function () { + describe('if the input tag is not INPUT', function() { + beforeEach(function() { this.input = { textContent: 'oldValue', tagName: 'SPAN', hasAttribute: () => {} }; this.config = { valueAttribute: {}, input: this.input }; InputSetter.setInput.call(this.inputSetter, this.config, this.selectedItem); }); - it('should set the textContent of the input', function () { + it('should set the textContent of the input', function() { expect(this.input.textContent).toBe(this.newValue); }); }); - describe('if there is an inputAttribute', function () { - beforeEach(function () { + describe('if there is an inputAttribute', function() { + beforeEach(function() { this.selectedItem = { getAttribute: () => {} }; this.input = { id: 'oldValue', hasAttribute: () => {}, setAttribute: () => {} }; this.inputSetter = { hook: { trigger: {} } }; @@ -185,25 +189,25 @@ describe('InputSetter', function () { InputSetter.setInput.call(this.inputSetter, this.config, this.selectedItem); }); - it('should call setAttribute', function () { + it('should call setAttribute', function() { expect(this.input.setAttribute).toHaveBeenCalledWith(this.inputAttribute, this.newValue); }); - it('should not set the value or textContent of the input', function () { + it('should not set the value or textContent of the input', function() { expect(this.input.value).not.toBe('newValue'); expect(this.input.textContent).not.toBe('newValue'); }); }); }); - describe('destroy', function () { - beforeEach(function () { + describe('destroy', function() { + beforeEach(function() { this.inputSetter = jasmine.createSpyObj('inputSetter', ['removeEvents']); InputSetter.destroy.call(this.inputSetter); }); - it('should call .removeEvents', function () { + it('should call .removeEvents', function() { expect(this.inputSetter.removeEvents).toHaveBeenCalled(); }); }); diff --git a/spec/javascripts/dropzone_input_spec.js b/spec/javascripts/dropzone_input_spec.js index 0c6b1a8946d..326b2c029fb 100644 --- a/spec/javascripts/dropzone_input_spec.js +++ b/spec/javascripts/dropzone_input_spec.js @@ -7,12 +7,10 @@ const TEST_FILE = { }; const TEST_UPLOAD_PATH = `${TEST_HOST}/upload/file`; const TEST_ERROR_MESSAGE = 'A big error occurred!'; -const TEMPLATE = ( -`<form class="gfm-form" data-uploads-path="${TEST_UPLOAD_PATH}"> +const TEMPLATE = `<form class="gfm-form" data-uploads-path="${TEST_UPLOAD_PATH}"> <textarea class="js-gfm-input"></textarea> <div class="uploading-error-message"></div> -</form>` -); +</form>`; describe('dropzone_input', () => { let form; diff --git a/spec/javascripts/emoji_spec.js b/spec/javascripts/emoji_spec.js index 629422780e8..3db4d9800f1 100644 --- a/spec/javascripts/emoji_spec.js +++ b/spec/javascripts/emoji_spec.js @@ -235,11 +235,11 @@ describe('gl_emoji', () => { expect(isRainbowFlagEmoji('🏳🌈')).toBeTruthy(); }); - it('should not detect flag_white on its\' own', () => { + it("should not detect flag_white on its' own", () => { expect(isRainbowFlagEmoji('🏳')).toBeFalsy(); }); - it('should not detect rainbow on its\' own', () => { + it("should not detect rainbow on its' own", () => { expect(isRainbowFlagEmoji('🌈')).toBeFalsy(); }); @@ -370,20 +370,14 @@ describe('gl_emoji', () => { describe('isEmojiUnicodeSupported', () => { it('should gracefully handle empty string with unicode support', () => { - const isSupported = isEmojiUnicodeSupported( - { '1.0': true }, - '', - '1.0', - ); + const isSupported = isEmojiUnicodeSupported({ '1.0': true }, '', '1.0'); + expect(isSupported).toBeTruthy(); }); it('should gracefully handle empty string without unicode support', () => { - const isSupported = isEmojiUnicodeSupported( - {}, - '', - '1.0', - ); + const isSupported = isEmojiUnicodeSupported({}, '', '1.0'); + expect(isSupported).toBeFalsy(); }); @@ -397,6 +391,7 @@ describe('gl_emoji', () => { emojiFixtureMap[emojiKey].moji, emojiFixtureMap[emojiKey].unicodeVersion, ); + expect(isSupported).toBeTruthy(); }); @@ -408,6 +403,7 @@ describe('gl_emoji', () => { emojiFixtureMap[emojiKey].moji, emojiFixtureMap[emojiKey].unicodeVersion, ); + expect(isSupported).toBeFalsy(); }); @@ -421,6 +417,7 @@ describe('gl_emoji', () => { emojiFixtureMap[emojiKey].moji, emojiFixtureMap[emojiKey].unicodeVersion, ); + expect(isSupported).toBeFalsy(); }); @@ -446,6 +443,7 @@ describe('gl_emoji', () => { emojiFixtureMap[emojiKey].moji, emojiFixtureMap[emojiKey].unicodeVersion, ); + expect(isSupported).toBeFalsy(); }); @@ -463,6 +461,7 @@ describe('gl_emoji', () => { emojiFixtureMap[emojiKey].moji, emojiFixtureMap[emojiKey].unicodeVersion, ); + expect(isSupported).toBeTruthy(); }); @@ -480,6 +479,7 @@ describe('gl_emoji', () => { emojiFixtureMap[emojiKey].moji, emojiFixtureMap[emojiKey].unicodeVersion, ); + expect(isSupported).toBeFalsy(); }); }); diff --git a/spec/javascripts/environments/emtpy_state_spec.js b/spec/javascripts/environments/emtpy_state_spec.js index 10a19af4175..d71dfe8197e 100644 --- a/spec/javascripts/environments/emtpy_state_spec.js +++ b/spec/javascripts/environments/emtpy_state_spec.js @@ -27,7 +27,7 @@ describe('environments empty state', () => { it('renders empty state and new environment button', () => { expect( vm.$el.querySelector('.js-blank-state-title').textContent.trim(), - ).toEqual('You don\'t have any environments right now.'); + ).toEqual('You don\'t have any environments right now'); expect( vm.$el.querySelector('.js-new-environment-button').getAttribute('href'), @@ -47,7 +47,7 @@ describe('environments empty state', () => { it('renders empty state without new button', () => { expect( vm.$el.querySelector('.js-blank-state-title').textContent.trim(), - ).toEqual('You don\'t have any environments right now.'); + ).toEqual('You don\'t have any environments right now'); expect( vm.$el.querySelector('.js-new-environment-button'), diff --git a/spec/javascripts/environments/environment_actions_spec.js b/spec/javascripts/environments/environment_actions_spec.js index ea40a1fcd4b..223153d4e31 100644 --- a/spec/javascripts/environments/environment_actions_spec.js +++ b/spec/javascripts/environments/environment_actions_spec.js @@ -40,23 +40,28 @@ describe('Actions Component', () => { it('should render a dropdown button with icon and title attribute', () => { expect(component.$el.querySelector('.fa-caret-down')).toBeDefined(); - expect(component.$el.querySelector('.dropdown-new').getAttribute('data-original-title')).toEqual('Deploy to...'); - expect(component.$el.querySelector('.dropdown-new').getAttribute('aria-label')).toEqual('Deploy to...'); + expect( + component.$el.querySelector('.dropdown-new').getAttribute('data-original-title'), + ).toEqual('Deploy to...'); + + expect(component.$el.querySelector('.dropdown-new').getAttribute('aria-label')).toEqual( + 'Deploy to...', + ); }); it('should render a dropdown with the provided list of actions', () => { - expect( - component.$el.querySelectorAll('.dropdown-menu li').length, - ).toEqual(actionsMock.length); + expect(component.$el.querySelectorAll('.dropdown-menu li').length).toEqual(actionsMock.length); }); - it('should render a disabled action when it\'s not playable', () => { + it("should render a disabled action when it's not playable", () => { expect( component.$el.querySelector('.dropdown-menu li:last-child button').getAttribute('disabled'), ).toEqual('disabled'); expect( - component.$el.querySelector('.dropdown-menu li:last-child button').classList.contains('disabled'), + component.$el + .querySelector('.dropdown-menu li:last-child button') + .classList.contains('disabled'), ).toEqual(true); }); }); diff --git a/spec/javascripts/environments/environments_app_spec.js b/spec/javascripts/environments/environments_app_spec.js index 60787b4c88d..7edc0ccac0b 100644 --- a/spec/javascripts/environments/environments_app_spec.js +++ b/spec/javascripts/environments/environments_app_spec.js @@ -50,7 +50,7 @@ describe('Environment', () => { expect( component.$el.querySelector('.js-blank-state-title').textContent, - ).toContain('You don\'t have any environments right now.'); + ).toContain('You don\'t have any environments right now'); }); }); @@ -94,6 +94,7 @@ describe('Environment', () => { spyOn(component, 'updateContent'); setTimeout(() => { component.$el.querySelector('.gl-pagination li:nth-child(5) a').click(); + expect(component.updateContent).toHaveBeenCalledWith({ scope: 'available', page: '2' }); done(); }, 0); @@ -126,7 +127,7 @@ describe('Environment', () => { it('should render empty state', () => { expect( component.$el.querySelector('.js-blank-state-title').textContent, - ).toContain('You don\'t have any environments right now.'); + ).toContain('You don\'t have any environments right now'); }); }); diff --git a/spec/javascripts/environments/environments_store_spec.js b/spec/javascripts/environments/environments_store_spec.js index f2c6ec24dd7..c3d16f10d72 100644 --- a/spec/javascripts/environments/environments_store_spec.js +++ b/spec/javascripts/environments/environments_store_spec.js @@ -17,23 +17,27 @@ describe('Store', () => { it('should store environments', () => { store.storeEnvironments(serverData); + expect(store.state.environments.length).toEqual(serverData.length); expect(store.state.environments[0]).toEqual(environmentsList[0]); }); it('should store available count', () => { store.storeAvailableCount(2); + expect(store.state.availableCounter).toEqual(2); }); it('should store stopped count', () => { store.storeStoppedCount(2); + expect(store.state.stoppedCounter).toEqual(2); }); describe('store environments', () => { it('should store environments', () => { store.storeEnvironments(serverData); + expect(store.state.environments.length).toEqual(serverData.length); }); @@ -45,6 +49,7 @@ describe('Store', () => { }; store.storeEnvironments([environment]); + expect(store.state.environments[0].isFolder).toEqual(true); expect(store.state.environments[0].folderName).toEqual('bar'); }); @@ -61,17 +66,20 @@ describe('Store', () => { }; store.storeEnvironments([environment]); + expect(store.state.environments[0].last_deployment).toEqual({}); expect(store.state.environments[0].isStoppable).toEqual(true); }); it('should store latest.name when the environment is not a folder', () => { store.storeEnvironments(serverData); + expect(store.state.environments[0].name).toEqual(serverData[0].latest.name); }); it('should store root level name when environment is a folder', () => { store.storeEnvironments(serverData); + expect(store.state.environments[1].folderName).toEqual(serverData[1].name); }); }); @@ -81,9 +89,11 @@ describe('Store', () => { store.storeEnvironments(serverData); store.toggleFolder(store.state.environments[1]); + expect(store.state.environments[1].isOpen).toEqual(true); store.toggleFolder(store.state.environments[1]); + expect(store.state.environments[1].isOpen).toEqual(false); }); @@ -91,9 +101,11 @@ describe('Store', () => { store.storeEnvironments(serverData); store.toggleFolder(store.state.environments[1]); + expect(store.state.environments[1].isOpen).toEqual(true); store.storeEnvironments(serverData); + expect(store.state.environments[1].isOpen).toEqual(true); }); }); @@ -116,6 +128,7 @@ describe('Store', () => { expect(store.state.environments[1].children.length).toEqual(serverData.length); // poll store.storeEnvironments(serverData); + expect(store.state.environments[1].children.length).toEqual(serverData.length); }); }); @@ -141,6 +154,7 @@ describe('Store', () => { }; store.setPagination(pagination); + expect(store.state.paginationInformation).toEqual(expectedResult); }); }); @@ -150,6 +164,7 @@ describe('Store', () => { store.storeEnvironments(serverData); store.toggleFolder(store.state.environments[1]); + expect(store.getOpenFolders()[0]).toEqual(store.state.environments[1]); }); }); diff --git a/spec/javascripts/feature_highlight/feature_highlight_helper_spec.js b/spec/javascripts/feature_highlight/feature_highlight_helper_spec.js index 2ab6a0077b5..e5795d4cbb1 100644 --- a/spec/javascripts/feature_highlight/feature_highlight_helper_spec.js +++ b/spec/javascripts/feature_highlight/feature_highlight_helper_spec.js @@ -1,11 +1,7 @@ import $ from 'jquery'; import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; -import { - getSelector, - dismiss, - inserted, -} from '~/feature_highlight/feature_highlight_helper'; +import { getSelector, dismiss, inserted } from '~/feature_highlight/feature_highlight_helper'; import { togglePopover } from '~/shared/popover'; import getSetTimeoutPromise from 'spec/helpers/set_timeout_promise_helper'; @@ -14,7 +10,10 @@ describe('feature highlight helper', () => { describe('getSelector', () => { it('returns js-feature-highlight selector', () => { const highlightId = 'highlightId'; - expect(getSelector(highlightId)).toEqual(`.js-feature-highlight[data-highlight=${highlightId}]`); + + expect(getSelector(highlightId)).toEqual( + `.js-feature-highlight[data-highlight=${highlightId}]`, + ); }); }); @@ -37,7 +36,7 @@ describe('feature highlight helper', () => { mock.restore(); }); - it('calls persistent dismissal endpoint', (done) => { + it('calls persistent dismissal endpoint', done => { const spy = jasmine.createSpy('dismiss-endpoint-hit'); mock.onPost('/-/callouts/dismiss').reply(spy); @@ -59,7 +58,7 @@ describe('feature highlight helper', () => { }); describe('inserted', () => { - it('registers click event callback', (done) => { + it('registers click event callback', done => { const context = { getAttribute: () => 'popoverId', dataset: { @@ -67,7 +66,7 @@ describe('feature highlight helper', () => { }, }; - spyOn($.fn, 'on').and.callFake((event) => { + spyOn($.fn, 'on').and.callFake(event => { expect(event).toEqual('click'); done(); }); diff --git a/spec/javascripts/feature_highlight/feature_highlight_spec.js b/spec/javascripts/feature_highlight/feature_highlight_spec.js index ec46d4f905a..0a9fba789c3 100644 --- a/spec/javascripts/feature_highlight/feature_highlight_spec.js +++ b/spec/javascripts/feature_highlight/feature_highlight_spec.js @@ -50,7 +50,7 @@ describe('feature highlight', () => { expect(toggleSpy).toHaveBeenCalledWith(jasmine.any(Object), true); }); - it('setup debounced mouseleave', (done) => { + it('setup debounced mouseleave', done => { const toggleSpy = spyOn(popover.togglePopover, 'call'); $(selector).trigger('mouseleave'); @@ -63,7 +63,10 @@ describe('feature highlight', () => { it('setup show.bs.popover', () => { $(selector).trigger('show.bs.popover'); - expect(window.addEventListener).toHaveBeenCalledWith('scroll', jasmine.any(Function), { once: true }); + + expect(window.addEventListener).toHaveBeenCalledWith('scroll', jasmine.any(Function), { + once: true, + }); }); it('removes disabled attribute', () => { @@ -73,6 +76,7 @@ describe('feature highlight', () => { it('displays popover', () => { expect(document.querySelector(selector).getAttribute('aria-describedby')).toBeFalsy(); $(selector).trigger('mouseenter'); + expect(document.querySelector(selector).getAttribute('aria-describedby')).toBeTruthy(); }); diff --git a/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js b/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js index 9d670afe206..d1742dcedfa 100644 --- a/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js +++ b/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js @@ -3,7 +3,7 @@ import eventHub from '~/filtered_search/event_hub'; import RecentSearchesDropdownContent from '~/filtered_search/components/recent_searches_dropdown_content.vue'; import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys'; -const createComponent = (propsData) => { +const createComponent = propsData => { const Component = Vue.extend(RecentSearchesDropdownContent); return new Component({ @@ -21,10 +21,7 @@ describe('RecentSearchesDropdownContent', () => { allowedKeys: IssuableFilteredSearchTokenKeys.getKeys(), }; const propsDataWithItems = { - items: [ - 'foo', - 'author:@root label:~foo bar', - ], + items: ['foo', 'author:@root label:~foo bar'], allowedKeys: IssuableFilteredSearchTokenKeys.getKeys(), }; @@ -47,6 +44,7 @@ describe('RecentSearchesDropdownContent', () => { expect(el.querySelector('.dropdown-info-note')).toBeDefined(); const items = el.querySelectorAll('.filtered-search-history-dropdown-item'); + expect(items.length).toEqual(propsDataWithoutItems.items.length); }); }); @@ -65,17 +63,27 @@ describe('RecentSearchesDropdownContent', () => { it('should render recent search items', () => { const items = el.querySelectorAll('.filtered-search-history-dropdown-item'); + expect(items.length).toEqual(propsDataWithItems.items.length); - expect(trimMarkupWhitespace(items[0].querySelector('.filtered-search-history-dropdown-search-token').textContent)).toEqual('foo'); + expect( + trimMarkupWhitespace( + items[0].querySelector('.filtered-search-history-dropdown-search-token').textContent, + ), + ).toEqual('foo'); const item1Tokens = items[1].querySelectorAll('.filtered-search-history-dropdown-token'); + expect(item1Tokens.length).toEqual(2); expect(item1Tokens[0].querySelector('.name').textContent).toEqual('author:'); expect(item1Tokens[0].querySelector('.value').textContent).toEqual('@root'); expect(item1Tokens[1].querySelector('.name').textContent).toEqual('label:'); expect(item1Tokens[1].querySelector('.value').textContent).toEqual('~foo'); - expect(trimMarkupWhitespace(items[1].querySelector('.filtered-search-history-dropdown-search-token').textContent)).toEqual('bar'); + expect( + trimMarkupWhitespace( + items[1].querySelector('.filtered-search-history-dropdown-search-token').textContent, + ), + ).toEqual('bar'); }); }); @@ -132,12 +140,14 @@ describe('RecentSearchesDropdownContent', () => { it('with items', () => { vm = createComponent(propsDataWithItems); const { hasItems } = vm; + expect(hasItems).toEqual(true); }); it('with no items', () => { vm = createComponent(propsDataWithoutItems); const { hasItems } = vm; + expect(hasItems).toEqual(false); }); }); @@ -161,6 +171,7 @@ describe('RecentSearchesDropdownContent', () => { it('emits event', () => { expect(onRecentSearchesItemSelectedSpy).not.toHaveBeenCalled(); vm.onItemActivated('something'); + expect(onRecentSearchesItemSelectedSpy).toHaveBeenCalledWith('something'); }); }); @@ -182,6 +193,7 @@ describe('RecentSearchesDropdownContent', () => { it('emits event', () => { expect(onRequestClearRecentSearchesSpy).not.toHaveBeenCalled(); vm.onRequestClearRecentSearches({ stopPropagation: () => {} }); + expect(onRequestClearRecentSearchesSpy).toHaveBeenCalled(); }); }); diff --git a/spec/javascripts/filtered_search/dropdown_user_spec.js b/spec/javascripts/filtered_search/dropdown_user_spec.js index b48b1456eff..e8fcc8592eb 100644 --- a/spec/javascripts/filtered_search/dropdown_user_spec.js +++ b/spec/javascripts/filtered_search/dropdown_user_spec.js @@ -28,14 +28,14 @@ describe('Dropdown User', () => { it('should not return the single quote found in value', () => { spyOn(FilteredSearchTokenizer, 'processTokens').and.returnValue({ - lastToken: '\'larry boy', + lastToken: "'larry boy", }); expect(dropdownUser.getSearchInput()).toBe('larry boy'); }); }); - describe('config AjaxFilter\'s endpoint', () => { + describe("config AjaxFilter's endpoint", () => { beforeEach(() => { spyOn(DropdownUser.prototype, 'bindEvents').and.callFake(() => {}); spyOn(DropdownUser.prototype, 'getProjectId').and.callFake(() => {}); @@ -88,10 +88,12 @@ describe('Dropdown User', () => { }); }); - const findCurrentUserElement = () => authorFilterDropdownElement.querySelector('.js-current-user'); + const findCurrentUserElement = () => + authorFilterDropdownElement.querySelector('.js-current-user'); it('hides the current user from dropdown', () => { const currentUserElement = findCurrentUserElement(); + expect(currentUserElement).not.toBe(null); dropdown.hideCurrentUser(); @@ -102,6 +104,7 @@ describe('Dropdown User', () => { it('does nothing if no user is logged in', () => { const currentUserElement = findCurrentUserElement(); currentUserElement.parentNode.removeChild(currentUserElement); + expect(findCurrentUserElement()).toBe(null); dropdown.hideCurrentUser(); diff --git a/spec/javascripts/filtered_search/dropdown_utils_spec.js b/spec/javascripts/filtered_search/dropdown_utils_spec.js index 3dc8089cd83..6605b0a30d7 100644 --- a/spec/javascripts/filtered_search/dropdown_utils_spec.js +++ b/spec/javascripts/filtered_search/dropdown_utils_spec.js @@ -10,25 +10,30 @@ describe('Dropdown Utils', () => { describe('getEscapedText', () => { it('should return same word when it has no space', () => { const escaped = DropdownUtils.getEscapedText('textWithoutSpace'); + expect(escaped).toBe('textWithoutSpace'); }); it('should escape with double quotes', () => { let escaped = DropdownUtils.getEscapedText('text with space'); + expect(escaped).toBe('"text with space"'); - escaped = DropdownUtils.getEscapedText('won\'t fix'); + escaped = DropdownUtils.getEscapedText("won't fix"); + expect(escaped).toBe('"won\'t fix"'); }); it('should escape with single quotes', () => { const escaped = DropdownUtils.getEscapedText('won"t fix'); - expect(escaped).toBe('\'won"t fix\''); + + expect(escaped).toBe("'won\"t fix'"); }); it('should escape with single quotes by default', () => { const escaped = DropdownUtils.getEscapedText('won"t\' fix'); - expect(escaped).toBe('\'won"t\' fix\''); + + expect(escaped).toBe("'won\"t' fix'"); }); }); @@ -50,6 +55,7 @@ describe('Dropdown Utils', () => { input.value = 'roo'; const updatedItem = DropdownUtils.filterWithSymbol('@', input, item); + expect(updatedItem.droplab_hidden).toBe(false); }); @@ -57,6 +63,7 @@ describe('Dropdown Utils', () => { input.value = '@roo'; const updatedItem = DropdownUtils.filterWithSymbol('@', input, item); + expect(updatedItem.droplab_hidden).toBe(false); }); @@ -69,6 +76,7 @@ describe('Dropdown Utils', () => { input.value = '"'; const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); + expect(updatedItem.droplab_hidden).toBe(false); }); @@ -76,6 +84,7 @@ describe('Dropdown Utils', () => { input.value = '~"'; const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); + expect(updatedItem.droplab_hidden).toBe(false); }); @@ -83,6 +92,7 @@ describe('Dropdown Utils', () => { input.value = '"community con'; const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); + expect(updatedItem.droplab_hidden).toBe(false); }); @@ -90,34 +100,39 @@ describe('Dropdown Utils', () => { input.value = '~"community con'; const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); + expect(updatedItem.droplab_hidden).toBe(false); }); it('should filter with single quote', () => { - input.value = '\''; + input.value = "'"; const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); + expect(updatedItem.droplab_hidden).toBe(false); }); it('should filter with single quote and symbol', () => { - input.value = '~\''; + input.value = "~'"; const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); + expect(updatedItem.droplab_hidden).toBe(false); }); it('should filter with single quote and multiple words', () => { - input.value = '\'community con'; + input.value = "'community con"; const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); + expect(updatedItem.droplab_hidden).toBe(false); }); it('should filter with single quote, symbol and multiple words', () => { - input.value = '~\'community con'; + input.value = "~'community con"; const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem); + expect(updatedItem.droplab_hidden).toBe(false); }); }); @@ -152,17 +167,20 @@ describe('Dropdown Utils', () => { let updatedItem = DropdownUtils.filterHint(config(), { hint: 'label', }); + expect(updatedItem.droplab_hidden).toBe(false); input.value = 'o'; updatedItem = DropdownUtils.filterHint(config(), { hint: 'label', }); + expect(updatedItem.droplab_hidden).toBe(true); }); it('should return droplab_hidden false when item has no hint', () => { const updatedItem = DropdownUtils.filterHint(config(), {}, ''); + expect(updatedItem.droplab_hidden).toBe(false); }); @@ -172,6 +190,7 @@ describe('Dropdown Utils', () => { hint: 'label', type: 'array', }); + expect(updatedItem.droplab_hidden).toBe(false); }); @@ -180,12 +199,14 @@ describe('Dropdown Utils', () => { let updatedItem = DropdownUtils.filterHint(config(), { hint: 'milestone', }); + expect(updatedItem.droplab_hidden).toBe(true); updatedItem = DropdownUtils.filterHint(config(), { hint: 'milestone', type: 'string', }); + expect(updatedItem.droplab_hidden).toBe(true); }); }); @@ -205,6 +226,7 @@ describe('Dropdown Utils', () => { }; const updated = DropdownUtils.mergeDuplicateLabels(dataMap, newLabel); + expect(updated[newLabel.title]).toEqual(newLabel); }); @@ -215,6 +237,7 @@ describe('Dropdown Utils', () => { }; const updated = DropdownUtils.mergeDuplicateLabels(dataMap, duplicate); + expect(updated.label.multipleColors).toEqual([dataMap.label.color, duplicate.color]); }); }); @@ -222,21 +245,42 @@ describe('Dropdown Utils', () => { describe('duplicateLabelColor', () => { it('should linear-gradient 2 colors', () => { const gradient = DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000']); - expect(gradient).toEqual('linear-gradient(#FFFFFF 0%, #FFFFFF 50%, #000000 50%, #000000 100%)'); + + expect(gradient).toEqual( + 'linear-gradient(#FFFFFF 0%, #FFFFFF 50%, #000000 50%, #000000 100%)', + ); }); it('should linear-gradient 3 colors', () => { const gradient = DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000', '#333333']); - expect(gradient).toEqual('linear-gradient(#FFFFFF 0%, #FFFFFF 33%, #000000 33%, #000000 66%, #333333 66%, #333333 100%)'); + + expect(gradient).toEqual( + 'linear-gradient(#FFFFFF 0%, #FFFFFF 33%, #000000 33%, #000000 66%, #333333 66%, #333333 100%)', + ); }); it('should linear-gradient 4 colors', () => { - const gradient = DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000', '#333333', '#DDDDDD']); - expect(gradient).toEqual('linear-gradient(#FFFFFF 0%, #FFFFFF 25%, #000000 25%, #000000 50%, #333333 50%, #333333 75%, #DDDDDD 75%, #DDDDDD 100%)'); + const gradient = DropdownUtils.duplicateLabelColor([ + '#FFFFFF', + '#000000', + '#333333', + '#DDDDDD', + ]); + + expect(gradient).toEqual( + 'linear-gradient(#FFFFFF 0%, #FFFFFF 25%, #000000 25%, #000000 50%, #333333 50%, #333333 75%, #DDDDDD 75%, #DDDDDD 100%)', + ); }); it('should not linear-gradient more than 4 colors', () => { - const gradient = DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000', '#333333', '#DDDDDD', '#EEEEEE']); + const gradient = DropdownUtils.duplicateLabelColor([ + '#FFFFFF', + '#000000', + '#333333', + '#DDDDDD', + '#EEEEEE', + ]); + expect(gradient.indexOf('#EEEEEE')).toBe(-1); }); }); @@ -244,17 +288,21 @@ describe('Dropdown Utils', () => { describe('duplicateLabelPreprocessing', () => { it('should set preprocessed to true', () => { const results = DropdownUtils.duplicateLabelPreprocessing([]); + expect(results.preprocessed).toEqual(true); }); it('should not mutate existing data if there are no duplicates', () => { - const data = [{ - title: 'label1', - color: '#FFFFFF', - }, { - title: 'label2', - color: '#000000', - }]; + const data = [ + { + title: 'label1', + color: '#FFFFFF', + }, + { + title: 'label2', + color: '#000000', + }, + ]; const results = DropdownUtils.duplicateLabelPreprocessing(data); expect(results.length).toEqual(2); @@ -263,13 +311,16 @@ describe('Dropdown Utils', () => { }); describe('duplicate labels', () => { - const data = [{ - title: 'label', - color: '#FFFFFF', - }, { - title: 'label', - color: '#000000', - }]; + const data = [ + { + title: 'label', + color: '#FFFFFF', + }, + { + title: 'label', + color: '#000000', + }, + ]; const results = DropdownUtils.duplicateLabelPreprocessing(data); it('should merge duplicate labels', () => { @@ -298,6 +349,7 @@ describe('Dropdown Utils', () => { }; DropdownUtils.setDataValueIfSelected(null, selected); + expect(FilteredSearchDropdownManager.addWordToInput.calls.count()).toEqual(1); }); @@ -308,6 +360,7 @@ describe('Dropdown Utils', () => { }; const result = DropdownUtils.setDataValueIfSelected(null, selected); + expect(result).toBe(true); }); @@ -317,6 +370,7 @@ describe('Dropdown Utils', () => { }; const result = DropdownUtils.setDataValueIfSelected(null, selected); + expect(result).toBe(false); }); }); diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js index a03d5a31b41..e076120f5cc 100644 --- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js +++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js @@ -9,7 +9,7 @@ import FilteredSearchDropdownManager from '~/filtered_search/filtered_search_dro import FilteredSearchManager from '~/filtered_search/filtered_search_manager'; import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper'; -describe('Filtered Search Manager', function () { +describe('Filtered Search Manager', function() { let input; let manager; let tokensContainer; @@ -97,7 +97,9 @@ describe('Filtered Search Manager', function () { }); it('should not instantiate Flash if an RecentSearchesServiceError is caught', () => { - spyOn(RecentSearchesService.prototype, 'fetch').and.callFake(() => Promise.reject(new RecentSearchesServiceError())); + spyOn(RecentSearchesService.prototype, 'fetch').and.callFake(() => + Promise.reject(new RecentSearchesServiceError()), + ); spyOn(window, 'Flash'); manager.setup(); @@ -134,6 +136,7 @@ describe('Filtered Search Manager', function () { }; manager.searchState(e); + expect(FilteredSearchManager.prototype.search).not.toHaveBeenCalled(); }); @@ -149,6 +152,7 @@ describe('Filtered Search Manager', function () { }; manager.searchState(e); + expect(FilteredSearchManager.prototype.search).toHaveBeenCalledWith('opened'); }); }); @@ -160,10 +164,10 @@ describe('Filtered Search Manager', function () { initializeManager(); }); - it('should search with a single word', (done) => { + it('should search with a single word', done => { input.value = 'searchTerm'; - spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake((url) => { + spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake(url => { expect(url).toEqual(`${defaultParams}&search=searchTerm`); done(); }); @@ -171,10 +175,10 @@ describe('Filtered Search Manager', function () { manager.search(); }); - it('should search with multiple words', (done) => { + it('should search with multiple words', done => { input.value = 'awesome search terms'; - spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake((url) => { + spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake(url => { expect(url).toEqual(`${defaultParams}&search=awesome+search+terms`); done(); }); @@ -182,24 +186,26 @@ describe('Filtered Search Manager', function () { manager.search(); }); - it('should search with special characters', (done) => { + it('should search with special characters', done => { input.value = '~!@#$%^&*()_+{}:<>,.?/'; - spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake((url) => { - expect(url).toEqual(`${defaultParams}&search=~!%40%23%24%25%5E%26*()_%2B%7B%7D%3A%3C%3E%2C.%3F%2F`); + spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake(url => { + expect(url).toEqual( + `${defaultParams}&search=~!%40%23%24%25%5E%26*()_%2B%7B%7D%3A%3C%3E%2C.%3F%2F`, + ); done(); }); manager.search(); }); - it('removes duplicated tokens', (done) => { + it('removes duplicated tokens', done => { tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(` ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug')} ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug')} `); - spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake((url) => { + spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake(url => { expect(url).toEqual(`${defaultParams}&label_name[]=bug`); done(); }); @@ -304,6 +310,7 @@ describe('Filtered Search Manager', function () { ); tokensContainer.querySelector('.js-visual-token .remove-token').click(); + expect(tokensContainer.querySelector('.js-visual-token')).toEqual(null); }); @@ -437,12 +444,18 @@ describe('Filtered Search Manager', function () { it('toggles on focus', () => { input.focus(); - expect(document.querySelector('.filtered-search-box').classList.contains('focus')).toEqual(true); + + expect(document.querySelector('.filtered-search-box').classList.contains('focus')).toEqual( + true, + ); }); it('toggles on blur', () => { input.blur(); - expect(document.querySelector('.filtered-search-box').classList.contains('focus')).toEqual(false); + + expect(document.querySelector('.filtered-search-box').classList.contains('focus')).toEqual( + false, + ); }); }); @@ -454,9 +467,12 @@ describe('Filtered Search Manager', function () { }); it('correctly modifies params when custom modifier is passed', () => { - const modifedParams = manager.getAllParams.call({ - modifyUrlParams: paramsArr => paramsArr.reverse(), - }, [].concat(this.paramsArr)); + const modifedParams = manager.getAllParams.call( + { + modifyUrlParams: paramsArr => paramsArr.reverse(), + }, + [].concat(this.paramsArr), + ); expect(modifedParams[0]).toBe(this.paramsArr[1]); }); diff --git a/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js b/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js index cf7789a1d57..d1fea18dea8 100644 --- a/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js +++ b/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js @@ -1,23 +1,26 @@ import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys'; describe('Filtered Search Token Keys', () => { - const tokenKeys = [{ - key: 'author', - type: 'string', - param: 'username', - symbol: '@', - icon: 'pencil', - tag: '@author', - }]; - - const conditions = [{ - url: 'assignee_id=0', - tokenKey: 'assignee', - value: 'none', - }]; + const tokenKeys = [ + { + key: 'author', + type: 'string', + param: 'username', + symbol: '@', + icon: 'pencil', + tag: '@author', + }, + ]; + + const conditions = [ + { + url: 'assignee_id=0', + tokenKey: 'assignee', + value: 'none', + }, + ]; describe('get', () => { - it('should return tokenKeys', () => { expect(new FilteredSearchTokenKeys().get()).not.toBeNull(); }); @@ -51,11 +54,13 @@ describe('Filtered Search Token Keys', () => { describe('searchByKey', () => { it('should return null when key not found', () => { const tokenKey = new FilteredSearchTokenKeys(tokenKeys).searchByKey('notakey'); + expect(tokenKey).toBeNull(); }); it('should return tokenKey when found by key', () => { const result = new FilteredSearchTokenKeys(tokenKeys).searchByKey(tokenKeys[0].key); + expect(result).toEqual(tokenKeys[0]); }); }); @@ -63,11 +68,13 @@ describe('Filtered Search Token Keys', () => { describe('searchBySymbol', () => { it('should return null when symbol not found', () => { const tokenKey = new FilteredSearchTokenKeys(tokenKeys).searchBySymbol('notasymbol'); + expect(tokenKey).toBeNull(); }); it('should return tokenKey when found by symbol', () => { const result = new FilteredSearchTokenKeys(tokenKeys).searchBySymbol(tokenKeys[0].symbol); + expect(result).toEqual(tokenKeys[0]); }); }); @@ -75,16 +82,23 @@ describe('Filtered Search Token Keys', () => { describe('searchByKeyParam', () => { it('should return null when key param not found', () => { const tokenKey = new FilteredSearchTokenKeys(tokenKeys).searchByKeyParam('notakeyparam'); + expect(tokenKey).toBeNull(); }); it('should return tokenKey when found by key param', () => { - const result = new FilteredSearchTokenKeys(tokenKeys).searchByKeyParam(`${tokenKeys[0].key}_${tokenKeys[0].param}`); + const result = new FilteredSearchTokenKeys(tokenKeys).searchByKeyParam( + `${tokenKeys[0].key}_${tokenKeys[0].param}`, + ); + expect(result).toEqual(tokenKeys[0]); }); it('should return alternative tokenKey when found by key param', () => { - const result = new FilteredSearchTokenKeys(tokenKeys).searchByKeyParam(`${tokenKeys[0].key}_${tokenKeys[0].param}`); + const result = new FilteredSearchTokenKeys(tokenKeys).searchByKeyParam( + `${tokenKeys[0].key}_${tokenKeys[0].param}`, + ); + expect(result).toEqual(tokenKeys[0]); }); }); @@ -92,26 +106,35 @@ describe('Filtered Search Token Keys', () => { describe('searchByConditionUrl', () => { it('should return null when condition url not found', () => { const condition = new FilteredSearchTokenKeys([], [], conditions).searchByConditionUrl(null); + expect(condition).toBeNull(); }); it('should return condition when found by url', () => { - const result = new FilteredSearchTokenKeys([], [], conditions) - .searchByConditionUrl(conditions[0].url); + const result = new FilteredSearchTokenKeys([], [], conditions).searchByConditionUrl( + conditions[0].url, + ); + expect(result).toBe(conditions[0]); }); }); describe('searchByConditionKeyValue', () => { it('should return null when condition tokenKey and value not found', () => { - const condition = new FilteredSearchTokenKeys([], [], conditions) - .searchByConditionKeyValue(null, null); + const condition = new FilteredSearchTokenKeys([], [], conditions).searchByConditionKeyValue( + null, + null, + ); + expect(condition).toBeNull(); }); it('should return condition when found by tokenKey and value', () => { - const result = new FilteredSearchTokenKeys([], [], conditions) - .searchByConditionKeyValue(conditions[0].tokenKey, conditions[0].value); + const result = new FilteredSearchTokenKeys([], [], conditions).searchByConditionKeyValue( + conditions[0].tokenKey, + conditions[0].value, + ); + expect(result).toEqual(conditions[0]); }); }); diff --git a/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js b/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js index 4f9f546cbb5..dec03e5ab93 100644 --- a/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js +++ b/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js @@ -7,14 +7,18 @@ describe('Filtered Search Tokenizer', () => { describe('processTokens', () => { it('returns for input containing only search value', () => { const results = FilteredSearchTokenizer.processTokens('searchTerm', allowedKeys); + expect(results.searchToken).toBe('searchTerm'); expect(results.tokens.length).toBe(0); expect(results.lastToken).toBe(results.searchToken); }); it('returns for input containing only tokens', () => { - const results = FilteredSearchTokenizer - .processTokens('author:@root label:~"Very Important" milestone:%v1.0 assignee:none', allowedKeys); + const results = FilteredSearchTokenizer.processTokens( + 'author:@root label:~"Very Important" milestone:%v1.0 assignee:none', + allowedKeys, + ); + expect(results.searchToken).toBe(''); expect(results.tokens.length).toBe(4); expect(results.tokens[3]).toBe(results.lastToken); @@ -37,8 +41,11 @@ describe('Filtered Search Tokenizer', () => { }); it('returns for input starting with search value and ending with tokens', () => { - const results = FilteredSearchTokenizer - .processTokens('searchTerm anotherSearchTerm milestone:none', allowedKeys); + const results = FilteredSearchTokenizer.processTokens( + 'searchTerm anotherSearchTerm milestone:none', + allowedKeys, + ); + expect(results.searchToken).toBe('searchTerm anotherSearchTerm'); expect(results.tokens.length).toBe(1); expect(results.tokens[0]).toBe(results.lastToken); @@ -48,8 +55,10 @@ describe('Filtered Search Tokenizer', () => { }); it('returns for input starting with tokens and ending with search value', () => { - const results = FilteredSearchTokenizer - .processTokens('assignee:@user searchTerm', allowedKeys); + const results = FilteredSearchTokenizer.processTokens( + 'assignee:@user searchTerm', + allowedKeys, + ); expect(results.searchToken).toBe('searchTerm'); expect(results.tokens.length).toBe(1); @@ -60,8 +69,10 @@ describe('Filtered Search Tokenizer', () => { }); it('returns for input containing search value wrapped between tokens', () => { - const results = FilteredSearchTokenizer - .processTokens('author:@root label:~"Won\'t fix" searchTerm anotherSearchTerm milestone:none', allowedKeys); + const results = FilteredSearchTokenizer.processTokens( + 'author:@root label:~"Won\'t fix" searchTerm anotherSearchTerm milestone:none', + allowedKeys, + ); expect(results.searchToken).toBe('searchTerm anotherSearchTerm'); expect(results.tokens.length).toBe(3); @@ -81,8 +92,11 @@ describe('Filtered Search Tokenizer', () => { }); it('returns for input containing search value in between tokens', () => { - const results = FilteredSearchTokenizer - .processTokens('author:@root searchTerm assignee:none anotherSearchTerm label:~Doing', allowedKeys); + const results = FilteredSearchTokenizer.processTokens( + 'author:@root searchTerm assignee:none anotherSearchTerm label:~Doing', + allowedKeys, + ); + expect(results.searchToken).toBe('searchTerm anotherSearchTerm'); expect(results.tokens.length).toBe(3); expect(results.tokens[2]).toBe(results.lastToken); @@ -102,6 +116,7 @@ describe('Filtered Search Tokenizer', () => { it('returns search value for invalid tokens', () => { const results = FilteredSearchTokenizer.processTokens('fake:token', allowedKeys); + expect(results.lastToken).toBe('fake:token'); expect(results.searchToken).toBe('fake:token'); expect(results.tokens.length).toEqual(0); @@ -109,6 +124,7 @@ describe('Filtered Search Tokenizer', () => { it('returns search value and token for mix of valid and invalid tokens', () => { const results = FilteredSearchTokenizer.processTokens('label:real fake:token', allowedKeys); + expect(results.tokens.length).toEqual(1); expect(results.tokens[0].key).toBe('label'); expect(results.tokens[0].value).toBe('real'); @@ -119,12 +135,14 @@ describe('Filtered Search Tokenizer', () => { it('returns search value for invalid symbols', () => { const results = FilteredSearchTokenizer.processTokens('std::includes', allowedKeys); + expect(results.lastToken).toBe('std::includes'); expect(results.searchToken).toBe('std::includes'); }); it('removes duplicated values', () => { const results = FilteredSearchTokenizer.processTokens('label:~foo label:~foo', allowedKeys); + expect(results.tokens.length).toBe(1); expect(results.tokens[0].key).toBe('label'); expect(results.tokens[0].value).toBe('foo'); diff --git a/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js b/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js index 53a6d1d62b0..0c1d5f5b0b4 100644 --- a/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js +++ b/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js @@ -9,7 +9,7 @@ import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper'; describe('Filtered Search Visual Tokens', () => { const subject = FilteredSearchVisualTokens; - const findElements = (tokenElement) => { + const findElements = tokenElement => { const tokenNameElement = tokenElement.querySelector('.name'); const tokenValueContainer = tokenElement.querySelector('.value-container'); const tokenValueElement = tokenValueContainer.querySelector('.value'); @@ -34,8 +34,7 @@ describe('Filtered Search Visual Tokens', () => { describe('getLastVisualTokenBeforeInput', () => { it('returns when there are no visual tokens', () => { - const { lastVisualToken, isLastVisualTokenValid } - = subject.getLastVisualTokenBeforeInput(); + const { lastVisualToken, isLastVisualTokenValid } = subject.getLastVisualTokenBeforeInput(); expect(lastVisualToken).toEqual(null); expect(isLastVisualTokenValid).toEqual(true); @@ -47,8 +46,7 @@ describe('Filtered Search Visual Tokens', () => { bugLabelToken.outerHTML, ); - const { lastVisualToken, isLastVisualTokenValid } - = subject.getLastVisualTokenBeforeInput(); + const { lastVisualToken, isLastVisualTokenValid } = subject.getLastVisualTokenBeforeInput(); expect(lastVisualToken).toEqual(document.querySelector('.filtered-search-token')); expect(isLastVisualTokenValid).toEqual(true); @@ -59,8 +57,7 @@ describe('Filtered Search Visual Tokens', () => { FilteredSearchSpecHelper.createNameFilterVisualTokenHTML('Author'), ); - const { lastVisualToken, isLastVisualTokenValid } - = subject.getLastVisualTokenBeforeInput(); + const { lastVisualToken, isLastVisualTokenValid } = subject.getLastVisualTokenBeforeInput(); expect(lastVisualToken).toEqual(document.querySelector('.filtered-search-token')); expect(isLastVisualTokenValid).toEqual(false); @@ -73,8 +70,7 @@ describe('Filtered Search Visual Tokens', () => { ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('author', '@root')} `); - const { lastVisualToken, isLastVisualTokenValid } - = subject.getLastVisualTokenBeforeInput(); + const { lastVisualToken, isLastVisualTokenValid } = subject.getLastVisualTokenBeforeInput(); const items = document.querySelectorAll('.tokens-container .js-visual-token'); expect(lastVisualToken.isEqualNode(items[items.length - 1])).toEqual(true); @@ -88,8 +84,7 @@ describe('Filtered Search Visual Tokens', () => { ${FilteredSearchSpecHelper.createNameFilterVisualTokenHTML('assignee')} `); - const { lastVisualToken, isLastVisualTokenValid } - = subject.getLastVisualTokenBeforeInput(); + const { lastVisualToken, isLastVisualTokenValid } = subject.getLastVisualTokenBeforeInput(); const items = document.querySelectorAll('.tokens-container .js-visual-token'); expect(lastVisualToken.isEqualNode(items[items.length - 1])).toEqual(true); @@ -105,8 +100,7 @@ describe('Filtered Search Visual Tokens', () => { ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('author', '@root')} `); - const { lastVisualToken, isLastVisualTokenValid } - = subject.getLastVisualTokenBeforeInput(); + const { lastVisualToken, isLastVisualTokenValid } = subject.getLastVisualTokenBeforeInput(); expect(lastVisualToken).toEqual(document.querySelector('.filtered-search-token')); expect(isLastVisualTokenValid).toEqual(true); @@ -119,8 +113,7 @@ describe('Filtered Search Visual Tokens', () => { ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('author', '@root')} `); - const { lastVisualToken, isLastVisualTokenValid } - = subject.getLastVisualTokenBeforeInput(); + const { lastVisualToken, isLastVisualTokenValid } = subject.getLastVisualTokenBeforeInput(); expect(lastVisualToken).toEqual(document.querySelector('.filtered-search-token')); expect(isLastVisualTokenValid).toEqual(false); @@ -131,6 +124,7 @@ describe('Filtered Search Visual Tokens', () => { describe('getEndpointWithQueryParams', () => { it('returns `endpoint` string as is when second param `endpointQueryParams` is undefined, null or empty string', () => { const endpoint = 'foo/bar/labels.json'; + expect(subject.getEndpointWithQueryParams(endpoint)).toBe(endpoint); expect(subject.getEndpointWithQueryParams(endpoint, null)).toBe(endpoint); expect(subject.getEndpointWithQueryParams(endpoint, '')).toBe(endpoint); @@ -141,8 +135,13 @@ describe('Filtered Search Visual Tokens', () => { const singleQueryParams = '{"foo":"true"}'; const multipleQueryParams = '{"foo":"true","bar":"true"}'; - expect(subject.getEndpointWithQueryParams(endpoint, singleQueryParams)).toBe(`${endpoint}?foo=true`); - expect(subject.getEndpointWithQueryParams(endpoint, multipleQueryParams)).toBe(`${endpoint}?foo=true&bar=true`); + expect(subject.getEndpointWithQueryParams(endpoint, singleQueryParams)).toBe( + `${endpoint}?foo=true`, + ); + + expect(subject.getEndpointWithQueryParams(endpoint, multipleQueryParams)).toBe( + `${endpoint}?foo=true&bar=true`, + ); }); }); @@ -161,6 +160,7 @@ describe('Filtered Search Visual Tokens', () => { `); const selected = tokensContainer.querySelector('.js-visual-token .selected'); + expect(selected.classList.contains('selected')).toEqual(true); subject.unselectTokens(); @@ -273,7 +273,9 @@ describe('Filtered Search Visual Tokens', () => { describe('remove token', () => { it('contains remove-token button', () => { - expect(tokenElement.querySelector('.value-container .remove-token')).toEqual(jasmine.anything()); + expect(tokenElement.querySelector('.value-container .remove-token')).toEqual( + jasmine.anything(), + ); }); it('contains fa-close icon', () => { @@ -311,7 +313,9 @@ describe('Filtered Search Visual Tokens', () => { }); it('inserts visual token before input', () => { - tokensContainer.appendChild(FilteredSearchSpecHelper.createFilterVisualToken('assignee', '@root')); + tokensContainer.appendChild( + FilteredSearchSpecHelper.createFilterVisualToken('assignee', '@root'), + ); subject.addVisualTokenElement('label', 'Frontend'); const tokens = tokensContainer.querySelectorAll('.js-visual-token'); @@ -550,7 +554,7 @@ describe('Filtered Search Visual Tokens', () => { token = document.querySelector('.js-visual-token'); }); - it('tokenize\'s existing input', () => { + it("tokenize's existing input", () => { input.value = 'some text'; spyOn(subject, 'tokenizeInput').and.callThrough(); @@ -623,7 +627,7 @@ describe('Filtered Search Visual Tokens', () => { expect(subject.getLastVisualTokenBeforeInput).not.toHaveBeenCalled(); }); - it('tokenize\'s input', () => { + it("tokenize's input", () => { tokensContainer.innerHTML = ` ${FilteredSearchSpecHelper.createNameFilterVisualTokenHTML('label')} ${FilteredSearchSpecHelper.createInputHTML()} @@ -675,13 +679,17 @@ describe('Filtered Search Visual Tokens', () => { subject.moveInputToTheRight(); const token = tokensContainer.children[1]; + expect(token.querySelector('.value').innerText).toEqual('~bug'); }); }); describe('renderVisualTokenValue', () => { const keywordToken = FilteredSearchSpecHelper.createFilterVisualToken('search'); - const milestoneToken = FilteredSearchSpecHelper.createFilterVisualToken('milestone', 'upcoming'); + const milestoneToken = FilteredSearchSpecHelper.createFilterVisualToken( + 'milestone', + 'upcoming', + ); let updateLabelTokenColorSpy; let updateUserTokenAppearanceSpy; @@ -702,8 +710,9 @@ describe('Filtered Search Visual Tokens', () => { }); it('renders a author token value element', () => { - const { tokenNameElement, tokenValueContainer, tokenValueElement } = - findElements(authorToken); + const { tokenNameElement, tokenValueContainer, tokenValueElement } = findElements( + authorToken, + ); const tokenName = tokenNameElement.innerText; const tokenValue = 'new value'; @@ -712,13 +721,15 @@ describe('Filtered Search Visual Tokens', () => { expect(tokenValueElement.innerText).toBe(tokenValue); expect(updateUserTokenAppearanceSpy.calls.count()).toBe(1); const expectedArgs = [tokenValueContainer, tokenValueElement, tokenValue]; + expect(updateUserTokenAppearanceSpy.calls.argsFor(0)).toEqual(expectedArgs); expect(updateLabelTokenColorSpy.calls.count()).toBe(0); }); it('renders a label token value element', () => { - const { tokenNameElement, tokenValueContainer, tokenValueElement } = - findElements(bugLabelToken); + const { tokenNameElement, tokenValueContainer, tokenValueElement } = findElements( + bugLabelToken, + ); const tokenName = tokenNameElement.innerText; const tokenValue = 'new value'; @@ -727,6 +738,7 @@ describe('Filtered Search Visual Tokens', () => { expect(tokenValueElement.innerText).toBe(tokenValue); expect(updateLabelTokenColorSpy.calls.count()).toBe(1); const expectedArgs = [tokenValueContainer, tokenValue]; + expect(updateLabelTokenColorSpy.calls.argsFor(0)).toEqual(expectedArgs); expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0); }); @@ -751,96 +763,103 @@ describe('Filtered Search Visual Tokens', () => { spyOn(UsersCache, 'retrieve').and.callFake(username => usersCacheSpy(username)); }); - it('ignores special value "none"', (done) => { - usersCacheSpy = (username) => { + it('ignores special value "none"', done => { + usersCacheSpy = username => { expect(username).toBe('none'); done.fail('Should not resolve "none"!'); }; const { tokenValueContainer, tokenValueElement } = findElements(authorToken); - subject.updateUserTokenAppearance(tokenValueContainer, tokenValueElement, 'none') - .then(done) - .catch(done.fail); + subject + .updateUserTokenAppearance(tokenValueContainer, tokenValueElement, 'none') + .then(done) + .catch(done.fail); }); - it('ignores error if UsersCache throws', (done) => { + it('ignores error if UsersCache throws', done => { spyOn(window, 'Flash'); const dummyError = new Error('Earth rotated backwards'); const { tokenValueContainer, tokenValueElement } = findElements(authorToken); const tokenValue = tokenValueElement.innerText; - usersCacheSpy = (username) => { + usersCacheSpy = username => { expect(`@${username}`).toBe(tokenValue); return Promise.reject(dummyError); }; - subject.updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) - .then(() => { - expect(window.Flash.calls.count()).toBe(0); - }) - .then(done) - .catch(done.fail); + subject + .updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) + .then(() => { + expect(window.Flash.calls.count()).toBe(0); + }) + .then(done) + .catch(done.fail); }); - it('does nothing if user cannot be found', (done) => { + it('does nothing if user cannot be found', done => { const { tokenValueContainer, tokenValueElement } = findElements(authorToken); const tokenValue = tokenValueElement.innerText; - usersCacheSpy = (username) => { + usersCacheSpy = username => { expect(`@${username}`).toBe(tokenValue); return Promise.resolve(undefined); }; - subject.updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) - .then(() => { - expect(tokenValueElement.innerText).toBe(tokenValue); - }) - .then(done) - .catch(done.fail); + subject + .updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) + .then(() => { + expect(tokenValueElement.innerText).toBe(tokenValue); + }) + .then(done) + .catch(done.fail); }); - it('replaces author token with avatar and display name', (done) => { + it('replaces author token with avatar and display name', done => { const dummyUser = { name: 'Important Person', avatar_url: 'https://host.invalid/mypics/avatar.png', }; const { tokenValueContainer, tokenValueElement } = findElements(authorToken); const tokenValue = tokenValueElement.innerText; - usersCacheSpy = (username) => { + usersCacheSpy = username => { expect(`@${username}`).toBe(tokenValue); return Promise.resolve(dummyUser); }; - subject.updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) - .then(() => { - expect(tokenValueContainer.dataset.originalValue).toBe(tokenValue); - expect(tokenValueElement.innerText.trim()).toBe(dummyUser.name); - const avatar = tokenValueElement.querySelector('img.avatar'); - expect(avatar.src).toBe(dummyUser.avatar_url); - expect(avatar.alt).toBe(''); - }) - .then(done) - .catch(done.fail); + subject + .updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) + .then(() => { + expect(tokenValueContainer.dataset.originalValue).toBe(tokenValue); + expect(tokenValueElement.innerText.trim()).toBe(dummyUser.name); + const avatar = tokenValueElement.querySelector('img.avatar'); + + expect(avatar.src).toBe(dummyUser.avatar_url); + expect(avatar.alt).toBe(''); + }) + .then(done) + .catch(done.fail); }); - it('escapes user name when creating token', (done) => { + it('escapes user name when creating token', done => { const dummyUser = { name: '<script>', avatar_url: `${gl.TEST_HOST}/mypics/avatar.png`, }; const { tokenValueContainer, tokenValueElement } = findElements(authorToken); const tokenValue = tokenValueElement.innerText; - usersCacheSpy = (username) => { + usersCacheSpy = username => { expect(`@${username}`).toBe(tokenValue); return Promise.resolve(dummyUser); }; - subject.updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) - .then(() => { - expect(tokenValueElement.innerText.trim()).toBe(dummyUser.name); - tokenValueElement.querySelector('.avatar').remove(); - expect(tokenValueElement.innerHTML.trim()).toBe(_.escape(dummyUser.name)); - }) - .then(done) - .catch(done.fail); + subject + .updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) + .then(() => { + expect(tokenValueElement.innerText.trim()).toBe(dummyUser.name); + tokenValueElement.querySelector('.avatar').remove(); + + expect(tokenValueElement.innerHTML.trim()).toBe(_.escape(dummyUser.name)); + }) + .then(done) + .catch(done.fail); }); }); @@ -854,23 +873,31 @@ describe('Filtered Search Visual Tokens', () => { it('should set backgroundColor', () => { const originalBackgroundColor = bugLabelToken.style.backgroundColor; const token = subject.setTokenStyle(bugLabelToken, 'blue', 'white'); + expect(token.style.backgroundColor).toEqual('blue'); expect(token.style.backgroundColor).not.toEqual(originalBackgroundColor); }); it('should not set backgroundColor when it is a linear-gradient', () => { - const token = subject.setTokenStyle(bugLabelToken, 'linear-gradient(135deg, red, blue)', 'white'); + const token = subject.setTokenStyle( + bugLabelToken, + 'linear-gradient(135deg, red, blue)', + 'white', + ); + expect(token.style.backgroundColor).toEqual(bugLabelToken.style.backgroundColor); }); it('should set textColor', () => { const token = subject.setTokenStyle(bugLabelToken, 'white', 'black'); + expect(token.style.color).toEqual('black'); expect(token.style.color).not.toEqual(originalTextColor); }); it('should add inverted class when textColor is #FFFFFF', () => { const token = subject.setTokenStyle(bugLabelToken, 'black', '#FFFFFF'); + expect(token.style.color).toEqual('rgb(255, 255, 255)'); expect(token.style.color).not.toEqual(originalTextColor); expect(token.querySelector('.remove-token').classList.contains('inverted')).toEqual(true); @@ -894,14 +921,17 @@ describe('Filtered Search Visual Tokens', () => { describe('not preprocessed before', () => { it('returns preprocessed labels', () => { let labels = []; + expect(labels.preprocessed).not.toEqual(true); labels = FilteredSearchVisualTokens.preprocessLabel(endpoint, labels); + expect(labels.preprocessed).toEqual(true); }); it('overrides AjaxCache with preprocessed results', () => { spyOn(AjaxCache, 'override').and.callFake(() => {}); FilteredSearchVisualTokens.preprocessLabel(endpoint, []); + expect(AjaxCache.override.calls.count()).toEqual(1); }); }); @@ -919,8 +949,14 @@ describe('Filtered Search Visual Tokens', () => { labelData = getJSONFixture(jsonFixtureName); }); - const missingLabelToken = FilteredSearchSpecHelper.createFilterVisualToken('label', '~doesnotexist'); - const spaceLabelToken = FilteredSearchSpecHelper.createFilterVisualToken('label', '~"some space"'); + const missingLabelToken = FilteredSearchSpecHelper.createFilterVisualToken( + 'label', + '~doesnotexist', + ); + const spaceLabelToken = FilteredSearchSpecHelper.createFilterVisualToken( + 'label', + '~"some space"', + ); beforeEach(() => { tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(` @@ -932,11 +968,11 @@ describe('Filtered Search Visual Tokens', () => { const filteredSearchInput = document.querySelector('.filtered-search'); filteredSearchInput.dataset.baseEndpoint = dummyEndpoint; - AjaxCache.internalStorage = { }; + AjaxCache.internalStorage = {}; AjaxCache.internalStorage[`${dummyEndpoint}/labels.json`] = labelData; }); - const parseColor = (color) => { + const parseColor = color => { const dummyElement = document.createElement('div'); dummyElement.style.color = color; return dummyElement.style.color; @@ -948,16 +984,16 @@ describe('Filtered Search Visual Tokens', () => { expect(tokenValueContainer.style.color).toBe(parseColor(label.text_color)); }; - const findLabel = tokenValue => labelData.find( - label => tokenValue === `~${DropdownUtils.getEscapedText(label.title)}`, - ); + const findLabel = tokenValue => + labelData.find(label => tokenValue === `~${DropdownUtils.getEscapedText(label.title)}`); - it('updates the color of a label token', (done) => { + it('updates the color of a label token', done => { const { tokenValueContainer, tokenValueElement } = findElements(bugLabelToken); const tokenValue = tokenValueElement.innerText; const matchingLabel = findLabel(tokenValue); - subject.updateLabelTokenColor(tokenValueContainer, tokenValue) + subject + .updateLabelTokenColor(tokenValueContainer, tokenValue) .then(() => { expectValueContainerStyle(tokenValueContainer, matchingLabel); }) @@ -965,12 +1001,13 @@ describe('Filtered Search Visual Tokens', () => { .catch(done.fail); }); - it('updates the color of a label token with spaces', (done) => { + it('updates the color of a label token with spaces', done => { const { tokenValueContainer, tokenValueElement } = findElements(spaceLabelToken); const tokenValue = tokenValueElement.innerText; const matchingLabel = findLabel(tokenValue); - subject.updateLabelTokenColor(tokenValueContainer, tokenValue) + subject + .updateLabelTokenColor(tokenValueContainer, tokenValue) .then(() => { expectValueContainerStyle(tokenValueContainer, matchingLabel); }) @@ -978,13 +1015,15 @@ describe('Filtered Search Visual Tokens', () => { .catch(done.fail); }); - it('does not change color of a missing label', (done) => { + it('does not change color of a missing label', done => { const { tokenValueContainer, tokenValueElement } = findElements(missingLabelToken); const tokenValue = tokenValueElement.innerText; const matchingLabel = findLabel(tokenValue); + expect(matchingLabel).toBe(undefined); - subject.updateLabelTokenColor(tokenValueContainer, tokenValue) + subject + .updateLabelTokenColor(tokenValueContainer, tokenValue) .then(() => { expect(tokenValueContainer.getAttribute('style')).toBe(null); }) diff --git a/spec/javascripts/filtered_search/recent_searches_root_spec.js b/spec/javascripts/filtered_search/recent_searches_root_spec.js index d063fcf4f2d..70dd4e9570d 100644 --- a/spec/javascripts/filtered_search/recent_searches_root_spec.js +++ b/spec/javascripts/filtered_search/recent_searches_root_spec.js @@ -14,7 +14,7 @@ describe('RecentSearchesRoot', () => { }, }; - VueSpy = spyOnDependency(RecentSearchesRoot, 'Vue').and.callFake((options) => { + VueSpy = spyOnDependency(RecentSearchesRoot, 'Vue').and.callFake(options => { ({ data, template } = options); }); diff --git a/spec/javascripts/filtered_search/services/recent_searches_service_spec.js b/spec/javascripts/filtered_search/services/recent_searches_service_spec.js index c293c0afa97..188f83eca16 100644 --- a/spec/javascripts/filtered_search/services/recent_searches_service_spec.js +++ b/spec/javascripts/filtered_search/services/recent_searches_service_spec.js @@ -15,48 +15,49 @@ describe('RecentSearchesService', () => { spyOn(RecentSearchesService, 'isAvailable').and.returnValue(true); }); - it('should default to empty array', (done) => { + it('should default to empty array', done => { const fetchItemsPromise = service.fetch(); fetchItemsPromise - .then((items) => { + .then(items => { expect(items).toEqual([]); }) .then(done) .catch(done.fail); }); - it('should reject when unable to parse', (done) => { + it('should reject when unable to parse', done => { window.localStorage.setItem(service.localStorageKey, 'fail'); const fetchItemsPromise = service.fetch(); fetchItemsPromise .then(done.fail) - .catch((error) => { + .catch(error => { expect(error).toEqual(jasmine.any(SyntaxError)); }) .then(done) .catch(done.fail); }); - it('should reject when service is unavailable', (done) => { + it('should reject when service is unavailable', done => { RecentSearchesService.isAvailable.and.returnValue(false); - service.fetch() + service + .fetch() .then(done.fail) - .catch((error) => { + .catch(error => { expect(error).toEqual(jasmine.any(Error)); }) .then(done) .catch(done.fail); }); - it('should return items from localStorage', (done) => { + it('should return items from localStorage', done => { window.localStorage.setItem(service.localStorageKey, '["foo", "bar"]'); const fetchItemsPromise = service.fetch(); fetchItemsPromise - .then((items) => { + .then(items => { expect(items).toEqual(['foo', 'bar']); }) .then(done) @@ -70,10 +71,11 @@ describe('RecentSearchesService', () => { spyOn(window.localStorage, 'getItem'); }); - it('should not call .getItem', (done) => { - RecentSearchesService.prototype.fetch() + it('should not call .getItem', done => { + RecentSearchesService.prototype + .fetch() .then(done.fail) - .catch((err) => { + .catch(err => { expect(err).toEqual(new RecentSearchesServiceError()); expect(window.localStorage.getItem).not.toHaveBeenCalled(); }) @@ -92,6 +94,7 @@ describe('RecentSearchesService', () => { const items = ['foo', 'bar']; service.save(items); const newLocalStorageValue = window.localStorage.getItem(service.localStorageKey); + expect(JSON.parse(newLocalStorageValue)).toEqual(items); }); }); diff --git a/spec/javascripts/filtered_search/stores/recent_searches_store_spec.js b/spec/javascripts/filtered_search/stores/recent_searches_store_spec.js index 1eebc6f2367..56bb82ae941 100644 --- a/spec/javascripts/filtered_search/stores/recent_searches_store_spec.js +++ b/spec/javascripts/filtered_search/stores/recent_searches_store_spec.js @@ -38,14 +38,8 @@ describe('RecentSearchesStore', () => { describe('setRecentSearches', () => { it('should override list', () => { - store.setRecentSearches([ - 'foo', - 'bar', - ]); - store.setRecentSearches([ - 'baz', - 'qux', - ]); + store.setRecentSearches(['foo', 'bar']); + store.setRecentSearches(['baz', 'qux']); expect(store.state.recentSearches).toEqual(['baz', 'qux']); }); diff --git a/spec/javascripts/flash_spec.js b/spec/javascripts/flash_spec.js index 0a8e5a628c0..d7338ee0f66 100644 --- a/spec/javascripts/flash_spec.js +++ b/spec/javascripts/flash_spec.js @@ -1,9 +1,4 @@ -import flash, { - createFlashEl, - createAction, - hideFlash, - removeFlashClickListener, -} from '~/flash'; +import flash, { createFlashEl, createAction, hideFlash, removeFlashClickListener } from '~/flash'; describe('Flash', () => { describe('createFlashEl', () => { @@ -20,28 +15,23 @@ describe('Flash', () => { it('creates flash element with type', () => { el.innerHTML = createFlashEl('testing', 'alert'); - expect( - el.querySelector('.flash-alert'), - ).not.toBeNull(); + expect(el.querySelector('.flash-alert')).not.toBeNull(); }); it('escapes text', () => { el.innerHTML = createFlashEl('<script>alert("a");</script>', 'alert'); - expect( - el.querySelector('.flash-text').textContent.trim(), - ).toBe('<script>alert("a");</script>'); + expect(el.querySelector('.flash-text').textContent.trim()).toBe( + '<script>alert("a");</script>', + ); }); it('adds container classes when inside content wrapper', () => { el.innerHTML = createFlashEl('testing', 'alert', true); - expect( - el.querySelector('.flash-text').classList.contains('container-fluid'), - ).toBeTruthy(); - expect( - el.querySelector('.flash-text').classList.contains('container-limited'), - ).toBeTruthy(); + expect(el.querySelector('.flash-text').classList.contains('container-fluid')).toBeTruthy(); + + expect(el.querySelector('.flash-text').classList.contains('container-limited')).toBeTruthy(); }); }); @@ -56,31 +46,23 @@ describe('Flash', () => { it('sets transition style', () => { hideFlash(el); - expect( - el.style['transition-property'], - ).toBe('opacity'); - expect( - el.style['transition-duration'], - ).toBe('0.3s'); + expect(el.style['transition-property']).toBe('opacity'); + + expect(el.style['transition-duration']).toBe('0.3s'); }); it('sets opacity style', () => { hideFlash(el); - expect( - el.style.opacity, - ).toBe('0'); + expect(el.style.opacity).toBe('0'); }); it('does not set styles when fadeTransition is false', () => { hideFlash(el, false); - expect( - el.style.opacity, - ).toBe(''); - expect( - el.style.transition, - ).toBe(''); + expect(el.style.opacity).toBe(''); + + expect(el.style.transition).toBe(''); }); it('removes element after transitionend', () => { @@ -89,9 +71,7 @@ describe('Flash', () => { hideFlash(el); el.dispatchEvent(new Event('transitionend')); - expect( - document.querySelector('.js-testing'), - ).toBeNull(); + expect(document.querySelector('.js-testing')).toBeNull(); }); it('calls event listener callback once', () => { @@ -103,9 +83,7 @@ describe('Flash', () => { el.dispatchEvent(new Event('transitionend')); el.dispatchEvent(new Event('transitionend')); - expect( - el.remove.calls.count(), - ).toBe(1); + expect(el.remove.calls.count()).toBe(1); }); }); @@ -122,9 +100,7 @@ describe('Flash', () => { title: 'test', }); - expect( - el.querySelector('.flash-action').href, - ).toContain('testing'); + expect(el.querySelector('.flash-action').href).toContain('testing'); }); it('uses hash as href when no href is present', () => { @@ -132,9 +108,7 @@ describe('Flash', () => { title: 'test', }); - expect( - el.querySelector('.flash-action').href, - ).toContain('#'); + expect(el.querySelector('.flash-action').href).toContain('#'); }); it('adds role when no href is present', () => { @@ -142,9 +116,7 @@ describe('Flash', () => { title: 'test', }); - expect( - el.querySelector('.flash-action').getAttribute('role'), - ).toBe('button'); + expect(el.querySelector('.flash-action').getAttribute('role')).toBe('button'); }); it('escapes the title text', () => { @@ -152,9 +124,9 @@ describe('Flash', () => { title: '<script>alert("a")</script>', }); - expect( - el.querySelector('.flash-action').textContent.trim(), - ).toBe('<script>alert("a")</script>'); + expect(el.querySelector('.flash-action').textContent.trim()).toBe( + '<script>alert("a")</script>', + ); }); }); @@ -163,12 +135,9 @@ describe('Flash', () => { it('does not add to the DOM', () => { const flashEl = flash('testing'); - expect( - flashEl, - ).toBeNull(); - expect( - document.querySelector('.flash-alert'), - ).toBeNull(); + expect(flashEl).toBeNull(); + + expect(document.querySelector('.flash-alert')).toBeNull(); }); }); @@ -188,42 +157,30 @@ describe('Flash', () => { it('adds flash element into container', () => { flash('test', 'alert', document, null, false, true); - expect( - document.querySelector('.flash-alert'), - ).not.toBeNull(); + expect(document.querySelector('.flash-alert')).not.toBeNull(); - expect( - document.body.className, - ).toContain('flash-shown'); + expect(document.body.className).toContain('flash-shown'); }); it('adds flash into specified parent', () => { - flash( - 'test', - 'alert', - document.querySelector('.content-wrapper'), - ); + flash('test', 'alert', document.querySelector('.content-wrapper')); - expect( - document.querySelector('.content-wrapper .flash-alert'), - ).not.toBeNull(); + expect(document.querySelector('.content-wrapper .flash-alert')).not.toBeNull(); }); it('adds container classes when inside content-wrapper', () => { flash('test'); - expect( - document.querySelector('.flash-text').className, - ).toBe('flash-text container-fluid container-limited'); + expect(document.querySelector('.flash-text').className).toBe( + 'flash-text container-fluid container-limited', + ); }); it('does not add container when outside of content-wrapper', () => { document.querySelector('.content-wrapper').className = 'js-content-wrapper'; flash('test'); - expect( - document.querySelector('.flash-text').className.trim(), - ).toBe('flash-text'); + expect(document.querySelector('.flash-text').className.trim()).toBe('flash-text'); }); it('removes element after clicking', () => { @@ -231,29 +188,18 @@ describe('Flash', () => { document.querySelector('.flash-alert').click(); - expect( - document.querySelector('.flash-alert'), - ).toBeNull(); + expect(document.querySelector('.flash-alert')).toBeNull(); - expect( - document.body.className, - ).not.toContain('flash-shown'); + expect(document.body.className).not.toContain('flash-shown'); }); describe('with actionConfig', () => { it('adds action link', () => { - flash( - 'test', - 'alert', - document, - { - title: 'test', - }, - ); - - expect( - document.querySelector('.flash-action'), - ).not.toBeNull(); + flash('test', 'alert', document, { + title: 'test', + }); + + expect(document.querySelector('.flash-action')).not.toBeNull(); }); it('calls actionConfig clickHandler on click', () => { @@ -262,18 +208,11 @@ describe('Flash', () => { clickHandler: jasmine.createSpy('actionConfig'), }; - flash( - 'test', - 'alert', - document, - actionConfig, - ); + flash('test', 'alert', document, actionConfig); document.querySelector('.flash-action').click(); - expect( - actionConfig.clickHandler, - ).toHaveBeenCalled(); + expect(actionConfig.clickHandler).toHaveBeenCalled(); }); }); }); @@ -284,7 +223,7 @@ describe('Flash', () => { document.body.innerHTML += '<div class="flash-container"><div class="flash"></div></div>'; }); - it('removes global flash on click', (done) => { + it('removes global flash on click', done => { const flashEl = document.querySelector('.flash'); removeFlashClickListener(flashEl, false); diff --git a/spec/javascripts/fly_out_nav_spec.js b/spec/javascripts/fly_out_nav_spec.js index eb9330f5e5b..7ef44f29c5b 100644 --- a/spec/javascripts/fly_out_nav_spec.js +++ b/spec/javascripts/fly_out_nav_spec.js @@ -45,9 +45,7 @@ describe('Fly out sidebar navigation', () => { height: 100, }; - expect( - calculateTop(boundingRect, 100), - ).toBe(100); + expect(calculateTop(boundingRect, 100)).toBe(100); }); it('returns boundingRect - bottomOverflow', () => { @@ -56,27 +54,22 @@ describe('Fly out sidebar navigation', () => { height: 100, }; - expect( - calculateTop(boundingRect, 100), - ).toBe(window.innerHeight - 50); + expect(calculateTop(boundingRect, 100)).toBe(window.innerHeight - 50); }); }); describe('getHideSubItemsInterval', () => { beforeEach(() => { - el.innerHTML = '<div class="sidebar-sub-level-items" style="position: fixed; top: 0; left: 100px; height: 150px;"></div>'; + el.innerHTML = + '<div class="sidebar-sub-level-items" style="position: fixed; top: 0; left: 100px; height: 150px;"></div>'; }); it('returns 0 if currentOpenMenu is nil', () => { - expect( - getHideSubItemsInterval(), - ).toBe(0); + expect(getHideSubItemsInterval()).toBe(0); }); it('returns 0 if mousePos is empty', () => { - expect( - getHideSubItemsInterval(), - ).toBe(0); + expect(getHideSubItemsInterval()).toBe(0); }); it('returns 0 when mouse above sub-items', () => { @@ -90,9 +83,7 @@ describe('Fly out sidebar navigation', () => { clientY: el.getBoundingClientRect().top - 50, }); - expect( - getHideSubItemsInterval(), - ).toBe(0); + expect(getHideSubItemsInterval()).toBe(0); }); it('returns 0 when mouse is below sub-items', () => { @@ -105,12 +96,10 @@ describe('Fly out sidebar navigation', () => { }); documentMouseMove({ clientX: el.getBoundingClientRect().left, - clientY: (el.getBoundingClientRect().top - subItems.getBoundingClientRect().height) + 50, + clientY: el.getBoundingClientRect().top - subItems.getBoundingClientRect().height + 50, }); - expect( - getHideSubItemsInterval(), - ).toBe(0); + expect(getHideSubItemsInterval()).toBe(0); }); it('returns 300 when mouse is moved towards sub-items', () => { @@ -124,9 +113,7 @@ describe('Fly out sidebar navigation', () => { clientY: el.getBoundingClientRect().top + 10, }); - expect( - getHideSubItemsInterval(), - ).toBe(300); + expect(getHideSubItemsInterval()).toBe(300); }); }); @@ -138,9 +125,7 @@ describe('Fly out sidebar navigation', () => { it('removes is-over class if currentOpenMenu is null', () => { mouseLeaveTopItem(el); - expect( - el.classList.remove, - ).toHaveBeenCalledWith('is-over'); + expect(el.classList.remove).toHaveBeenCalledWith('is-over'); }); it('removes is-over class if currentOpenMenu is null & there are sub-items', () => { @@ -148,9 +133,7 @@ describe('Fly out sidebar navigation', () => { mouseLeaveTopItem(el); - expect( - el.classList.remove, - ).toHaveBeenCalledWith('is-over'); + expect(el.classList.remove).toHaveBeenCalledWith('is-over'); }); it('does not remove is-over class if currentOpenMenu is the passed in sub-items', () => { @@ -159,34 +142,29 @@ describe('Fly out sidebar navigation', () => { setOpenMenu(el.querySelector('.sidebar-sub-level-items')); mouseLeaveTopItem(el); - expect( - el.classList.remove, - ).not.toHaveBeenCalled(); + expect(el.classList.remove).not.toHaveBeenCalled(); }); }); describe('mouseEnterTopItems', () => { beforeEach(() => { - el.innerHTML = '<div class="sidebar-sub-level-items" style="position: absolute; top: 0; left: 100px; height: 200px;"></div>'; + el.innerHTML = + '<div class="sidebar-sub-level-items" style="position: absolute; top: 0; left: 100px; height: 200px;"></div>'; }); - it('shows sub-items after 0ms if no menu is open', (done) => { + it('shows sub-items after 0ms if no menu is open', done => { mouseEnterTopItems(el); - expect( - getHideSubItemsInterval(), - ).toBe(0); + expect(getHideSubItemsInterval()).toBe(0); setTimeout(() => { - expect( - el.querySelector('.sidebar-sub-level-items').style.display, - ).toBe('block'); + expect(el.querySelector('.sidebar-sub-level-items').style.display).toBe('block'); done(); }); }); - it('shows sub-items after 300ms if a menu is currently open', (done) => { + it('shows sub-items after 300ms if a menu is currently open', done => { documentMouseMove({ clientX: el.getBoundingClientRect().left, clientY: el.getBoundingClientRect().top, @@ -201,14 +179,10 @@ describe('Fly out sidebar navigation', () => { mouseEnterTopItems(el, 0); - expect( - getHideSubItemsInterval(), - ).toBe(300); + expect(getHideSubItemsInterval()).toBe(300); setTimeout(() => { - expect( - el.querySelector('.sidebar-sub-level-items').style.display, - ).toBe('block'); + expect(el.querySelector('.sidebar-sub-level-items').style.display).toBe('block'); done(); }); @@ -225,9 +199,7 @@ describe('Fly out sidebar navigation', () => { showSubLevelItems(el); - expect( - el.classList.add, - ).toHaveBeenCalledWith('is-over'); + expect(el.classList.add).toHaveBeenCalledWith('is-over'); }); it('does not show sub-items on mobile', () => { @@ -235,17 +207,13 @@ describe('Fly out sidebar navigation', () => { showSubLevelItems(el); - expect( - el.querySelector('.sidebar-sub-level-items').style.display, - ).not.toBe('block'); + expect(el.querySelector('.sidebar-sub-level-items').style.display).not.toBe('block'); }); it('shows sub-items', () => { showSubLevelItems(el); - expect( - el.querySelector('.sidebar-sub-level-items').style.display, - ).toBe('block'); + expect(el.querySelector('.sidebar-sub-level-items').style.display).toBe('block'); }); it('shows collapsed only sub-items if icon only sidebar', () => { @@ -258,9 +226,7 @@ describe('Fly out sidebar navigation', () => { showSubLevelItems(el); - expect( - el.querySelector('.sidebar-sub-level-items').style.display, - ).toBe('block'); + expect(el.querySelector('.sidebar-sub-level-items').style.display).toBe('block'); }); it('does not show collapsed only sub-items if icon only sidebar', () => { @@ -269,9 +235,7 @@ describe('Fly out sidebar navigation', () => { showSubLevelItems(el); - expect( - subItems.style.display, - ).not.toBe('block'); + expect(subItems.style.display).not.toBe('block'); }); it('sets transform of sub-items', () => { @@ -285,9 +249,10 @@ describe('Fly out sidebar navigation', () => { setSidebar(sidebar); showSubLevelItems(el); - expect( - subItems.style.transform, - ).toBe(`translate3d(200px, ${Math.floor(el.getBoundingClientRect().top) - getHeaderHeight()}px, 0px)`); + expect(subItems.style.transform).toBe( + `translate3d(200px, ${Math.floor(el.getBoundingClientRect().top) - + getHeaderHeight()}px, 0px)`, + ); }); it('sets is-above when element is above', () => { @@ -299,33 +264,25 @@ describe('Fly out sidebar navigation', () => { showSubLevelItems(el); - expect( - subItems.classList.add, - ).toHaveBeenCalledWith('is-above'); + expect(subItems.classList.add).toHaveBeenCalledWith('is-above'); }); }); describe('canShowSubItems', () => { it('returns true if on desktop size', () => { - expect( - canShowSubItems(), - ).toBeTruthy(); + expect(canShowSubItems()).toBeTruthy(); }); it('returns false if on mobile size', () => { breakpointSize = 'xs'; - expect( - canShowSubItems(), - ).toBeFalsy(); + expect(canShowSubItems()).toBeFalsy(); }); }); describe('canShowActiveSubItems', () => { it('returns true by default', () => { - expect( - canShowActiveSubItems(el), - ).toBeTruthy(); + expect(canShowActiveSubItems(el)).toBeTruthy(); }); it('returns false when active & expanded sidebar', () => { @@ -334,9 +291,7 @@ describe('Fly out sidebar navigation', () => { setSidebar(sidebar); - expect( - canShowActiveSubItems(el), - ).toBeFalsy(); + expect(canShowActiveSubItems(el)).toBeFalsy(); }); it('returns true when active & collapsed sidebar', () => { @@ -346,9 +301,7 @@ describe('Fly out sidebar navigation', () => { setSidebar(sidebar); - expect( - canShowActiveSubItems(el), - ).toBeTruthy(); + expect(canShowActiveSubItems(el)).toBeTruthy(); }); }); @@ -362,18 +315,14 @@ describe('Fly out sidebar navigation', () => { it('hides subMenu if element is not hovered', () => { subItemsMouseLeave(el); - expect( - getOpenMenu(), - ).toBeNull(); + expect(getOpenMenu()).toBeNull(); }); it('does not hide subMenu if element is hovered', () => { el.classList.add('is-over'); subItemsMouseLeave(el); - expect( - getOpenMenu(), - ).not.toBeNull(); + expect(getOpenMenu()).not.toBeNull(); }); }); }); diff --git a/spec/javascripts/frequent_items/components/app_spec.js b/spec/javascripts/frequent_items/components/app_spec.js index 834f919524d..b1cc4d8dc8d 100644 --- a/spec/javascripts/frequent_items/components/app_spec.js +++ b/spec/javascripts/frequent_items/components/app_spec.js @@ -232,8 +232,7 @@ describe('Frequent Items App Component', () => { expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(1); vm.$store.dispatch('setSearchQuery', 'gitlab'); - vm - .$nextTick() + vm.$nextTick() .then(() => { expect(vm.$el.querySelector('.loading-animation')).toBeDefined(); }) diff --git a/spec/javascripts/frequent_items/components/frequent_items_list_item_spec.js b/spec/javascripts/frequent_items/components/frequent_items_list_item_spec.js index 201aca77b10..7deed985219 100644 --- a/spec/javascripts/frequent_items/components/frequent_items_list_item_spec.js +++ b/spec/javascripts/frequent_items/components/frequent_items_list_item_spec.js @@ -30,9 +30,11 @@ describe('FrequentItemsListItemComponent', () => { describe('hasAvatar', () => { it('should return `true` or `false` if whether avatar is present or not', () => { vm.avatarUrl = 'path/to/avatar.png'; + expect(vm.hasAvatar).toBe(true); vm.avatarUrl = null; + expect(vm.hasAvatar).toBe(false); }); }); @@ -40,11 +42,13 @@ describe('FrequentItemsListItemComponent', () => { describe('highlightedItemName', () => { it('should enclose part of project name in <b> & </b> which matches with `matcher` prop', () => { vm.matcher = 'lab'; + expect(vm.highlightedItemName).toContain('<b>Lab</b>'); }); it('should return project name as it is if `matcher` is not available', () => { vm.matcher = null; + expect(vm.highlightedItemName).toBe(mockProject.name); }); }); @@ -52,11 +56,13 @@ describe('FrequentItemsListItemComponent', () => { describe('truncatedNamespace', () => { it('should truncate project name from namespace string', () => { vm.namespace = 'platform / nokia-3310'; + expect(vm.truncatedNamespace).toBe('platform'); }); it('should truncate namespace string from the middle if it includes more than two groups in path', () => { vm.namespace = 'platform / hardware / broadcom / Wifi Group / Mobile Chipset / nokia-3310'; + expect(vm.truncatedNamespace).toBe('platform / ... / Mobile Chipset'); }); }); diff --git a/spec/javascripts/frequent_items/components/frequent_items_list_spec.js b/spec/javascripts/frequent_items/components/frequent_items_list_spec.js index 3003b7ee000..8518a681a26 100644 --- a/spec/javascripts/frequent_items/components/frequent_items_list_spec.js +++ b/spec/javascripts/frequent_items/components/frequent_items_list_spec.js @@ -30,9 +30,11 @@ describe('FrequentItemsListComponent', () => { describe('isListEmpty', () => { it('should return `true` or `false` representing whether if `items` is empty or not with projects', () => { vm.items = []; + expect(vm.isListEmpty).toBe(true); vm.items = mockFrequentProjects; + expect(vm.isListEmpty).toBe(false); }); }); @@ -40,9 +42,11 @@ describe('FrequentItemsListComponent', () => { describe('fetched item messages', () => { it('should return appropriate empty list message based on value of `localStorageFailed` prop with projects', () => { vm.isFetchFailed = true; + expect(vm.listEmptyMessage).toBe('This feature requires browser localStorage support'); vm.isFetchFailed = false; + expect(vm.listEmptyMessage).toBe('Projects you visit often will appear here'); }); }); @@ -51,9 +55,11 @@ describe('FrequentItemsListComponent', () => { it('should return appropriate empty list message based on value of `searchFailed` prop with projects', () => { vm.hasSearchQuery = true; vm.isFetchFailed = true; + expect(vm.listEmptyMessage).toBe('Something went wrong on our end.'); vm.isFetchFailed = false; + expect(vm.listEmptyMessage).toBe('Sorry, no projects matched your search'); }); }); diff --git a/spec/javascripts/frequent_items/components/frequent_items_search_input_spec.js b/spec/javascripts/frequent_items/components/frequent_items_search_input_spec.js index 6a11038e70a..d564292f1ba 100644 --- a/spec/javascripts/frequent_items/components/frequent_items_search_input_spec.js +++ b/spec/javascripts/frequent_items/components/frequent_items_search_input_spec.js @@ -26,6 +26,7 @@ describe('FrequentItemsSearchInputComponent', () => { spyOn(vm.$refs.search, 'focus'); vm.setFocus(); + expect(vm.$refs.search.focus).toHaveBeenCalled(); }); }); diff --git a/spec/javascripts/gfm_auto_complete_spec.js b/spec/javascripts/gfm_auto_complete_spec.js index b57c4943c01..6f414c8ccf1 100644 --- a/spec/javascripts/gfm_auto_complete_spec.js +++ b/spec/javascripts/gfm_auto_complete_spec.js @@ -6,39 +6,38 @@ import GfmAutoComplete from '~/gfm_auto_complete'; import 'vendor/jquery.caret'; import 'vendor/jquery.atwho'; -describe('GfmAutoComplete', function () { +describe('GfmAutoComplete', function() { const gfmAutoCompleteCallbacks = GfmAutoComplete.prototype.getDefaultCallbacks.call({ fetchData: () => {}, }); - describe('DefaultOptions.sorter', function () { - describe('assets loading', function () { - beforeEach(function () { + describe('DefaultOptions.sorter', function() { + describe('assets loading', function() { + beforeEach(function() { spyOn(GfmAutoComplete, 'isLoading').and.returnValue(true); this.atwhoInstance = { setting: {} }; this.items = []; - this.sorterValue = gfmAutoCompleteCallbacks.sorter - .call(this.atwhoInstance, '', this.items); + this.sorterValue = gfmAutoCompleteCallbacks.sorter.call(this.atwhoInstance, '', this.items); }); - it('should disable highlightFirst', function () { + it('should disable highlightFirst', function() { expect(this.atwhoInstance.setting.highlightFirst).toBe(false); }); - it('should return the passed unfiltered items', function () { + it('should return the passed unfiltered items', function() { expect(this.sorterValue).toEqual(this.items); }); }); - describe('assets finished loading', function () { - beforeEach(function () { + describe('assets finished loading', function() { + beforeEach(function() { spyOn(GfmAutoComplete, 'isLoading').and.returnValue(false); spyOn($.fn.atwho.default.callbacks, 'sorter'); }); - it('should enable highlightFirst if alwaysHighlightFirst is set', function () { + it('should enable highlightFirst if alwaysHighlightFirst is set', function() { const atwhoInstance = { setting: { alwaysHighlightFirst: true } }; gfmAutoCompleteCallbacks.sorter.call(atwhoInstance); @@ -46,7 +45,7 @@ describe('GfmAutoComplete', function () { expect(atwhoInstance.setting.highlightFirst).toBe(true); }); - it('should enable highlightFirst if a query is present', function () { + it('should enable highlightFirst if a query is present', function() { const atwhoInstance = { setting: {} }; gfmAutoCompleteCallbacks.sorter.call(atwhoInstance, 'query'); @@ -54,7 +53,7 @@ describe('GfmAutoComplete', function () { expect(atwhoInstance.setting.highlightFirst).toBe(true); }); - it('should call the default atwho sorter', function () { + it('should call the default atwho sorter', function() { const atwhoInstance = { setting: {} }; const query = 'query'; @@ -69,9 +68,8 @@ describe('GfmAutoComplete', function () { }); describe('DefaultOptions.beforeInsert', () => { - const beforeInsert = (context, value) => ( - gfmAutoCompleteCallbacks.beforeInsert.call(context, value) - ); + const beforeInsert = (context, value) => + gfmAutoCompleteCallbacks.beforeInsert.call(context, value); const atwhoInstance = { setting: { skipSpecialCharacterTest: false } }; @@ -98,29 +96,51 @@ describe('GfmAutoComplete', function () { }); }); - describe('DefaultOptions.matcher', function () { - const defaultMatcher = (context, flag, subtext) => ( - gfmAutoCompleteCallbacks.matcher.call(context, flag, subtext) - ); + describe('DefaultOptions.matcher', function() { + const defaultMatcher = (context, flag, subtext) => + gfmAutoCompleteCallbacks.matcher.call(context, flag, subtext); const flagsUseDefaultMatcher = ['@', '#', '!', '~', '%', '$']; const otherFlags = ['/', ':']; const flags = flagsUseDefaultMatcher.concat(otherFlags); - const flagsHash = flags.reduce((hash, el) => { hash[el] = null; return hash; }, {}); + const flagsHash = flags.reduce((hash, el) => { + hash[el] = null; + return hash; + }, {}); const atwhoInstance = { setting: {}, app: { controllers: flagsHash } }; const minLen = 1; const maxLen = 20; const argumentSize = [minLen, maxLen / 2, maxLen]; - const allowedSymbols = ['', 'a', 'n', 'z', 'A', 'Z', 'N', '0', '5', '9', 'А', 'а', 'Я', 'я', '.', '\'', '+', '-', '_']; + const allowedSymbols = [ + '', + 'a', + 'n', + 'z', + 'A', + 'Z', + 'N', + '0', + '5', + '9', + 'А', + 'а', + 'Я', + 'я', + '.', + "'", + '+', + '-', + '_', + ]; const jointAllowedSymbols = allowedSymbols.join(''); describe('should match regular symbols', () => { - flagsUseDefaultMatcher.forEach((flag) => { - allowedSymbols.forEach((symbol) => { - argumentSize.forEach((size) => { + flagsUseDefaultMatcher.forEach(flag => { + allowedSymbols.forEach(symbol => { + argumentSize.forEach(size => { const query = new Array(size + 1).join(symbol); const subtext = flag + query; @@ -142,8 +162,8 @@ describe('GfmAutoComplete', function () { const shouldNotBeFollowedBy = flags.concat(['\x00', '\x10', '\x3f', '\n', ' ']); const shouldNotBePrependedBy = ['`']; - flagsUseDefaultMatcher.forEach((atSign) => { - shouldNotBeFollowedBy.forEach((followedSymbol) => { + flagsUseDefaultMatcher.forEach(atSign => { + shouldNotBeFollowedBy.forEach(followedSymbol => { const seq = atSign + followedSymbol; it(`should not match ${JSON.stringify(seq)}`, () => { @@ -151,7 +171,7 @@ describe('GfmAutoComplete', function () { }); }); - shouldNotBePrependedBy.forEach((prependedSymbol) => { + shouldNotBePrependedBy.forEach(prependedSymbol => { const seq = prependedSymbol + atSign; it(`should not match "${seq}"`, () => { @@ -162,28 +182,26 @@ describe('GfmAutoComplete', function () { }); }); - describe('isLoading', function () { - it('should be true with loading data object item', function () { + describe('isLoading', function() { + it('should be true with loading data object item', function() { expect(GfmAutoComplete.isLoading({ name: 'loading' })).toBe(true); }); - it('should be true with loading data array', function () { + it('should be true with loading data array', function() { expect(GfmAutoComplete.isLoading(['loading'])).toBe(true); }); - it('should be true with loading data object array', function () { + it('should be true with loading data object array', function() { expect(GfmAutoComplete.isLoading([{ name: 'loading' }])).toBe(true); }); - it('should be false with actual array data', function () { - expect(GfmAutoComplete.isLoading([ - { title: 'Foo' }, - { title: 'Bar' }, - { title: 'Qux' }, - ])).toBe(false); + it('should be false with actual array data', function() { + expect( + GfmAutoComplete.isLoading([{ title: 'Foo' }, { title: 'Bar' }, { title: 'Qux' }]), + ).toBe(false); }); - it('should be false with actual data item', function () { + it('should be false with actual data item', function() { expect(GfmAutoComplete.isLoading({ title: 'Foo' })).toBe(false); }); }); diff --git a/spec/javascripts/gl_dropdown_spec.js b/spec/javascripts/gl_dropdown_spec.js index 62c87642184..85083653db8 100644 --- a/spec/javascripts/gl_dropdown_spec.js +++ b/spec/javascripts/gl_dropdown_spec.js @@ -8,7 +8,8 @@ describe('glDropdown', function describeDropdown() { preloadFixtures('static/gl_dropdown.html.raw'); loadJSONFixtures('projects.json'); - const NON_SELECTABLE_CLASSES = '.divider, .separator, .dropdown-header, .dropdown-menu-empty-item'; + const NON_SELECTABLE_CLASSES = + '.divider, .separator, .dropdown-header, .dropdown-menu-empty-item'; const SEARCH_INPUT_SELECTOR = '.dropdown-input-field'; const ITEM_SELECTOR = `.dropdown-content li:not(${NON_SELECTABLE_CLASSES})`; const FOCUSED_ITEM_SELECTOR = `${ITEM_SELECTOR} a.is-focused`; @@ -17,7 +18,7 @@ describe('glDropdown', function describeDropdown() { DOWN: 40, UP: 38, ENTER: 13, - ESC: 27 + ESC: 27, }; let remoteCallback; @@ -28,7 +29,7 @@ describe('glDropdown', function describeDropdown() { $('body').trigger({ type: 'keydown', which: ARROW_KEYS[direction], - keyCode: ARROW_KEYS[direction] + keyCode: ARROW_KEYS[direction], }); i += 1; if (i <= steps) { @@ -43,17 +44,23 @@ describe('glDropdown', function describeDropdown() { }; function initDropDown(hasRemote, isFilterable, extraOpts = {}) { - const options = Object.assign({ - selectable: true, - filterable: isFilterable, - data: hasRemote ? remoteMock.bind({}, this.projectsData) : this.projectsData, - search: { - fields: ['name'] + const options = Object.assign( + { + selectable: true, + filterable: isFilterable, + data: hasRemote ? remoteMock.bind({}, this.projectsData) : this.projectsData, + search: { + fields: ['name'], + }, + text: project => project.name_with_namespace || project.name, + id: project => project.id, }, - text: project => (project.name_with_namespace || project.name), - id: project => project.id, - }, extraOpts); - this.dropdownButtonElement = $('#js-project-dropdown', this.dropdownContainerElement).glDropdown(options); + extraOpts, + ); + this.dropdownButtonElement = $( + '#js-project-dropdown', + this.dropdownContainerElement, + ).glDropdown(options); } beforeEach(() => { @@ -70,8 +77,10 @@ describe('glDropdown', function describeDropdown() { it('should open on click', () => { initDropDown.call(this, false); + expect(this.dropdownContainerElement).not.toHaveClass('show'); this.dropdownButtonElement.click(); + expect(this.dropdownContainerElement).toHaveClass('show'); }); @@ -82,9 +91,7 @@ describe('glDropdown', function describeDropdown() { this.dropdownButtonElement.click(); - expect( - $('.dropdown-content li:first-child').text(), - ).toBe('<script>alert("testing");</script>'); + expect($('.dropdown-content li:first-child').text()).toBe('<script>alert("testing");</script>'); }); it('should output HTML when highlighting', () => { @@ -97,13 +104,11 @@ describe('glDropdown', function describeDropdown() { this.dropdownButtonElement.click(); - expect( - $('.dropdown-content li:first-child').text(), - ).toBe('testing'); + expect($('.dropdown-content li:first-child').text()).toBe('testing'); - expect( - $('.dropdown-content li:first-child a').html(), - ).toBe('<b>t</b><b>e</b><b>s</b><b>t</b>ing'); + expect($('.dropdown-content li:first-child a').html()).toBe( + '<b>t</b><b>e</b><b>s</b><b>t</b>ing', + ); }); describe('that is open', () => { @@ -114,21 +119,28 @@ describe('glDropdown', function describeDropdown() { it('should select a following item on DOWN keypress', () => { expect($(FOCUSED_ITEM_SELECTOR, this.$dropdownMenuElement).length).toBe(0); - const randomIndex = (Math.floor(Math.random() * (this.projectsData.length - 1)) + 0); + const randomIndex = Math.floor(Math.random() * (this.projectsData.length - 1)) + 0; navigateWithKeys('down', randomIndex, () => { expect($(FOCUSED_ITEM_SELECTOR, this.$dropdownMenuElement).length).toBe(1); - expect($(`${ITEM_SELECTOR}:eq(${randomIndex}) a`, this.$dropdownMenuElement)).toHaveClass('is-focused'); + expect($(`${ITEM_SELECTOR}:eq(${randomIndex}) a`, this.$dropdownMenuElement)).toHaveClass( + 'is-focused', + ); }); }); it('should select a previous item on UP keypress', () => { expect($(FOCUSED_ITEM_SELECTOR, this.$dropdownMenuElement).length).toBe(0); - navigateWithKeys('down', (this.projectsData.length - 1), () => { + navigateWithKeys('down', this.projectsData.length - 1, () => { expect($(FOCUSED_ITEM_SELECTOR, this.$dropdownMenuElement).length).toBe(1); - const randomIndex = (Math.floor(Math.random() * (this.projectsData.length - 2)) + 0); + const randomIndex = Math.floor(Math.random() * (this.projectsData.length - 2)) + 0; navigateWithKeys('up', randomIndex, () => { expect($(FOCUSED_ITEM_SELECTOR, this.$dropdownMenuElement).length).toBe(1); - expect($(`${ITEM_SELECTOR}:eq(${((this.projectsData.length - 2) - randomIndex)}) a`, this.$dropdownMenuElement)).toHaveClass('is-focused'); + expect( + $( + `${ITEM_SELECTOR}:eq(${this.projectsData.length - 2 - randomIndex}) a`, + this.$dropdownMenuElement, + ), + ).toHaveClass('is-focused'); }); }); }); @@ -141,9 +153,12 @@ describe('glDropdown', function describeDropdown() { navigateWithKeys('enter', null, () => { expect(this.dropdownContainerElement).not.toHaveClass('show'); const link = $(`${ITEM_SELECTOR}:eq(${randomIndex}) a`, this.$dropdownMenuElement); + expect(link).toHaveClass('is-active'); const linkedLocation = link.attr('href'); - if (linkedLocation && linkedLocation !== '#') expect(visitUrl).toHaveBeenCalledWith(linkedLocation); + if (linkedLocation && linkedLocation !== '#') { + expect(visitUrl).toHaveBeenCalledWith(linkedLocation); + } }); }); }); @@ -153,8 +168,9 @@ describe('glDropdown', function describeDropdown() { this.dropdownContainerElement.trigger({ type: 'keyup', which: ARROW_KEYS.ESC, - keyCode: ARROW_KEYS.ESC + keyCode: ARROW_KEYS.ESC, }); + expect(this.dropdownContainerElement).not.toHaveClass('show'); }); }); @@ -170,17 +186,20 @@ describe('glDropdown', function describeDropdown() { expect(dropdownMenu.className.indexOf('is-loading')).not.toBe(-1); remoteCallback(); + expect(dropdownMenu.className.indexOf('is-loading')).toBe(-1); }); it('should not focus search input while remote task is not complete', () => { expect($(document.activeElement)).not.toEqual($(SEARCH_INPUT_SELECTOR)); remoteCallback(); + expect($(document.activeElement)).toEqual($(SEARCH_INPUT_SELECTOR)); }); it('should focus search input after remote task is complete', () => { remoteCallback(); + expect($(document.activeElement)).toEqual($(SEARCH_INPUT_SELECTOR)); }); @@ -189,10 +208,11 @@ describe('glDropdown', function describeDropdown() { this.dropdownContainerElement.trigger({ type: 'keyup', which: ARROW_KEYS.ESC, - keyCode: ARROW_KEYS.ESC + keyCode: ARROW_KEYS.ESC, }); this.dropdownButtonElement.click(); this.dropdownContainerElement.trigger('transitionend'); + expect($(document.activeElement)).toEqual($(SEARCH_INPUT_SELECTOR)); }); }); @@ -202,6 +222,7 @@ describe('glDropdown', function describeDropdown() { initDropDown.call(this, false, true); this.dropdownButtonElement.click(); this.dropdownContainerElement.trigger('transitionend'); + expect($(document.activeElement)).toEqual($(SEARCH_INPUT_SELECTOR)); }); }); @@ -213,11 +234,11 @@ describe('glDropdown', function describeDropdown() { .trigger('focus') .val('g') .trigger('input'); + expect($searchInput.val()).toEqual('g'); this.dropdownButtonElement.trigger('hidden.bs.dropdown'); - $searchInput - .trigger('blur') - .trigger('focus'); + $searchInput.trigger('blur').trigger('focus'); + expect($searchInput.val()).toEqual('g'); }); @@ -226,31 +247,31 @@ describe('glDropdown', function describeDropdown() { let dropdown; beforeEach(() => { - const dropdownOptions = { - - }; + const dropdownOptions = {}; const $dropdownDiv = $('<div />'); $dropdownDiv.glDropdown(dropdownOptions); dropdown = $dropdownDiv.data('glDropdown'); }); it('marks items without ID as active', () => { - const dummyData = { }; + const dummyData = {}; const html = dropdown.renderItem(dummyData, null, null); const link = html.querySelector('a'); + expect(link).toHaveClass('is-active'); }); it('does not mark items with ID as active', () => { const dummyData = { - id: 'ea' + id: 'ea', }; const html = dropdown.renderItem(dummyData, null, null); const link = html.querySelector('a'); + expect(link).not.toHaveClass('is-active'); }); }); @@ -271,13 +292,14 @@ describe('glDropdown', function describeDropdown() { // select item the first time this.dropdownButtonElement.click(); $item.click(); + expect($item).toHaveClass('is-active'); // select item the second time this.dropdownButtonElement.click(); $item.click(); + expect($item).toHaveClass('is-active'); expect($('.dropdown-toggle-text')).toHaveText(this.projectsData[0].id.toString()); }); }); - diff --git a/spec/javascripts/gl_field_errors_spec.js b/spec/javascripts/gl_field_errors_spec.js index 21c462cd040..b463c9afbee 100644 --- a/spec/javascripts/gl_field_errors_spec.js +++ b/spec/javascripts/gl_field_errors_spec.js @@ -19,6 +19,7 @@ describe('GL Style Field Errors', function() { expect(this.$form.length).toBe(1); expect(this.fieldErrors).toBeDefined(); const { inputs } = this.fieldErrors.state; + expect(inputs.length).toBe(4); }); @@ -28,29 +29,50 @@ describe('GL Style Field Errors', function() { expect(customErrorElem.length).toBe(1); - const customErrors = this.fieldErrors.state.inputs.filter((input) => { + const customErrors = this.fieldErrors.state.inputs.filter(input => { return input.inputElement.hasClass(customErrorFlag); }); + expect(customErrors.length).toBe(0); }); it('should not show any errors before submit attempt', function() { - this.$form.find('.email').val('not-a-valid-email').keyup(); - this.$form.find('.text-required').val('').keyup(); - this.$form.find('.alphanumberic').val('?---*').keyup(); + this.$form + .find('.email') + .val('not-a-valid-email') + .keyup(); + this.$form + .find('.text-required') + .val('') + .keyup(); + this.$form + .find('.alphanumberic') + .val('?---*') + .keyup(); const errorsShown = this.$form.find('.gl-field-error-outline'); + expect(errorsShown.length).toBe(0); }); it('should show errors when input valid is submitted', function() { - this.$form.find('.email').val('not-a-valid-email').keyup(); - this.$form.find('.text-required').val('').keyup(); - this.$form.find('.alphanumberic').val('?---*').keyup(); + this.$form + .find('.email') + .val('not-a-valid-email') + .keyup(); + this.$form + .find('.text-required') + .val('') + .keyup(); + this.$form + .find('.alphanumberic') + .val('?---*') + .keyup(); this.$form.submit(); const errorsShown = this.$form.find('.gl-field-error-outline'); + expect(errorsShown.length).toBe(4); }); @@ -68,30 +90,35 @@ describe('GL Style Field Errors', function() { // Then invalid input emailInputElement.val('not-a-valid-email').keyup(); + expect(emailInputElement).toHaveClass('gl-field-error-outline'); expect(fieldState.empty).toBe(false); expect(fieldState.valid).toBe(false); // Then valid input emailInputElement.val('email@gitlab.com').keyup(); + expect(emailInputElement).not.toHaveClass('gl-field-error-outline'); expect(fieldState.empty).toBe(false); expect(fieldState.valid).toBe(true); // Then invalid input emailInputElement.val('not-a-valid-email').keyup(); + expect(emailInputElement).toHaveClass('gl-field-error-outline'); expect(fieldState.empty).toBe(false); expect(fieldState.valid).toBe(false); // Then empty input emailInputElement.val('').keyup(); + expect(emailInputElement).toHaveClass('gl-field-error-outline'); expect(fieldState.empty).toBe(true); expect(fieldState.valid).toBe(false); // Then valid input emailInputElement.val('email@gitlab.com').keyup(); + expect(emailInputElement).not.toHaveClass('gl-field-error-outline'); expect(fieldState.empty).toBe(false); expect(fieldState.valid).toBe(true); diff --git a/spec/javascripts/gl_form_spec.js b/spec/javascripts/gl_form_spec.js index 74383f901b2..69b3dae743a 100644 --- a/spec/javascripts/gl_form_spec.js +++ b/spec/javascripts/gl_form_spec.js @@ -5,8 +5,8 @@ import '~/lib/utils/text_utility'; import '~/lib/utils/common_utils'; describe('GLForm', () => { - describe('when instantiated', function () { - beforeEach((done) => { + describe('when instantiated', function() { + beforeEach(done => { this.form = $('<form class="gfm-form"><textarea class="js-gfm-input"></form>'); this.textarea = this.form.find('textarea'); spyOn($.prototype, 'off').and.returnValue(this.textarea); @@ -23,7 +23,7 @@ describe('GLForm', () => { }); describe('setupAutosize', () => { - beforeEach((done) => { + beforeEach(done => { this.glForm.setupAutosize(); setTimeout(() => { done(); @@ -101,6 +101,7 @@ describe('GLForm', () => { spyOn($.prototype, 'outerHeight').and.returnValue(200); spyOn($.prototype, 'data').and.returnValue(200); spyOn(autosize, 'destroy'); + expect(this.glForm.destroyAutosize()).toBeUndefined(); expect(autosize.destroy).not.toHaveBeenCalled(); }); diff --git a/spec/javascripts/gpg_badges_spec.js b/spec/javascripts/gpg_badges_spec.js index 78330dd9633..e73f6d3909e 100644 --- a/spec/javascripts/gpg_badges_spec.js +++ b/spec/javascripts/gpg_badges_spec.js @@ -69,6 +69,7 @@ describe('GpgBadges', () => { .then(() => { expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null); const spinners = document.querySelectorAll('.js-loading-gpg-badge i.fa.fa-spinner.fa-spin'); + expect(spinners.length).toBe(1); done(); }) @@ -82,6 +83,7 @@ describe('GpgBadges', () => { .then(() => { expect(document.querySelector('.js-loading-gpg-badge')).toBe(null); const parentContainer = document.querySelector('.parent-container'); + expect(parentContainer.innerHTML.trim()).toEqual(dummyBadgeHtml); done(); }) diff --git a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js index 4a4d6969e86..563d134ca81 100644 --- a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js +++ b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js @@ -2,99 +2,118 @@ import { scaleLinear, scaleTime } from 'd3-scale'; import { timeParse } from 'd3-time-format'; -import { ContributorsGraph, ContributorsMasterGraph } from '~/pages/projects/graphs/show/stat_graph_contributors_graph'; +import { + ContributorsGraph, + ContributorsMasterGraph, +} from '~/pages/projects/graphs/show/stat_graph_contributors_graph'; const d3 = { scaleLinear, scaleTime, timeParse }; -describe("ContributorsGraph", function () { - describe("#set_x_domain", function () { - it("set the x_domain", function () { +describe('ContributorsGraph', function() { + describe('#set_x_domain', function() { + it('set the x_domain', function() { ContributorsGraph.set_x_domain(20); + expect(ContributorsGraph.prototype.x_domain).toEqual(20); }); }); - describe("#set_y_domain", function () { - it("sets the y_domain", function () { + describe('#set_y_domain', function() { + it('sets the y_domain', function() { ContributorsGraph.set_y_domain([{ commits: 30 }]); + expect(ContributorsGraph.prototype.y_domain).toEqual([0, 30]); }); }); - describe("#init_x_domain", function () { - it("sets the initial x_domain", function () { - ContributorsGraph.init_x_domain([{ date: "2013-01-31" }, { date: "2012-01-31" }]); - expect(ContributorsGraph.prototype.x_domain).toEqual(["2012-01-31", "2013-01-31"]); + describe('#init_x_domain', function() { + it('sets the initial x_domain', function() { + ContributorsGraph.init_x_domain([{ date: '2013-01-31' }, { date: '2012-01-31' }]); + + expect(ContributorsGraph.prototype.x_domain).toEqual(['2012-01-31', '2013-01-31']); }); }); - describe("#init_y_domain", function () { - it("sets the initial y_domain", function () { + describe('#init_y_domain', function() { + it('sets the initial y_domain', function() { ContributorsGraph.init_y_domain([{ commits: 30 }]); + expect(ContributorsGraph.prototype.y_domain).toEqual([0, 30]); }); }); - describe("#init_domain", function () { - it("calls init_x_domain and init_y_domain", function () { - spyOn(ContributorsGraph, "init_x_domain"); - spyOn(ContributorsGraph, "init_y_domain"); + describe('#init_domain', function() { + it('calls init_x_domain and init_y_domain', function() { + spyOn(ContributorsGraph, 'init_x_domain'); + spyOn(ContributorsGraph, 'init_y_domain'); ContributorsGraph.init_domain(); + expect(ContributorsGraph.init_x_domain).toHaveBeenCalled(); expect(ContributorsGraph.init_y_domain).toHaveBeenCalled(); }); }); - describe("#set_dates", function () { - it("sets the dates", function () { - ContributorsGraph.set_dates("2013-12-01"); - expect(ContributorsGraph.prototype.dates).toEqual("2013-12-01"); + describe('#set_dates', function() { + it('sets the dates', function() { + ContributorsGraph.set_dates('2013-12-01'); + + expect(ContributorsGraph.prototype.dates).toEqual('2013-12-01'); }); }); - describe("#set_x_domain", function () { - it("sets the instance's x domain using the prototype's x_domain", function () { + describe('#set_x_domain', function() { + it("sets the instance's x domain using the prototype's x_domain", function() { ContributorsGraph.prototype.x_domain = 20; var instance = new ContributorsGraph(); - instance.x = d3.scaleTime().range([0, 100]).clamp(true); + instance.x = d3 + .scaleTime() + .range([0, 100]) + .clamp(true); spyOn(instance.x, 'domain'); instance.set_x_domain(); + expect(instance.x.domain).toHaveBeenCalledWith(20); }); }); - describe("#set_y_domain", function () { - it("sets the instance's y domain using the prototype's y_domain", function () { + describe('#set_y_domain', function() { + it("sets the instance's y domain using the prototype's y_domain", function() { ContributorsGraph.prototype.y_domain = 30; var instance = new ContributorsGraph(); - instance.y = d3.scaleLinear().range([100, 0]).nice(); + instance.y = d3 + .scaleLinear() + .range([100, 0]) + .nice(); spyOn(instance.y, 'domain'); instance.set_y_domain(); + expect(instance.y.domain).toHaveBeenCalledWith(30); }); }); - describe("#set_domain", function () { - it("calls set_x_domain and set_y_domain", function () { + describe('#set_domain', function() { + it('calls set_x_domain and set_y_domain', function() { var instance = new ContributorsGraph(); spyOn(instance, 'set_x_domain'); spyOn(instance, 'set_y_domain'); instance.set_domain(); + expect(instance.set_x_domain).toHaveBeenCalled(); expect(instance.set_y_domain).toHaveBeenCalled(); }); }); - describe("#set_data", function () { - it("sets the data", function () { + describe('#set_data', function() { + it('sets the data', function() { var instance = new ContributorsGraph(); - instance.set_data("20"); - expect(instance.data).toEqual("20"); + instance.set_data('20'); + + expect(instance.data).toEqual('20'); }); }); }); -describe("ContributorsMasterGraph", function () { +describe('ContributorsMasterGraph', function() { // TODO: fix or remove // describe("#process_dates", function () { // it("gets and parses dates", function () { @@ -110,21 +129,23 @@ describe("ContributorsMasterGraph", function () { // }); // }); - describe("#get_dates", function () { - it("plucks the date field from data collection", function () { + describe('#get_dates', function() { + it('plucks the date field from data collection', function() { var graph = new ContributorsMasterGraph(); - var data = [{ date: "2013-01-01" }, { date: "2012-12-15" }]; - expect(graph.get_dates(data)).toEqual(["2013-01-01", "2012-12-15"]); + var data = [{ date: '2013-01-01' }, { date: '2012-12-15' }]; + + expect(graph.get_dates(data)).toEqual(['2013-01-01', '2012-12-15']); }); }); - describe("#parse_dates", function () { - it("parses the dates", function () { + describe('#parse_dates', function() { + it('parses the dates', function() { var graph = new ContributorsMasterGraph(); - var parseDate = d3.timeParse("%Y-%m-%d"); - var data = [{ date: "2013-01-01" }, { date: "2012-12-15" }]; + var parseDate = d3.timeParse('%Y-%m-%d'); + var data = [{ date: '2013-01-01' }, { date: '2012-12-15' }]; var correct = [{ date: parseDate(data[0].date) }, { date: parseDate(data[1].date) }]; graph.parse_dates(data); + expect(data).toEqual(correct); }); }); diff --git a/spec/javascripts/graphs/stat_graph_contributors_spec.js b/spec/javascripts/graphs/stat_graph_contributors_spec.js index e03114c1cc5..2ebb6845a8b 100644 --- a/spec/javascripts/graphs/stat_graph_contributors_spec.js +++ b/spec/javascripts/graphs/stat_graph_contributors_spec.js @@ -20,7 +20,9 @@ describe('ContributorsStatGraph', () => { graph.change_date_header(); - expect(document.getElementById('date_header').innerText).toBe('31. Januar 2012 – 31. Januar 2013'); + expect(document.getElementById('date_header').innerText).toBe( + '31. Januar 2012 – 31. Januar 2013', + ); }); }); }); diff --git a/spec/javascripts/graphs/stat_graph_contributors_util_spec.js b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js index 8854d3d554a..511b660c671 100644 --- a/spec/javascripts/graphs/stat_graph_contributors_util_spec.js +++ b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js @@ -2,56 +2,85 @@ import ContributorsStatGraphUtil from '~/pages/projects/graphs/show/stat_graph_contributors_util'; -describe("ContributorsStatGraphUtil", function () { - describe("#parse_log", function () { - it("returns a correctly parsed log", function () { +describe('ContributorsStatGraphUtil', function() { + describe('#parse_log', function() { + it('returns a correctly parsed log', function() { var fake_log = [ - { author_email: "karlo@email.com", author_name: "Karlo Soriano", date: "2013-05-09", additions: 471 }, - { author_email: "dzaporozhets@email.com", author_name: "Dmitriy Zaporozhets", date: "2013-05-08", additions: 6, deletions: 1 }, - { author_email: "dzaporozhets@email.com", author_name: "Dmitriy Zaporozhets", date: "2013-05-08", additions: 19, deletions: 3 }, - { author_email: "dzaporozhets@email.com", author_name: "Dmitriy Zaporozhets", date: "2013-05-08", additions: 29, deletions: 3 } + { + author_email: 'karlo@email.com', + author_name: 'Karlo Soriano', + date: '2013-05-09', + additions: 471, + }, + { + author_email: 'dzaporozhets@email.com', + author_name: 'Dmitriy Zaporozhets', + date: '2013-05-08', + additions: 6, + deletions: 1, + }, + { + author_email: 'dzaporozhets@email.com', + author_name: 'Dmitriy Zaporozhets', + date: '2013-05-08', + additions: 19, + deletions: 3, + }, + { + author_email: 'dzaporozhets@email.com', + author_name: 'Dmitriy Zaporozhets', + date: '2013-05-08', + additions: 29, + deletions: 3, + }, ]; var correct_parsed_log = { total: [ - { date: "2013-05-09", additions: 471, deletions: 0, commits: 1 }, - { date: "2013-05-08", additions: 54, deletions: 7, commits: 3 } + { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 }, + { date: '2013-05-08', additions: 54, deletions: 7, commits: 3 }, ], by_author: [ { - author_name: "Karlo Soriano", author_email: "karlo@email.com", - "2013-05-09": { date: "2013-05-09", additions: 471, deletions: 0, commits: 1 } + author_name: 'Karlo Soriano', + author_email: 'karlo@email.com', + '2013-05-09': { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 }, }, { - author_name: "Dmitriy Zaporozhets", author_email: "dzaporozhets@email.com", - "2013-05-08": { date: "2013-05-08", additions: 54, deletions: 7, commits: 3 } - } - ] + author_name: 'Dmitriy Zaporozhets', + author_email: 'dzaporozhets@email.com', + '2013-05-08': { date: '2013-05-08', additions: 54, deletions: 7, commits: 3 }, + }, + ], }; + expect(ContributorsStatGraphUtil.parse_log(fake_log)).toEqual(correct_parsed_log); }); }); - describe("#store_data", function () { - var fake_entry = { author: "Karlo Soriano", date: "2013-05-09", additions: 471 }; + describe('#store_data', function() { + var fake_entry = { author: 'Karlo Soriano', date: '2013-05-09', additions: 471 }; var fake_total = {}; var fake_by_author = {}; - it("calls #store_commits", function () { + it('calls #store_commits', function() { spyOn(ContributorsStatGraphUtil, 'store_commits'); ContributorsStatGraphUtil.store_data(fake_entry, fake_total, fake_by_author); + expect(ContributorsStatGraphUtil.store_commits).toHaveBeenCalled(); }); - it("calls #store_additions", function () { + it('calls #store_additions', function() { spyOn(ContributorsStatGraphUtil, 'store_additions'); ContributorsStatGraphUtil.store_data(fake_entry, fake_total, fake_by_author); + expect(ContributorsStatGraphUtil.store_additions).toHaveBeenCalled(); }); - it("calls #store_deletions", function () { + it('calls #store_deletions', function() { spyOn(ContributorsStatGraphUtil, 'store_deletions'); ContributorsStatGraphUtil.store_data(fake_entry, fake_total, fake_by_author); + expect(ContributorsStatGraphUtil.store_deletions).toHaveBeenCalled(); }); }); @@ -68,16 +97,18 @@ describe("ContributorsStatGraphUtil", function () { // }); // }); - describe("#add", function () { - it("adds 1 to current test_field in collection", function () { + describe('#add', function() { + it('adds 1 to current test_field in collection', function() { var fake_collection = { test_field: 10 }; - ContributorsStatGraphUtil.add(fake_collection, "test_field", 1); + ContributorsStatGraphUtil.add(fake_collection, 'test_field', 1); + expect(fake_collection.test_field).toEqual(11); }); - it("inits and adds 1 if test_field in collection is not defined", function () { + it('inits and adds 1 if test_field in collection is not defined', function() { var fake_collection = {}; - ContributorsStatGraphUtil.add(fake_collection, "test_field", 1); + ContributorsStatGraphUtil.add(fake_collection, 'test_field', 1); + expect(fake_collection.test_field).toEqual(1); }); }); @@ -106,113 +137,161 @@ describe("ContributorsStatGraphUtil", function () { // }); // }); - describe("#add_date", function () { - it("adds a date field to the collection", function () { - var fake_date = "2013-10-02"; + describe('#add_date', function() { + it('adds a date field to the collection', function() { + var fake_date = '2013-10-02'; var fake_collection = {}; ContributorsStatGraphUtil.add_date(fake_date, fake_collection); - expect(fake_collection[fake_date].date).toEqual("2013-10-02"); + + expect(fake_collection[fake_date].date).toEqual('2013-10-02'); }); }); - describe("#add_author", function () { - it("adds an author field to the collection", function () { - var fake_author = { author_name: "Author", author_email: 'fake@email.com' }; + describe('#add_author', function() { + it('adds an author field to the collection', function() { + var fake_author = { author_name: 'Author', author_email: 'fake@email.com' }; var fake_author_collection = {}; var fake_email_collection = {}; - ContributorsStatGraphUtil.add_author(fake_author, fake_author_collection, fake_email_collection); - expect(fake_author_collection[fake_author.author_name].author_name).toEqual("Author"); - expect(fake_email_collection[fake_author.author_email].author_name).toEqual("Author"); + ContributorsStatGraphUtil.add_author( + fake_author, + fake_author_collection, + fake_email_collection, + ); + + expect(fake_author_collection[fake_author.author_name].author_name).toEqual('Author'); + expect(fake_email_collection[fake_author.author_email].author_name).toEqual('Author'); }); }); - describe("#get_total_data", function () { - it("returns the collection sorted via specified field", function () { + describe('#get_total_data', function() { + it('returns the collection sorted via specified field', function() { var fake_parsed_log = { total: [ - { date: "2013-05-09", additions: 471, deletions: 0, commits: 1 }, - { date: "2013-05-08", additions: 54, deletions: 7, commits: 3 } + { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 }, + { date: '2013-05-08', additions: 54, deletions: 7, commits: 3 }, ], by_author: [ { - author: "Karlo Soriano", - "2013-05-09": { date: "2013-05-09", additions: 471, deletions: 0, commits: 1 } + author: 'Karlo Soriano', + '2013-05-09': { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 }, }, { - author: "Dmitriy Zaporozhets", - "2013-05-08": { date: "2013-05-08", additions: 54, deletions: 7, commits: 3 } - } - ] + author: 'Dmitriy Zaporozhets', + '2013-05-08': { date: '2013-05-08', additions: 54, deletions: 7, commits: 3 }, + }, + ], }; var correct_total_data = [ - { date: "2013-05-08", commits: 3 }, - { date: "2013-05-09", commits: 1 } + { date: '2013-05-08', commits: 3 }, + { date: '2013-05-09', commits: 1 }, ]; - expect(ContributorsStatGraphUtil.get_total_data(fake_parsed_log, "commits")).toEqual(correct_total_data); + + expect(ContributorsStatGraphUtil.get_total_data(fake_parsed_log, 'commits')).toEqual( + correct_total_data, + ); }); }); - describe("#pick_field", function () { - it("returns the collection with only the specified field and date", function () { + describe('#pick_field', function() { + it('returns the collection with only the specified field and date', function() { var fake_parsed_log_total = [ - { date: "2013-05-09", additions: 471, deletions: 0, commits: 1 }, - { date: "2013-05-08", additions: 54, deletions: 7, commits: 3 } + { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 }, + { date: '2013-05-08', additions: 54, deletions: 7, commits: 3 }, + ]; + ContributorsStatGraphUtil.pick_field(fake_parsed_log_total, 'commits'); + var correct_pick_field_data = [ + { date: '2013-05-09', commits: 1 }, + { date: '2013-05-08', commits: 3 }, ]; - ContributorsStatGraphUtil.pick_field(fake_parsed_log_total, "commits"); - var correct_pick_field_data = [{ date: "2013-05-09", commits: 1 }, { date: "2013-05-08", commits: 3 }]; - expect(ContributorsStatGraphUtil.pick_field(fake_parsed_log_total, "commits")).toEqual(correct_pick_field_data); + + expect(ContributorsStatGraphUtil.pick_field(fake_parsed_log_total, 'commits')).toEqual( + correct_pick_field_data, + ); }); }); - describe("#get_author_data", function () { - it("returns the log by author sorted by specified field", function () { + describe('#get_author_data', function() { + it('returns the log by author sorted by specified field', function() { var fake_parsed_log = { total: [ - { date: "2013-05-09", additions: 471, deletions: 0, commits: 1 }, - { date: "2013-05-08", additions: 54, deletions: 7, commits: 3 } + { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 }, + { date: '2013-05-08', additions: 54, deletions: 7, commits: 3 }, ], by_author: [ { - author_name: "Karlo Soriano", author_email: "karlo@email.com", - "2013-05-09": { date: "2013-05-09", additions: 471, deletions: 0, commits: 1 } + author_name: 'Karlo Soriano', + author_email: 'karlo@email.com', + '2013-05-09': { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 }, }, { - author_name: "Dmitriy Zaporozhets", author_email: "dzaporozhets@email.com", - "2013-05-08": { date: "2013-05-08", additions: 54, deletions: 7, commits: 3 } - } - ] + author_name: 'Dmitriy Zaporozhets', + author_email: 'dzaporozhets@email.com', + '2013-05-08': { date: '2013-05-08', additions: 54, deletions: 7, commits: 3 }, + }, + ], }; var correct_author_data = [ - { author_name: "Dmitriy Zaporozhets", author_email: "dzaporozhets@email.com", dates: { "2013-05-08": 3 }, deletions: 7, additions: 54, "commits": 3 }, - { author_name: "Karlo Soriano", author_email: "karlo@email.com", dates: { "2013-05-09": 1 }, deletions: 0, additions: 471, commits: 1 } + { + author_name: 'Dmitriy Zaporozhets', + author_email: 'dzaporozhets@email.com', + dates: { '2013-05-08': 3 }, + deletions: 7, + additions: 54, + commits: 3, + }, + { + author_name: 'Karlo Soriano', + author_email: 'karlo@email.com', + dates: { '2013-05-09': 1 }, + deletions: 0, + additions: 471, + commits: 1, + }, ]; - expect(ContributorsStatGraphUtil.get_author_data(fake_parsed_log, "commits")).toEqual(correct_author_data); + + expect(ContributorsStatGraphUtil.get_author_data(fake_parsed_log, 'commits')).toEqual( + correct_author_data, + ); }); }); - describe("#parse_log_entry", function () { - it("adds the corresponding info from the log entry to the author", function () { - var fake_log_entry = { author_name: "Karlo Soriano", author_email: "karlo@email.com", - "2013-05-09": { date: "2013-05-09", additions: 471, deletions: 0, commits: 1 } + describe('#parse_log_entry', function() { + it('adds the corresponding info from the log entry to the author', function() { + var fake_log_entry = { + author_name: 'Karlo Soriano', + author_email: 'karlo@email.com', + '2013-05-09': { date: '2013-05-09', additions: 471, deletions: 0, commits: 1 }, }; - var correct_parsed_log = { author_name: "Karlo Soriano", author_email: "karlo@email.com", dates: { "2013-05-09": 1 }, deletions: 0, additions: 471, commits: 1 }; - expect(ContributorsStatGraphUtil.parse_log_entry(fake_log_entry, 'commits', null)).toEqual(correct_parsed_log); + var correct_parsed_log = { + author_name: 'Karlo Soriano', + author_email: 'karlo@email.com', + dates: { '2013-05-09': 1 }, + deletions: 0, + additions: 471, + commits: 1, + }; + + expect(ContributorsStatGraphUtil.parse_log_entry(fake_log_entry, 'commits', null)).toEqual( + correct_parsed_log, + ); }); }); - describe("#in_range", function () { - var date = "2013-05-09"; - it("returns true if date_range is null", function () { + describe('#in_range', function() { + var date = '2013-05-09'; + it('returns true if date_range is null', function() { expect(ContributorsStatGraphUtil.in_range(date, null)).toEqual(true); }); - it("returns true if date is in range", function () { - var date_range = [new Date("2013-01-01"), new Date("2013-12-12")]; + it('returns true if date is in range', function() { + var date_range = [new Date('2013-01-01'), new Date('2013-12-12')]; + expect(ContributorsStatGraphUtil.in_range(date, date_range)).toEqual(true); }); - it("returns false if date is not in range", function () { - var date_range = [new Date("1999-12-01"), new Date("2000-12-01")]; + it('returns false if date is not in range', function() { + var date_range = [new Date('1999-12-01'), new Date('2000-12-01')]; + expect(ContributorsStatGraphUtil.in_range(date, date_range)).toEqual(false); }); }); diff --git a/spec/javascripts/groups/components/app_spec.js b/spec/javascripts/groups/components/app_spec.js index 89c07d1f06d..d832441dc93 100644 --- a/spec/javascripts/groups/components/app_spec.js +++ b/spec/javascripts/groups/components/app_spec.js @@ -76,6 +76,7 @@ describe('AppComponent', () => { spyOn(vm.store, 'getGroups'); const { groups } = vm; + expect(vm.store.getGroups).toHaveBeenCalled(); expect(groups).not.toBeDefined(); }); @@ -86,6 +87,7 @@ describe('AppComponent', () => { spyOn(vm.store, 'getPaginationInfo'); const { pageInfo } = vm; + expect(vm.store.getPaginationInfo).toHaveBeenCalled(); expect(pageInfo).not.toBeDefined(); }); @@ -154,6 +156,7 @@ describe('AppComponent', () => { spyOn(vm, 'updateGroups').and.callThrough(); vm.fetchAllGroups(); + expect(vm.isLoading).toBe(true); expect(vm.fetchGroups).toHaveBeenCalled(); setTimeout(() => { @@ -168,6 +171,7 @@ describe('AppComponent', () => { spyOn(vm, 'updateGroups').and.callThrough(); vm.fetchAllGroups(); + expect(vm.fetchGroups).toHaveBeenCalledWith({ page: null, filterGroupsBy: null, @@ -191,6 +195,7 @@ describe('AppComponent', () => { spyOn($, 'scrollTo'); vm.fetchPage(2, null, null, true); + expect(vm.isLoading).toBe(true); expect(vm.fetchGroups).toHaveBeenCalledWith({ page: 2, @@ -210,6 +215,7 @@ describe('AppComponent', () => { jasmine.any(String), jasmine.any(String), ); + expect(vm.updateGroups).toHaveBeenCalled(); done(); }, 0); @@ -230,6 +236,7 @@ describe('AppComponent', () => { spyOn(vm.store, 'setGroupChildren'); vm.toggleChildren(groupItem); + expect(groupItem.isChildrenLoading).toBe(true); expect(vm.fetchGroups).toHaveBeenCalledWith({ parentId: groupItem.id, @@ -245,6 +252,7 @@ describe('AppComponent', () => { groupItem.children = mockRawChildren; vm.toggleChildren(groupItem); + expect(vm.fetchGroups).not.toHaveBeenCalled(); expect(groupItem.isOpen).toBe(true); }); @@ -254,6 +262,7 @@ describe('AppComponent', () => { groupItem.isOpen = true; vm.toggleChildren(groupItem); + expect(vm.fetchGroups).not.toHaveBeenCalled(); expect(groupItem.isOpen).toBe(false); }); @@ -262,6 +271,7 @@ describe('AppComponent', () => { spyOn(vm, 'fetchGroups').and.returnValue(returnServicePromise({}, true)); vm.toggleChildren(groupItem); + expect(groupItem.isChildrenLoading).toBe(true); setTimeout(() => { expect(groupItem.isChildrenLoading).toBe(false); @@ -273,18 +283,22 @@ describe('AppComponent', () => { describe('showLeaveGroupModal', () => { it('caches candidate group (as props) which is to be left', () => { const group = Object.assign({}, mockParentGroupItem); + expect(vm.targetGroup).toBe(null); expect(vm.targetParentGroup).toBe(null); vm.showLeaveGroupModal(group, mockParentGroupItem); + expect(vm.targetGroup).not.toBe(null); expect(vm.targetParentGroup).not.toBe(null); }); it('updates props which show modal confirmation dialog', () => { const group = Object.assign({}, mockParentGroupItem); + expect(vm.showModal).toBe(false); expect(vm.groupLeaveConfirmationMessage).toBe(''); vm.showLeaveGroupModal(group, mockParentGroupItem); + expect(vm.showModal).toBe(true); expect(vm.groupLeaveConfirmationMessage).toBe( `Are you sure you want to leave the "${group.fullName}" group?`, @@ -296,8 +310,10 @@ describe('AppComponent', () => { it('hides modal confirmation which is shown before leaving the group', () => { const group = Object.assign({}, mockParentGroupItem); vm.showLeaveGroupModal(group, mockParentGroupItem); + expect(vm.showModal).toBe(true); vm.hideLeaveGroupModal(); + expect(vm.showModal).toBe(false); }); }); @@ -323,6 +339,7 @@ describe('AppComponent', () => { spyOn($, 'scrollTo'); vm.leaveGroup(); + expect(vm.showModal).toBe(false); expect(vm.targetGroup.isBeingRemoved).toBe(true); expect(vm.service.leaveGroup).toHaveBeenCalledWith(vm.targetGroup.leavePath); @@ -343,6 +360,7 @@ describe('AppComponent', () => { spyOn(window, 'Flash'); vm.leaveGroup(); + expect(vm.targetGroup.isBeingRemoved).toBe(true); expect(vm.service.leaveGroup).toHaveBeenCalledWith(childGroupItem.leavePath); setTimeout(() => { @@ -362,6 +380,7 @@ describe('AppComponent', () => { spyOn(window, 'Flash'); vm.leaveGroup(childGroupItem, groupItem); + expect(vm.targetGroup.isBeingRemoved).toBe(true); expect(vm.service.leaveGroup).toHaveBeenCalledWith(childGroupItem.leavePath); setTimeout(() => { @@ -378,6 +397,7 @@ describe('AppComponent', () => { spyOn(vm.store, 'setPaginationInfo'); vm.updatePagination(mockRawPageInfo); + expect(vm.store.setPaginationInfo).toHaveBeenCalledWith(mockRawPageInfo); }); }); @@ -387,6 +407,7 @@ describe('AppComponent', () => { spyOn(vm.store, 'setGroups'); vm.updateGroups(mockGroups); + expect(vm.store.setGroups).toHaveBeenCalledWith(mockGroups); }); @@ -394,14 +415,17 @@ describe('AppComponent', () => { spyOn(vm.store, 'setSearchedGroups'); vm.updateGroups(mockGroups, true); + expect(vm.store.setSearchedGroups).toHaveBeenCalledWith(mockGroups); }); it('should set `isSearchEmpty` prop based on groups count', () => { vm.updateGroups(mockGroups); + expect(vm.isSearchEmpty).toBe(false); vm.updateGroups([]); + expect(vm.isSearchEmpty).toBe(true); }); }); @@ -497,6 +521,7 @@ describe('AppComponent', () => { vm.showModal = true; Vue.nextTick(() => { const modalDialogEl = vm.$el.querySelector('.modal'); + expect(modalDialogEl).not.toBe(null); expect(modalDialogEl.querySelector('.modal-title').innerText.trim()).toBe('Are you sure?'); expect(modalDialogEl.querySelector('.btn.btn-warning').innerText.trim()).toBe('Leave'); diff --git a/spec/javascripts/groups/components/group_folder_spec.js b/spec/javascripts/groups/components/group_folder_spec.js index 4eb198595fb..fdfd1b82bd8 100644 --- a/spec/javascripts/groups/components/group_folder_spec.js +++ b/spec/javascripts/groups/components/group_folder_spec.js @@ -18,7 +18,7 @@ const createComponent = (groups = mockGroups, parentGroup = mockParentGroupItem) describe('GroupFolderComponent', () => { let vm; - beforeEach((done) => { + beforeEach(done => { Vue.component('group-item', groupItemComponent); vm = createComponent(); @@ -59,6 +59,7 @@ describe('GroupFolderComponent', () => { const newVm = createComponent(mockGroups, parentGroup); newVm.$mount(); + expect(newVm.$el.querySelector('li.group-row a.has-more-items')).toBeDefined(); newVm.$destroy(); }); diff --git a/spec/javascripts/groups/components/group_item_spec.js b/spec/javascripts/groups/components/group_item_spec.js index 49d4f7efd72..4d6d0c895b6 100644 --- a/spec/javascripts/groups/components/group_item_spec.js +++ b/spec/javascripts/groups/components/group_item_spec.js @@ -17,7 +17,7 @@ const createComponent = (group = mockParentGroupItem, parentGroup = mockChildren describe('GroupItemComponent', () => { let vm; - beforeEach((done) => { + beforeEach(done => { Vue.component('group-folder', groupFolderComponent); vm = createComponent(); @@ -44,7 +44,7 @@ describe('GroupItemComponent', () => { const { rowClass } = vm; expect(Object.keys(rowClass).length).toBe(classes.length); - Object.keys(rowClass).forEach((className) => { + Object.keys(rowClass).forEach(className => { expect(classes.indexOf(className)).toBeGreaterThan(-1); }); }); @@ -57,11 +57,13 @@ describe('GroupItemComponent', () => { group.childrenCount = 5; newVm = createComponent(group); + expect(newVm.hasChildren).toBeTruthy(); newVm.$destroy(); group.childrenCount = 0; newVm = createComponent(group); + expect(newVm.hasChildren).toBeFalsy(); newVm.$destroy(); }); @@ -74,11 +76,13 @@ describe('GroupItemComponent', () => { group.avatarUrl = null; newVm = createComponent(group); + expect(newVm.hasAvatar).toBeFalsy(); newVm.$destroy(); group.avatarUrl = '/uploads/group_avatar.png'; newVm = createComponent(group); + expect(newVm.hasAvatar).toBeTruthy(); newVm.$destroy(); }); @@ -91,11 +95,13 @@ describe('GroupItemComponent', () => { group.type = 'group'; newVm = createComponent(group); + expect(newVm.isGroup).toBeTruthy(); newVm.$destroy(); group.type = 'project'; newVm = createComponent(group); + expect(newVm.isGroup).toBeFalsy(); newVm.$destroy(); }); @@ -127,10 +133,11 @@ describe('GroupItemComponent', () => { spyOn(eventHub, '$emit'); vm.onClickRowGroup(event); + expect(eventHub.$emit).toHaveBeenCalledWith('toggleChildren', vm.group); }); - it('should navigate page to group homepage if group does not have any children present', (done) => { + it('should navigate page to group homepage if group does not have any children present', done => { const group = Object.assign({}, mockParentGroupItem); group.childrenCount = 0; const newVm = createComponent(group); diff --git a/spec/javascripts/groups/components/groups_spec.js b/spec/javascripts/groups/components/groups_spec.js index 5af86b55532..6ba4fe23a69 100644 --- a/spec/javascripts/groups/components/groups_spec.js +++ b/spec/javascripts/groups/components/groups_spec.js @@ -21,7 +21,7 @@ const createComponent = (searchEmpty = false) => { describe('GroupsComponent', () => { let vm; - beforeEach((done) => { + beforeEach(done => { Vue.component('group-folder', groupFolderComponent); Vue.component('group-item', groupItemComponent); @@ -42,13 +42,20 @@ describe('GroupsComponent', () => { spyOn(eventHub, '$emit').and.stub(); vm.change(2); - expect(eventHub.$emit).toHaveBeenCalledWith('fetchPage', 2, jasmine.any(Object), jasmine.any(Object), jasmine.any(Object)); + + expect(eventHub.$emit).toHaveBeenCalledWith( + 'fetchPage', + 2, + jasmine.any(Object), + jasmine.any(Object), + jasmine.any(Object), + ); }); }); }); describe('template', () => { - it('should render component template correctly', (done) => { + it('should render component template correctly', done => { Vue.nextTick(() => { expect(vm.$el.querySelector('.groups-list-tree-container')).toBeDefined(); expect(vm.$el.querySelector('.group-list-tree')).toBeDefined(); @@ -58,7 +65,7 @@ describe('GroupsComponent', () => { }); }); - it('should render empty search message when `searchEmpty` is `true`', (done) => { + it('should render empty search message when `searchEmpty` is `true`', done => { vm.searchEmpty = true; Vue.nextTick(() => { expect(vm.$el.querySelector('.has-no-search-results')).toBeDefined(); diff --git a/spec/javascripts/groups/components/item_actions_spec.js b/spec/javascripts/groups/components/item_actions_spec.js index 15fd37ebcd2..3f66e7fd6f2 100644 --- a/spec/javascripts/groups/components/item_actions_spec.js +++ b/spec/javascripts/groups/components/item_actions_spec.js @@ -30,7 +30,12 @@ describe('ItemActionsComponent', () => { it('emits `showLeaveGroupModal` event with `group` and `parentGroup` props', () => { spyOn(eventHub, '$emit'); vm.onLeaveGroup(); - expect(eventHub.$emit).toHaveBeenCalledWith('showLeaveGroupModal', vm.group, vm.parentGroup); + + expect(eventHub.$emit).toHaveBeenCalledWith( + 'showLeaveGroupModal', + vm.group, + vm.parentGroup, + ); }); }); }); @@ -46,6 +51,7 @@ describe('ItemActionsComponent', () => { const newVm = createComponent(group); const editBtn = newVm.$el.querySelector('a.edit-group'); + expect(editBtn).toBeDefined(); expect(editBtn.classList.contains('no-expand')).toBeTruthy(); expect(editBtn.getAttribute('href')).toBe(group.editPath); @@ -63,6 +69,7 @@ describe('ItemActionsComponent', () => { const newVm = createComponent(group); const leaveBtn = newVm.$el.querySelector('a.leave-group'); + expect(leaveBtn).toBeDefined(); expect(leaveBtn.classList.contains('no-expand')).toBeTruthy(); expect(leaveBtn.getAttribute('href')).toBe(group.leavePath); diff --git a/spec/javascripts/groups/components/item_caret_spec.js b/spec/javascripts/groups/components/item_caret_spec.js index 36f838a104f..6e430dbcdb2 100644 --- a/spec/javascripts/groups/components/item_caret_spec.js +++ b/spec/javascripts/groups/components/item_caret_spec.js @@ -16,6 +16,7 @@ describe('ItemCaretComponent', () => { describe('template', () => { it('should render component template correctly', () => { const vm = createComponent(); + expect(vm.$el.classList.contains('folder-caret')).toBeTruthy(); expect(vm.$el.querySelectorAll('svg').length).toBe(1); vm.$destroy(); @@ -23,12 +24,14 @@ describe('ItemCaretComponent', () => { it('should render caret down icon if `isGroupOpen` prop is `true`', () => { const vm = createComponent(true); + expect(vm.$el.querySelector('svg use').getAttribute('xlink:href')).toContain('angle-down'); vm.$destroy(); }); it('should render caret right icon if `isGroupOpen` prop is `false`', () => { const vm = createComponent(); + expect(vm.$el.querySelector('svg use').getAttribute('xlink:href')).toContain('angle-right'); vm.$destroy(); }); diff --git a/spec/javascripts/groups/components/item_stats_spec.js b/spec/javascripts/groups/components/item_stats_spec.js index e6a57495eb1..00d6a4817d7 100644 --- a/spec/javascripts/groups/components/item_stats_spec.js +++ b/spec/javascripts/groups/components/item_stats_spec.js @@ -22,9 +22,10 @@ describe('ItemStatsComponent', () => { describe('computed', () => { describe('visibilityIcon', () => { it('should return icon class based on `item.visibility` value', () => { - Object.keys(VISIBILITY_TYPE_ICON).forEach((visibility) => { + Object.keys(VISIBILITY_TYPE_ICON).forEach(visibility => { const item = Object.assign({}, mockParentGroupItem, { visibility }); const vm = createComponent(item); + expect(vm.visibilityIcon).toBe(VISIBILITY_TYPE_ICON[visibility]); vm.$destroy(); }); @@ -33,24 +34,26 @@ describe('ItemStatsComponent', () => { describe('visibilityTooltip', () => { it('should return tooltip string for Group based on `item.visibility` value', () => { - Object.keys(GROUP_VISIBILITY_TYPE).forEach((visibility) => { + Object.keys(GROUP_VISIBILITY_TYPE).forEach(visibility => { const item = Object.assign({}, mockParentGroupItem, { visibility, type: ITEM_TYPE.GROUP, }); const vm = createComponent(item); + expect(vm.visibilityTooltip).toBe(GROUP_VISIBILITY_TYPE[visibility]); vm.$destroy(); }); }); it('should return tooltip string for Project based on `item.visibility` value', () => { - Object.keys(PROJECT_VISIBILITY_TYPE).forEach((visibility) => { + Object.keys(PROJECT_VISIBILITY_TYPE).forEach(visibility => { const item = Object.assign({}, mockParentGroupItem, { visibility, type: ITEM_TYPE.PROJECT, }); const vm = createComponent(item); + expect(vm.visibilityTooltip).toBe(PROJECT_VISIBILITY_TYPE[visibility]); vm.$destroy(); }); @@ -64,11 +67,13 @@ describe('ItemStatsComponent', () => { item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.PROJECT }); vm = createComponent(item); + expect(vm.isProject).toBeTruthy(); vm.$destroy(); item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.GROUP }); vm = createComponent(item); + expect(vm.isProject).toBeFalsy(); vm.$destroy(); }); @@ -81,11 +86,13 @@ describe('ItemStatsComponent', () => { item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.GROUP }); vm = createComponent(item); + expect(vm.isGroup).toBeTruthy(); vm.$destroy(); item = Object.assign({}, mockParentGroupItem, { type: ITEM_TYPE.PROJECT }); vm = createComponent(item); + expect(vm.isGroup).toBeFalsy(); vm.$destroy(); }); @@ -105,6 +112,7 @@ describe('ItemStatsComponent', () => { const vm = createComponent(); const visibilityIconEl = vm.$el.querySelector('.item-visibility'); + expect(visibilityIconEl).not.toBe(null); expect(visibilityIconEl.dataset.originalTitle).toBe(vm.visibilityTooltip); expect(visibilityIconEl.querySelectorAll('svg').length).toBeGreaterThan(0); @@ -120,6 +128,7 @@ describe('ItemStatsComponent', () => { const vm = createComponent(item); const projectStarIconEl = vm.$el.querySelector('.project-stars'); + expect(projectStarIconEl).not.toBeNull(); expect(projectStarIconEl.querySelectorAll('svg').length).toBeGreaterThan(0); expect(projectStarIconEl.querySelectorAll('.stat-value').length).toBeGreaterThan(0); diff --git a/spec/javascripts/groups/components/item_stats_value_spec.js b/spec/javascripts/groups/components/item_stats_value_spec.js index 2a995e8efe6..ea8edcf49cd 100644 --- a/spec/javascripts/groups/components/item_stats_value_spec.js +++ b/spec/javascripts/groups/components/item_stats_value_spec.js @@ -29,11 +29,13 @@ describe('ItemStatsValueComponent', () => { describe('isValuePresent', () => { it('returns true if non-empty `value` is present', () => { vm = createComponent(Object.assign({}, itemConfig, { value: 10 })); + expect(vm.isValuePresent).toBeTruthy(); }); it('returns false if empty `value` is present', () => { vm = createComponent(itemConfig); + expect(vm.isValuePresent).toBeFalsy(); }); diff --git a/spec/javascripts/groups/components/item_type_icon_spec.js b/spec/javascripts/groups/components/item_type_icon_spec.js index 24380689b29..73108512222 100644 --- a/spec/javascripts/groups/components/item_type_icon_spec.js +++ b/spec/javascripts/groups/components/item_type_icon_spec.js @@ -18,6 +18,7 @@ describe('ItemTypeIconComponent', () => { it('should render component template correctly', () => { const vm = createComponent(); vm.$mount(); + expect(vm.$el.classList.contains('item-type-icon')).toBeTruthy(); vm.$destroy(); }); @@ -27,11 +28,13 @@ describe('ItemTypeIconComponent', () => { vm = createComponent(ITEM_TYPE.GROUP, true); vm.$mount(); + expect(vm.$el.querySelector('use').getAttribute('xlink:href')).toContain('folder-open'); vm.$destroy(); vm = createComponent(ITEM_TYPE.GROUP); vm.$mount(); + expect(vm.$el.querySelector('use').getAttribute('xlink:href')).toContain('folder'); vm.$destroy(); }); @@ -41,11 +44,13 @@ describe('ItemTypeIconComponent', () => { vm = createComponent(ITEM_TYPE.PROJECT); vm.$mount(); + expect(vm.$el.querySelector('use').getAttribute('xlink:href')).toContain('bookmark'); vm.$destroy(); vm = createComponent(ITEM_TYPE.GROUP); vm.$mount(); + expect(vm.$el.querySelector('use').getAttribute('xlink:href')).not.toContain('bookmark'); vm.$destroy(); }); diff --git a/spec/javascripts/groups/mock_data.js b/spec/javascripts/groups/mock_data.js index 8bf6417487d..2fdc844f3d9 100644 --- a/spec/javascripts/groups/mock_data.js +++ b/spec/javascripts/groups/mock_data.js @@ -340,7 +340,8 @@ export const mockSearchedGroups = [ { id: 17, name: 'v4.4', - description: 'Voluptatem qui ea error aperiam veritatis doloremque consequatur temporibus.', + description: + 'Voluptatem qui ea error aperiam veritatis doloremque consequatur temporibus.', visibility: 'public', full_name: 'platform / hardware / bsp / kernel / common / v4.4', relative_path: '/platform/hardware/bsp/kernel/common/v4.4', diff --git a/spec/javascripts/groups/service/groups_service_spec.js b/spec/javascripts/groups/service/groups_service_spec.js index 20bb63687f7..339e5131615 100644 --- a/spec/javascripts/groups/service/groups_service_spec.js +++ b/spec/javascripts/groups/service/groups_service_spec.js @@ -24,9 +24,11 @@ describe('GroupsService', () => { }; service.getGroups(55, 2, 'git', 'created_asc', true); + expect(service.groups.get).toHaveBeenCalledWith({ parent_id: 55 }); service.getGroups(null, 2, 'git', 'created_asc', true); + expect(service.groups.get).toHaveBeenCalledWith(queryParams); }); }); @@ -36,6 +38,7 @@ describe('GroupsService', () => { spyOn(Vue.http, 'delete').and.stub(); service.leaveGroup(mockParentGroupItem.leavePath); + expect(Vue.http.delete).toHaveBeenCalledWith(mockParentGroupItem.leavePath); }); }); diff --git a/spec/javascripts/groups/store/groups_store_spec.js b/spec/javascripts/groups/store/groups_store_spec.js index 78caf8f80bf..38de4b89f31 100644 --- a/spec/javascripts/groups/store/groups_store_spec.js +++ b/spec/javascripts/groups/store/groups_store_spec.js @@ -1,7 +1,9 @@ import GroupsStore from '~/groups/store/groups_store'; import { - mockGroups, mockSearchedGroups, - mockParentGroupItem, mockRawChildren, + mockGroups, + mockSearchedGroups, + mockParentGroupItem, + mockRawChildren, mockRawPageInfo, } from '../mock_data'; @@ -11,12 +13,14 @@ describe('ProjectsStore', () => { let store; store = new GroupsStore(); + expect(Object.keys(store.state).length).toBe(2); expect(Array.isArray(store.state.groups)).toBeTruthy(); expect(Object.keys(store.state.pageInfo).length).toBe(0); expect(store.hideProjects).not.toBeDefined(); store = new GroupsStore(true); + expect(store.hideProjects).toBeTruthy(); }); }); @@ -27,6 +31,7 @@ describe('ProjectsStore', () => { spyOn(store, 'formatGroupItem').and.callThrough(); store.setGroups(mockGroups); + expect(store.state.groups.length).toBe(mockGroups.length); expect(store.formatGroupItem).toHaveBeenCalledWith(jasmine.any(Object)); expect(Object.keys(store.state.groups[0]).indexOf('fullName')).toBeGreaterThan(-1); @@ -39,10 +44,13 @@ describe('ProjectsStore', () => { spyOn(store, 'formatGroupItem').and.callThrough(); store.setSearchedGroups(mockSearchedGroups); + expect(store.state.groups.length).toBe(mockSearchedGroups.length); expect(store.formatGroupItem).toHaveBeenCalledWith(jasmine.any(Object)); expect(Object.keys(store.state.groups[0]).indexOf('fullName')).toBeGreaterThan(-1); - expect(Object.keys(store.state.groups[0].children[0]).indexOf('fullName')).toBeGreaterThan(-1); + expect(Object.keys(store.state.groups[0].children[0]).indexOf('fullName')).toBeGreaterThan( + -1, + ); }); }); @@ -52,6 +60,7 @@ describe('ProjectsStore', () => { spyOn(store, 'formatGroupItem').and.callThrough(); store.setGroupChildren(mockParentGroupItem, mockRawChildren); + expect(store.formatGroupItem).toHaveBeenCalledWith(jasmine.any(Object)); expect(mockParentGroupItem.children.length).toBe(1); expect(Object.keys(mockParentGroupItem.children[0]).indexOf('fullName')).toBeGreaterThan(-1); @@ -65,6 +74,7 @@ describe('ProjectsStore', () => { const store = new GroupsStore(); store.setPaginationInfo(mockRawPageInfo); + expect(store.state.pageInfo.perPage).toBe(10); expect(store.state.pageInfo.page).toBe(10); expect(store.state.pageInfo.total).toBe(10); @@ -81,6 +91,7 @@ describe('ProjectsStore', () => { store = new GroupsStore(); updatedGroupItem = store.formatGroupItem(mockRawChildren[0]); + expect(Object.keys(updatedGroupItem).indexOf('fullName')).toBeGreaterThan(-1); expect(updatedGroupItem.childrenCount).toBe(mockRawChildren[0].children_count); expect(updatedGroupItem.isChildrenLoading).toBe(false); @@ -88,6 +99,7 @@ describe('ProjectsStore', () => { store = new GroupsStore(true); updatedGroupItem = store.formatGroupItem(mockRawChildren[0]); + expect(Object.keys(updatedGroupItem).indexOf('fullName')).toBeGreaterThan(-1); expect(updatedGroupItem.childrenCount).toBe(mockRawChildren[0].subgroup_count); }); @@ -104,6 +116,7 @@ describe('ProjectsStore', () => { const childItem = store.state.groups[0].children[0]; store.removeGroup(childItem, store.state.groups[0]); + expect(store.state.groups[0].children.length).toBe(0); }); }); diff --git a/spec/javascripts/header_spec.js b/spec/javascripts/header_spec.js index 16ac438f7ac..2fe34e5a76f 100644 --- a/spec/javascripts/header_spec.js +++ b/spec/javascripts/header_spec.js @@ -1,7 +1,7 @@ import $ from 'jquery'; import initTodoToggle from '~/header'; -describe('Header', function () { +describe('Header', function() { const todosPendingCount = '.todos-count'; const fixtureTemplate = 'issues/open-issue.html.raw'; @@ -21,16 +21,19 @@ describe('Header', function () { it('should update todos-count after receiving the todo:toggle event', () => { triggerToggle('5'); + expect($(todosPendingCount).text()).toEqual('5'); }); it('should hide todos-count when it is 0', () => { triggerToggle('0'); + expect(isTodosCountHidden()).toEqual(true); }); it('should show todos-count when it is more than 0', () => { triggerToggle('10'); + expect(isTodosCountHidden()).toEqual(false); }); diff --git a/spec/javascripts/helpers/class_spec_helper_spec.js b/spec/javascripts/helpers/class_spec_helper_spec.js index fa104ae5bcd..f6268b0fb6d 100644 --- a/spec/javascripts/helpers/class_spec_helper_spec.js +++ b/spec/javascripts/helpers/class_spec_helper_spec.js @@ -2,11 +2,13 @@ import './class_spec_helper'; -describe('ClassSpecHelper', function () { +describe('ClassSpecHelper', function() { describe('itShouldBeAStaticMethod', () => { beforeEach(() => { class TestClass { - instanceMethod() { this.prop = 'val'; } + instanceMethod() { + this.prop = 'val'; + } static staticMethod() {} } diff --git a/spec/javascripts/helpers/locale_helper.js b/spec/javascripts/helpers/locale_helper.js index 99e6ce61234..80047b06003 100644 --- a/spec/javascripts/helpers/locale_helper.js +++ b/spec/javascripts/helpers/locale_helper.js @@ -1,6 +1,6 @@ /* eslint-disable import/prefer-default-export */ -export const setLanguage = (languageCode) => { +export const setLanguage = languageCode => { const htmlElement = document.querySelector('html'); if (languageCode) { diff --git a/spec/javascripts/helpers/set_timeout_promise_helper.js b/spec/javascripts/helpers/set_timeout_promise_helper.js index 1478073413c..47087619187 100644 --- a/spec/javascripts/helpers/set_timeout_promise_helper.js +++ b/spec/javascripts/helpers/set_timeout_promise_helper.js @@ -1,3 +1,4 @@ -export default (time = 0) => new Promise((resolve) => { - setTimeout(resolve, time); -}); +export default (time = 0) => + new Promise(resolve => { + setTimeout(resolve, time); + }); diff --git a/spec/javascripts/helpers/user_mock_data_helper.js b/spec/javascripts/helpers/user_mock_data_helper.js index f6c3ce5aecc..6999fa1f8a1 100644 --- a/spec/javascripts/helpers/user_mock_data_helper.js +++ b/spec/javascripts/helpers/user_mock_data_helper.js @@ -2,14 +2,12 @@ export default { createNumberRandomUsers(numberUsers) { const users = []; for (let i = 0; i < numberUsers; i += 1) { - users.push( - { - avatar: 'https://gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - id: (i + 1), - name: `GitLab User ${i}`, - username: `gitlab${i}`, - }, - ); + users.push({ + avatar: 'https://gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + id: i + 1, + name: `GitLab User ${i}`, + username: `gitlab${i}`, + }); } return users; }, diff --git a/spec/javascripts/helpers/vue_resource_helper.js b/spec/javascripts/helpers/vue_resource_helper.js index 0d1bf5e2e80..0f58af09933 100644 --- a/spec/javascripts/helpers/vue_resource_helper.js +++ b/spec/javascripts/helpers/vue_resource_helper.js @@ -1,6 +1,6 @@ // eslint-disable-next-line import/prefer-default-export export const headersInterceptor = (request, next) => { - next((response) => { + next(response => { const headers = {}; response.headers.forEach((value, key) => { headers[key] = value; diff --git a/spec/javascripts/ide/components/branches/item_spec.js b/spec/javascripts/ide/components/branches/item_spec.js index 8b756c8f168..36b6736bfd4 100644 --- a/spec/javascripts/ide/components/branches/item_spec.js +++ b/spec/javascripts/ide/components/branches/item_spec.js @@ -29,13 +29,16 @@ describe('IDE branch item', () => { it('renders branch name and timeago', () => { const timeText = getTimeago().format(TEST_BRANCH.committedDate); + expect(vm.$el).toContainText(TEST_BRANCH.name); expect(vm.$el.querySelector('time')).toHaveText(timeText); expect(vm.$el.querySelector('.ic-mobile-issue-close')).toBe(null); }); it('renders link to branch', () => { - const expectedHref = router.resolve(`/project/${TEST_PROJECT_ID}/edit/${TEST_BRANCH.name}`).href; + const expectedHref = router.resolve(`/project/${TEST_PROJECT_ID}/edit/${TEST_BRANCH.name}`) + .href; + expect(vm.$el).toMatch('a'); expect(vm.$el).toHaveAttr('href', expectedHref); }); diff --git a/spec/javascripts/ide/components/branches/search_list_spec.js b/spec/javascripts/ide/components/branches/search_list_spec.js index c3f84ba1c24..72a3c2d5dcd 100644 --- a/spec/javascripts/ide/components/branches/search_list_spec.js +++ b/spec/javascripts/ide/components/branches/search_list_spec.js @@ -62,8 +62,9 @@ describe('IDE branches search list', () => { }); it('renders list', () => { - const elementText = Array.from(vm.$el.querySelectorAll('li strong')) - .map(x => x.textContent.trim()); + const elementText = Array.from(vm.$el.querySelectorAll('li strong')).map(x => + x.textContent.trim(), + ); expect(elementText).toEqual(testBranches.map(x => x.name)); }); diff --git a/spec/javascripts/ide/components/commit_sidebar/message_field_spec.js b/spec/javascripts/ide/components/commit_sidebar/message_field_spec.js index 942cc19f46d..af67991eadd 100644 --- a/spec/javascripts/ide/components/commit_sidebar/message_field_spec.js +++ b/spec/javascripts/ide/components/commit_sidebar/message_field_spec.js @@ -36,8 +36,7 @@ describe('IDE commit message field', () => { it('removed is-focused class on blur', done => { vm.$el.querySelector('textarea').focus(); - vm - .$nextTick() + vm.$nextTick() .then(() => { expect(vm.$el.querySelector('.is-focused')).not.toBeNull(); @@ -70,12 +69,12 @@ describe('IDE commit message field', () => { it('does not highlight less than 50 characters', done => { vm.text = 'text less than 50 chars'; - vm - .$nextTick() + vm.$nextTick() .then(() => { expect(vm.$el.querySelector('.highlights span').textContent).toContain( 'text less than 50 chars', ); + expect(vm.$el.querySelector('mark').style.display).toBe('none'); }) .then(done) @@ -86,12 +85,12 @@ describe('IDE commit message field', () => { vm.text = 'text less than 50 chars that should not highlighted. text more than 50 should be highlighted'; - vm - .$nextTick() + vm.$nextTick() .then(() => { expect(vm.$el.querySelector('.highlights span').textContent).toContain( 'text less than 50 chars that should not highlighte', ); + expect(vm.$el.querySelector('mark').style.display).not.toBe('none'); expect(vm.$el.querySelector('mark').textContent).toBe( 'd. text more than 50 should be highlighted', @@ -106,8 +105,7 @@ describe('IDE commit message field', () => { it('does not highlight body text less tan 72 characters', done => { vm.text = 'subject line\nbody content'; - vm - .$nextTick() + vm.$nextTick() .then(() => { expect(vm.$el.querySelectorAll('.highlights span').length).toBe(2); expect(vm.$el.querySelectorAll('mark')[1].style.display).toBe('none'); @@ -120,8 +118,7 @@ describe('IDE commit message field', () => { vm.text = 'subject line\nbody content that will be highlighted when it is more than 72 characters in length'; - vm - .$nextTick() + vm.$nextTick() .then(() => { expect(vm.$el.querySelectorAll('.highlights span').length).toBe(2); expect(vm.$el.querySelectorAll('mark')[1].style.display).not.toBe('none'); @@ -135,8 +132,7 @@ describe('IDE commit message field', () => { vm.text = 'text less than 50 chars that should not highlighted\nbody content that will be highlighted when it is more than 72 characters in length'; - vm - .$nextTick() + vm.$nextTick() .then(() => { expect(vm.$el.querySelectorAll('.highlights span').length).toBe(2); expect(vm.$el.querySelectorAll('mark').length).toBe(2); @@ -154,8 +150,7 @@ describe('IDE commit message field', () => { it('updates transform of highlights', done => { vm.text = 'subject line\n\n\n\n\n\n\n\n\n\n\nbody content'; - vm - .$nextTick() + vm.$nextTick() .then(() => { vm.$el.querySelector('textarea').scrollTo(0, 50); diff --git a/spec/javascripts/ide/components/file_finder/index_spec.js b/spec/javascripts/ide/components/file_finder/index_spec.js index 4f208e946d2..4d934f92f72 100644 --- a/spec/javascripts/ide/components/file_finder/index_spec.js +++ b/spec/javascripts/ide/components/file_finder/index_spec.js @@ -85,8 +85,7 @@ describe('IDE File finder item spec', () => { it('clear button resets searchText', done => { vm.searchText = 'index'; - vm - .$nextTick() + vm.$nextTick() .then(() => { vm.$el.querySelector('.dropdown-input-clear').click(); }) @@ -102,8 +101,7 @@ describe('IDE File finder item spec', () => { spyOn(vm.$refs.searchInput, 'focus'); vm.searchText = 'index'; - vm - .$nextTick() + vm.$nextTick() .then(() => { vm.$el.querySelector('.dropdown-input-clear').click(); }) @@ -178,8 +176,7 @@ describe('IDE File finder item spec', () => { vm.searchText = 'test'; vm.$store.state.fileFindVisible = true; - vm - .$nextTick() + vm.$nextTick() .then(() => { vm.$store.state.fileFindVisible = false; }) diff --git a/spec/javascripts/ide/components/ide_spec.js b/spec/javascripts/ide/components/ide_spec.js index 49b8e934cdd..c02a1ad246c 100644 --- a/spec/javascripts/ide/components/ide_spec.js +++ b/spec/javascripts/ide/components/ide_spec.js @@ -84,8 +84,7 @@ describe('ide component', () => { it('calls toggleFileFinder on `t` key press', done => { Mousetrap.trigger('t'); - vm - .$nextTick() + vm.$nextTick() .then(() => { expect(vm.toggleFileFinder).toHaveBeenCalled(); }) @@ -96,8 +95,7 @@ describe('ide component', () => { it('calls toggleFileFinder on `command+p` key press', done => { Mousetrap.trigger('command+p'); - vm - .$nextTick() + vm.$nextTick() .then(() => { expect(vm.toggleFileFinder).toHaveBeenCalled(); }) @@ -108,8 +106,7 @@ describe('ide component', () => { it('calls toggleFileFinder on `ctrl+p` key press', done => { Mousetrap.trigger('ctrl+p'); - vm - .$nextTick() + vm.$nextTick() .then(() => { expect(vm.toggleFileFinder).toHaveBeenCalled(); }) diff --git a/spec/javascripts/ide/components/ide_status_bar_spec.js b/spec/javascripts/ide/components/ide_status_bar_spec.js index 47d6492a7a6..ab032b4cb98 100644 --- a/spec/javascripts/ide/components/ide_status_bar_spec.js +++ b/spec/javascripts/ide/components/ide_status_bar_spec.js @@ -50,9 +50,11 @@ describe('ideStatusBar', () => { expect(vm.commitAgeUpdate).not.toHaveBeenCalled(); jasmine.clock().tick(1100); + expect(vm.commitAgeUpdate.calls.count()).toEqual(1); jasmine.clock().tick(1000); + expect(vm.commitAgeUpdate.calls.count()).toEqual(2); }); }); @@ -76,8 +78,7 @@ describe('ideStatusBar', () => { }, }); - vm - .$nextTick() + vm.$nextTick() .then(() => { vm.$el.querySelector('.ide-status-pipeline button').click(); diff --git a/spec/javascripts/ide/components/jobs/detail_spec.js b/spec/javascripts/ide/components/jobs/detail_spec.js index 8f8d4b9709e..a4e6b81acba 100644 --- a/spec/javascripts/ide/components/jobs/detail_spec.js +++ b/spec/javascripts/ide/components/jobs/detail_spec.js @@ -109,8 +109,7 @@ describe('IDE jobs detail view', () => { vm.scrollPos = 1; - vm - .$nextTick() + vm.$nextTick() .then(() => vm.$el.querySelector('.btn-scroll').click()) .then(() => vm.$nextTick()) .then(() => { diff --git a/spec/javascripts/ide/components/merge_requests/item_spec.js b/spec/javascripts/ide/components/merge_requests/item_spec.js index 750948cae3c..155a247defb 100644 --- a/spec/javascripts/ide/components/merge_requests/item_spec.js +++ b/spec/javascripts/ide/components/merge_requests/item_spec.js @@ -29,7 +29,10 @@ describe('IDE merge request item', () => { }); it('renders link with href', () => { - const expectedHref = router.resolve(`/project/${vm.item.projectPathWithNamespace}/merge_requests/${vm.item.iid}`).href; + const expectedHref = router.resolve( + `/project/${vm.item.projectPathWithNamespace}/merge_requests/${vm.item.iid}`, + ).href; + expect(vm.$el).toMatch('a'); expect(vm.$el).toHaveAttr('href', expectedHref); }); diff --git a/spec/javascripts/ide/components/merge_requests/list_spec.js b/spec/javascripts/ide/components/merge_requests/list_spec.js index c761315444c..55e4f46d9ca 100644 --- a/spec/javascripts/ide/components/merge_requests/list_spec.js +++ b/spec/javascripts/ide/components/merge_requests/list_spec.js @@ -118,8 +118,9 @@ describe('IDE merge requests list', () => { vm.$nextTick() .then(() => { const expectedSearchTypes = vm.$options.searchTypes.map(x => x.label); - const renderedSearchTypes = Array.from(vm.$el.querySelectorAll('li')) - .map(x => x.textContent.trim()); + const renderedSearchTypes = Array.from(vm.$el.querySelectorAll('li')).map(x => + x.textContent.trim(), + ); expect(renderedSearchTypes).toEqual(expectedSearchTypes); }) diff --git a/spec/javascripts/ide/components/new_dropdown/index_spec.js b/spec/javascripts/ide/components/new_dropdown/index_spec.js index 8a8cbd2cee4..83e530f0a6a 100644 --- a/spec/javascripts/ide/components/new_dropdown/index_spec.js +++ b/spec/javascripts/ide/components/new_dropdown/index_spec.js @@ -36,6 +36,7 @@ describe('new dropdown component', () => { it('renders new file, upload and new directory links', () => { const buttons = vm.$el.querySelectorAll('.dropdown-menu button'); + expect(buttons[0].textContent.trim()).toBe('New file'); expect(buttons[1].textContent.trim()).toBe('Upload file'); expect(buttons[2].textContent.trim()).toBe('New directory'); diff --git a/spec/javascripts/ide/components/new_dropdown/upload_spec.js b/spec/javascripts/ide/components/new_dropdown/upload_spec.js index 70b885ede26..878e17ac805 100644 --- a/spec/javascripts/ide/components/new_dropdown/upload_spec.js +++ b/spec/javascripts/ide/components/new_dropdown/upload_spec.js @@ -40,21 +40,10 @@ describe('new dropdown upload', () => { describe('readFile', () => { beforeEach(() => { - spyOn(FileReader.prototype, 'readAsText'); spyOn(FileReader.prototype, 'readAsDataURL'); }); - it('calls readAsText for text files', () => { - const file = { - type: 'text/html', - }; - - vm.readFile(file); - - expect(FileReader.prototype.readAsText).toHaveBeenCalledWith(file); - }); - - it('calls readAsDataURL for non-text files', () => { + it('calls readAsDataURL for all files', () => { const file = { type: 'images/png', }; @@ -66,32 +55,37 @@ describe('new dropdown upload', () => { }); describe('createFile', () => { - const target = { - result: 'content', + const textTarget = { + result: 'base64,cGxhaW4gdGV4dA==', }; const binaryTarget = { - result: 'base64,base64content', + result: 'base64,w4I=', + }; + const textFile = { + name: 'textFile', + type: 'text/plain', }; - const file = { - name: 'file', + const binaryFile = { + name: 'binaryFile', + type: 'image/png', }; - it('creates new file', () => { - vm.createFile(target, file, true); + it('creates file in plain text (without encoding) if the file content is plain text', () => { + vm.createFile(textTarget, textFile); expect(vm.$emit).toHaveBeenCalledWith('create', { - name: file.name, + name: textFile.name, type: 'blob', - content: target.result, + content: 'plain text', base64: false, }); }); it('splits content on base64 if binary', () => { - vm.createFile(binaryTarget, file, false); + vm.createFile(binaryTarget, binaryFile); expect(vm.$emit).toHaveBeenCalledWith('create', { - name: file.name, + name: binaryFile.name, type: 'blob', content: binaryTarget.result.split('base64,')[1], base64: true, diff --git a/spec/javascripts/ide/components/preview/clientside_spec.js b/spec/javascripts/ide/components/preview/clientside_spec.js index d6983f5a3b8..b9bf5c51ffe 100644 --- a/spec/javascripts/ide/components/preview/clientside_spec.js +++ b/spec/javascripts/ide/components/preview/clientside_spec.js @@ -59,8 +59,7 @@ describe('IDE clientside preview', () => { }), ); - vm - .$nextTick() + vm.$nextTick() .then(() => vm.initPreview()) .then(vm.$nextTick) .then(done) diff --git a/spec/javascripts/ide/components/repo_editor_spec.js b/spec/javascripts/ide/components/repo_editor_spec.js index 991fb750876..002b5a005b8 100644 --- a/spec/javascripts/ide/components/repo_editor_spec.js +++ b/spec/javascripts/ide/components/repo_editor_spec.js @@ -52,6 +52,7 @@ describe('RepoEditor', () => { it('renders only an edit tab', done => { Vue.nextTick(() => { const tabs = vm.$el.querySelectorAll('.ide-mode-tabs .nav-links li'); + expect(tabs.length).toBe(1); expect(tabs[0].textContent.trim()).toBe('Edit'); @@ -72,6 +73,7 @@ describe('RepoEditor', () => { it('renders an Edit and a Preview Tab', done => { Vue.nextTick(() => { const tabs = vm.$el.querySelectorAll('.ide-mode-tabs .nav-links li'); + expect(tabs.length).toBe(2); expect(tabs[0].textContent.trim()).toBe('Edit'); expect(tabs[1].textContent.trim()).toBe('Preview Markdown'); @@ -109,6 +111,7 @@ describe('RepoEditor', () => { it('renders an Edit and a Preview Tab', done => { Vue.nextTick(() => { const tabs = vm.$el.querySelectorAll('.ide-mode-tabs .nav-links li'); + expect(tabs.length).toBe(2); expect(tabs[0].textContent.trim()).toBe('Review'); expect(tabs[1].textContent.trim()).toBe('Preview Markdown'); @@ -122,8 +125,7 @@ describe('RepoEditor', () => { vm.file.path = `${vm.file.path}.md`; vm.$store.state.entries[vm.file.path] = vm.file; - vm - .$nextTick() + vm.$nextTick() .then(() => { vm.$el.querySelectorAll('.ide-mode-tabs .nav-links a')[1].click(); }) @@ -294,8 +296,7 @@ describe('RepoEditor', () => { it('calls updateDimensions when panelResizing is false', done => { vm.$store.state.panelResizing = true; - vm - .$nextTick() + vm.$nextTick() .then(() => { vm.$store.state.panelResizing = false; }) @@ -363,8 +364,7 @@ describe('RepoEditor', () => { vm.file.pending = true; - vm - .$nextTick() + vm.$nextTick() .then(() => { vm.file = file('testing'); diff --git a/spec/javascripts/ide/components/shared/tokened_input_spec.js b/spec/javascripts/ide/components/shared/tokened_input_spec.js index 09940fe8c6a..b09bf760543 100644 --- a/spec/javascripts/ide/components/shared/tokened_input_spec.js +++ b/spec/javascripts/ide/components/shared/tokened_input_spec.js @@ -44,8 +44,7 @@ describe('IDE shared/TokenedInput', () => { }); it('renders tokens', () => { - const renderedTokens = getTokenElements(vm) - .map(x => x.textContent.trim()); + const renderedTokens = getTokenElements(vm).map(x => x.textContent.trim()); expect(renderedTokens).toEqual(TEST_TOKENS.map(x => x.label)); }); @@ -85,10 +84,12 @@ describe('IDE shared/TokenedInput', () => { vm.value = ''; vm.onBackspace(); + expect(vm.$emit).not.toHaveBeenCalled(); expect(vm.backspaceCount).toEqual(1); vm.onBackspace(); + expect(vm.$emit).toHaveBeenCalledWith('removeToken', TEST_TOKENS[TEST_TOKENS.length - 1]); expect(vm.backspaceCount).toEqual(0); }); diff --git a/spec/javascripts/ide/stores/actions/merge_request_spec.js b/spec/javascripts/ide/stores/actions/merge_request_spec.js index 8564f04ce8a..3a4e0d7507f 100644 --- a/spec/javascripts/ide/stores/actions/merge_request_spec.js +++ b/spec/javascripts/ide/stores/actions/merge_request_spec.js @@ -262,7 +262,7 @@ describe('IDE store merge request actions', () => { bar: {}, }; - spyOn(store, 'dispatch').and.callFake((type) => { + spyOn(store, 'dispatch').and.callFake(type => { switch (type) { case 'getMergeRequestData': return Promise.resolve(testMergeRequest); @@ -280,14 +280,20 @@ describe('IDE store merge request actions', () => { expect(store.dispatch.calls.allArgs()).toEqual([ ['getMergeRequestData', mr], ['setCurrentBranchId', testMergeRequest.source_branch], - ['getBranchData', { - projectId: mr.projectId, - branchId: testMergeRequest.source_branch, - }], - ['getFiles', { - projectId: mr.projectId, - branchId: testMergeRequest.source_branch, - }], + [ + 'getBranchData', + { + projectId: mr.projectId, + branchId: testMergeRequest.source_branch, + }, + ], + [ + 'getFiles', + { + projectId: mr.projectId, + branchId: testMergeRequest.source_branch, + }, + ], ['getMergeRequestVersions', mr], ['getMergeRequestChanges', mr], ]); @@ -297,20 +303,21 @@ describe('IDE store merge request actions', () => { }); it('updates activity bar view and gets file data, if changes are found', done => { - testMergeRequestChanges.changes = [ - { new_path: 'foo' }, - { new_path: 'bar' }, - ]; + testMergeRequestChanges.changes = [{ new_path: 'foo' }, { new_path: 'bar' }]; openMergeRequest(store, mr) .then(() => { - expect(store.dispatch).toHaveBeenCalledWith('updateActivityBarView', activityBarViews.review); + expect(store.dispatch).toHaveBeenCalledWith( + 'updateActivityBarView', + activityBarViews.review, + ); testMergeRequestChanges.changes.forEach((change, i) => { expect(store.dispatch).toHaveBeenCalledWith('setFileMrChange', { file: store.state.entries[change.new_path], mrChange: change, }); + expect(store.dispatch).toHaveBeenCalledWith('getFileData', { path: change.new_path, makeFileActive: i === 0, @@ -334,7 +341,6 @@ describe('IDE store merge request actions', () => { }) .then(done) .catch(done.fail); - }); }); }); diff --git a/spec/javascripts/ide/stores/actions/project_spec.js b/spec/javascripts/ide/stores/actions/project_spec.js index 667e3e0a7ef..7d8c9edd965 100644 --- a/spec/javascripts/ide/stores/actions/project_spec.js +++ b/spec/javascripts/ide/stores/actions/project_spec.js @@ -270,7 +270,10 @@ describe('IDE store project actions', () => { it('does not handle tree entry action, if entry is pending', done => { openBranch(store, { ...branch, basePath: 'foo/bar-pending' }) .then(() => { - expect(store.dispatch).not.toHaveBeenCalledWith('handleTreeEntryAction', jasmine.anything()); + expect(store.dispatch).not.toHaveBeenCalledWith( + 'handleTreeEntryAction', + jasmine.anything(), + ); }) .then(done) .catch(done.fail); diff --git a/spec/javascripts/ide/stores/actions/tree_spec.js b/spec/javascripts/ide/stores/actions/tree_spec.js index 9f098eded08..d47c60dc581 100644 --- a/spec/javascripts/ide/stores/actions/tree_spec.js +++ b/spec/javascripts/ide/stores/actions/tree_spec.js @@ -71,6 +71,7 @@ describe('Multi-file store tree actions', () => { .dispatch('getFiles', basicCallParameters) .then(() => { projectTree = store.state.trees['abcproject/master']; + expect(projectTree.tree.length).toBe(2); expect(projectTree.tree[0].type).toBe('tree'); expect(projectTree.tree[0].tree[1].name).toBe('fileinfolder.js'); diff --git a/spec/javascripts/ide/stores/actions_spec.js b/spec/javascripts/ide/stores/actions_spec.js index c9a1158a14e..df291ade3f7 100644 --- a/spec/javascripts/ide/stores/actions_spec.js +++ b/spec/javascripts/ide/stores/actions_spec.js @@ -279,8 +279,6 @@ describe('Multi-file store actions', () => { }); }); - describe('popHistoryState', () => {}); - describe('scrollToTab', () => { it('focuses the current active element', done => { document.body.innerHTML += diff --git a/spec/javascripts/ide/stores/modules/branches/actions_spec.js b/spec/javascripts/ide/stores/modules/branches/actions_spec.js index 010f56af03b..2b3eac282f6 100644 --- a/spec/javascripts/ide/stores/modules/branches/actions_spec.js +++ b/spec/javascripts/ide/stores/modules/branches/actions_spec.js @@ -60,7 +60,6 @@ describe('IDE branches actions', () => { describe('receiveBranchesError', () => { it('should should commit error', done => { - testAction( receiveBranchesError, { search: TEST_SEARCH }, diff --git a/spec/javascripts/ide/stores/modules/pane/actions_spec.js b/spec/javascripts/ide/stores/modules/pane/actions_spec.js index f150ded6df5..799bc89a0c3 100644 --- a/spec/javascripts/ide/stores/modules/pane/actions_spec.js +++ b/spec/javascripts/ide/stores/modules/pane/actions_spec.js @@ -19,27 +19,13 @@ describe('IDE pane module actions', () => { }); it('dispatches close if opened', done => { - testAction( - actions.toggleOpen, - TEST_VIEW, - { isOpen: true }, - [], - [{ type: 'close' }], - done, - ); + testAction(actions.toggleOpen, TEST_VIEW, { isOpen: true }, [], [{ type: 'close' }], done); }); }); describe('open', () => { it('commits SET_OPEN', done => { - testAction( - actions.open, - null, - {}, - [{ type: types.SET_OPEN, payload: true }], - [], - done, - ); + testAction(actions.open, null, {}, [{ type: types.SET_OPEN, payload: true }], [], done); }); it('commits SET_CURRENT_VIEW if view is given', done => { @@ -74,14 +60,7 @@ describe('IDE pane module actions', () => { describe('close', () => { it('commits SET_OPEN', done => { - testAction( - actions.close, - null, - {}, - [{ type: types.SET_OPEN, payload: false }], - [], - done, - ); + testAction(actions.close, null, {}, [{ type: types.SET_OPEN, payload: false }], [], done); }); }); }); diff --git a/spec/javascripts/ide/stores/modules/pane/getters_spec.js b/spec/javascripts/ide/stores/modules/pane/getters_spec.js index 2060863b5d6..8a213323de0 100644 --- a/spec/javascripts/ide/stores/modules/pane/getters_spec.js +++ b/spec/javascripts/ide/stores/modules/pane/getters_spec.js @@ -23,10 +23,7 @@ describe('IDE pane module getters', () => { describe('isAliveView', () => { it('returns true if given view is in keepAliveViews', () => { - const result = getters.isAliveView( - { keepAliveViews: TEST_KEEP_ALIVE_VIEWS }, - {}, - )(TEST_VIEW); + const result = getters.isAliveView({ keepAliveViews: TEST_KEEP_ALIVE_VIEWS }, {})(TEST_VIEW); expect(result).toBe(true); }); @@ -41,10 +38,7 @@ describe('IDE pane module getters', () => { }); it('returns false if given view is active view and closed', () => { - const result = getters.isAliveView( - state(), - { isActiveView: () => true }, - )(TEST_VIEW); + const result = getters.isAliveView(state(), { isActiveView: () => true })(TEST_VIEW); expect(result).toBe(false); }); diff --git a/spec/javascripts/ide/stores/mutations/merge_request_spec.js b/spec/javascripts/ide/stores/mutations/merge_request_spec.js index f724bf464f5..e30ca22022f 100644 --- a/spec/javascripts/ide/stores/mutations/merge_request_spec.js +++ b/spec/javascripts/ide/stores/mutations/merge_request_spec.js @@ -45,6 +45,7 @@ describe('IDE store merge request mutations', () => { }); const newMr = localState.projects.abcproject.mergeRequests[1]; + expect(newMr.changes.diff).toBe('abc'); }); }); @@ -58,6 +59,7 @@ describe('IDE store merge request mutations', () => { }); const newMr = localState.projects.abcproject.mergeRequests[1]; + expect(newMr.versions.length).toBe(1); expect(newMr.versions[0].id).toBe(123); }); diff --git a/spec/javascripts/image_diff/helpers/badge_helper_spec.js b/spec/javascripts/image_diff/helpers/badge_helper_spec.js index ce3add1fd90..8ea05203d00 100644 --- a/spec/javascripts/image_diff/helpers/badge_helper_spec.js +++ b/spec/javascripts/image_diff/helpers/badge_helper_spec.js @@ -90,6 +90,7 @@ describe('badge helper', () => { it('should create icon comment button', () => { const iconEl = buttonEl.querySelector('svg'); + expect(iconEl).toBeDefined(); }); }); diff --git a/spec/javascripts/image_diff/helpers/comment_indicator_helper_spec.js b/spec/javascripts/image_diff/helpers/comment_indicator_helper_spec.js index 9d05c6859df..8e3e7f1222e 100644 --- a/spec/javascripts/image_diff/helpers/comment_indicator_helper_spec.js +++ b/spec/javascripts/image_diff/helpers/comment_indicator_helper_spec.js @@ -29,9 +29,11 @@ describe('commentIndicatorHelper', () => { it('should contain image-comment-dark svg', () => { const svgEl = buttonEl.querySelector('svg'); + expect(svgEl).toBeDefined(); const svgLink = svgEl.querySelector('use').getAttribute('xlink:href'); + expect(svgLink.indexOf('image-comment-dark')).not.toBe(-1); }); }); @@ -40,6 +42,7 @@ describe('commentIndicatorHelper', () => { describe('removeCommentIndicator', () => { it('should return removed false if there is no comment-indicator', () => { const result = commentIndicatorHelper.removeCommentIndicator(containerEl); + expect(result.removed).toEqual(false); }); @@ -84,6 +87,7 @@ describe('commentIndicatorHelper', () => { it('should set commentIndicator coordinates', () => { const commentIndicatorEl = containerEl.querySelector('.comment-indicator'); + expect(commentIndicatorEl.style.left).toEqual(`${coordinate.x}px`); expect(commentIndicatorEl.style.top).toEqual(`${coordinate.y}px`); }); @@ -96,6 +100,7 @@ describe('commentIndicatorHelper', () => { it('should addCommentIndicator', () => { const buttonEl = containerEl.querySelector('.comment-indicator'); + expect(buttonEl).toBeDefined(); expect(buttonEl.style.left).toEqual(`${coordinate.x}px`); expect(buttonEl.style.top).toEqual(`${coordinate.y}px`); diff --git a/spec/javascripts/image_diff/helpers/dom_helper_spec.js b/spec/javascripts/image_diff/helpers/dom_helper_spec.js index 8dde924e8ae..ffe712af2dd 100644 --- a/spec/javascripts/image_diff/helpers/dom_helper_spec.js +++ b/spec/javascripts/image_diff/helpers/dom_helper_spec.js @@ -92,6 +92,7 @@ describe('domHelper', () => { it('should force formEl to display none', () => { const formEl = element.querySelector('.discussion-form'); + expect(formEl.style.display).toEqual('none'); }); }); @@ -111,6 +112,7 @@ describe('domHelper', () => { it('should force formEl to display block', () => { const formEl = element.querySelector('.discussion-form'); + expect(formEl.style.display).toEqual('block'); }); }); diff --git a/spec/javascripts/image_diff/helpers/utils_helper_spec.js b/spec/javascripts/image_diff/helpers/utils_helper_spec.js index 31949c39d9c..6f62ee3f93b 100644 --- a/spec/javascripts/image_diff/helpers/utils_helper_spec.js +++ b/spec/javascripts/image_diff/helpers/utils_helper_spec.js @@ -5,13 +5,7 @@ import ImageBadge from '~/image_diff/image_badge'; import * as mockData from '../mock_data'; describe('utilsHelper', () => { - const { - noteId, - discussionId, - image, - imageProperties, - imageMeta, - } = mockData; + const { noteId, discussionId, image, imageProperties, imageMeta } = mockData; describe('resizeCoordinatesToImageElement', () => { let result; @@ -57,6 +51,7 @@ describe('utilsHelper', () => { it('should return actual image properties', () => { const { actual } = result; + expect(actual.x).toEqual(imageMeta.x); expect(actual.y).toEqual(imageMeta.y); expect(actual.width).toEqual(imageMeta.width); @@ -65,6 +60,7 @@ describe('utilsHelper', () => { it('should return browser image properties', () => { const { browser } = result; + expect(browser.x).toBeDefined(); expect(browser.y).toBeDefined(); expect(browser.width).toBeDefined(); @@ -106,6 +102,7 @@ describe('utilsHelper', () => { const result = utilsHelper.getTargetSelection(event); const { browser } = result; + expect(browser.x).toEqual(event.offsetX); expect(browser.y).toEqual(event.offsetY); expect(browser.width).toEqual(imageProperties.width); @@ -117,6 +114,7 @@ describe('utilsHelper', () => { const result = utilsHelper.getTargetSelection(event); const { actual } = result; + expect(actual.x).toEqual(100); expect(actual.y).toEqual(100); expect(actual.width).toEqual(imageProperties.naturalWidth); @@ -127,24 +125,28 @@ describe('utilsHelper', () => { it('should return x = 0 if x < 0', () => { const event = generateEvent(-5, 50); const result = utilsHelper.getTargetSelection(event); + expect(result.browser.x).toEqual(0); }); it('should return x = width if x > width', () => { const event = generateEvent(1000, 50); const result = utilsHelper.getTargetSelection(event); + expect(result.browser.x).toEqual(imageProperties.width); }); it('should return y = 0 if y < 0', () => { const event = generateEvent(50, -10); const result = utilsHelper.getTargetSelection(event); + expect(result.browser.y).toEqual(0); }); it('should return y = height if y > height', () => { const event = generateEvent(50, 1000); const result = utilsHelper.getTargetSelection(event); + expect(result.browser.y).toEqual(imageProperties.height); }); }); @@ -178,6 +180,7 @@ describe('utilsHelper', () => { `; const imageDiff = utilsHelper.initImageDiff(fileEl, true, false); + expect(ImageDiff.prototype.init).toHaveBeenCalled(); expect(imageDiff.canCreateNote).toEqual(true); expect(imageDiff.renderCommentBadge).toEqual(false); @@ -191,6 +194,7 @@ describe('utilsHelper', () => { `; const replacedImageDiff = utilsHelper.initImageDiff(fileEl, false, true); + expect(ReplacedImageDiff.prototype.init).toHaveBeenCalled(); expect(replacedImageDiff.canCreateNote).toEqual(false); expect(replacedImageDiff.renderCommentBadge).toEqual(true); diff --git a/spec/javascripts/image_diff/image_badge_spec.js b/spec/javascripts/image_diff/image_badge_spec.js index 87f98fc0926..2b23dce5d30 100644 --- a/spec/javascripts/image_diff/image_badge_spec.js +++ b/spec/javascripts/image_diff/image_badge_spec.js @@ -10,11 +10,14 @@ describe('ImageBadge', () => { }; it('should save actual property', () => { - const imageBadge = new ImageBadge(Object.assign({}, options, { - actual: imageMeta, - })); + const imageBadge = new ImageBadge( + Object.assign({}, options, { + actual: imageMeta, + }), + ); const { actual } = imageBadge; + expect(actual.x).toEqual(imageMeta.x); expect(actual.y).toEqual(imageMeta.y); expect(actual.width).toEqual(imageMeta.width); @@ -22,11 +25,14 @@ describe('ImageBadge', () => { }); it('should save browser property', () => { - const imageBadge = new ImageBadge(Object.assign({}, options, { - browser: imageMeta, - })); + const imageBadge = new ImageBadge( + Object.assign({}, options, { + browser: imageMeta, + }), + ); const { browser } = imageBadge; + expect(browser.x).toEqual(imageMeta.x); expect(browser.y).toEqual(imageMeta.y); expect(browser.width).toEqual(imageMeta.width); @@ -35,11 +41,13 @@ describe('ImageBadge', () => { it('should save noteId', () => { const imageBadge = new ImageBadge(options); + expect(imageBadge.noteId).toEqual(noteId); }); it('should save discussionId', () => { const imageBadge = new ImageBadge(options); + expect(imageBadge.discussionId).toEqual(discussionId); }); @@ -52,6 +60,7 @@ describe('ImageBadge', () => { it('should return defaultimageMeta if actual property is not provided', () => { const { actual } = imageBadge; + expect(actual.x).toEqual(0); expect(actual.y).toEqual(0); expect(actual.width).toEqual(0); @@ -60,6 +69,7 @@ describe('ImageBadge', () => { it('should return defaultimageMeta if browser property is not provided', () => { const { browser } = imageBadge; + expect(browser.x).toEqual(0); expect(browser.y).toEqual(0); expect(browser.width).toEqual(0); @@ -73,9 +83,11 @@ describe('ImageBadge', () => { }); it('should generate browser property', () => { - const imageBadge = new ImageBadge(Object.assign({}, options, { - imageEl: document.createElement('img'), - })); + const imageBadge = new ImageBadge( + Object.assign({}, options, { + imageEl: document.createElement('img'), + }), + ); expect(imageDiffHelper.resizeCoordinatesToImageElement).toHaveBeenCalled(); expect(imageBadge.browser).toEqual(true); diff --git a/spec/javascripts/image_diff/image_diff_spec.js b/spec/javascripts/image_diff/image_diff_spec.js index 346282328c7..21e7b8e2e9b 100644 --- a/spec/javascripts/image_diff/image_diff_spec.js +++ b/spec/javascripts/image_diff/image_diff_spec.js @@ -117,23 +117,15 @@ describe('ImageDiff', () => { it('should register click event delegation to js-diff-notes-toggle', () => { element.querySelector('.js-diff-notes-toggle').click(); + expect(imageDiffHelper.toggleCollapsed).toHaveBeenCalled(); }); it('should register click event delegation to comment-indicator', () => { element.querySelector('.comment-indicator').click(); - expect(imageDiffHelper.commentIndicatorOnClick).toHaveBeenCalled(); - }); - }); - describe('image loaded', () => { - beforeEach(() => { - spyOn(imageUtility, 'isImageLoaded').and.returnValue(true); - imageDiff = new ImageDiff(element); - imageDiff.imageEl = imageEl; + expect(imageDiffHelper.commentIndicatorOnClick).toHaveBeenCalled(); }); - - it('should renderBadges', () => {}); }); describe('image not loaded', () => { @@ -147,6 +139,7 @@ describe('ImageDiff', () => { it('should registers load eventListener', () => { const loadEvent = new Event('load'); imageEl.dispatchEvent(loadEvent); + expect(imageDiff.renderBadges).toHaveBeenCalled(); }); }); @@ -164,24 +157,28 @@ describe('ImageDiff', () => { it('should register click.imageDiff event', () => { const event = new CustomEvent('click.imageDiff'); element.dispatchEvent(event); + expect(imageDiff.imageClicked).toHaveBeenCalled(); }); it('should register blur.imageDiff event', () => { const event = new CustomEvent('blur.imageDiff'); element.dispatchEvent(event); + expect(imageDiffHelper.removeCommentIndicator).toHaveBeenCalled(); }); it('should register addBadge.imageDiff event', () => { const event = new CustomEvent('addBadge.imageDiff'); element.dispatchEvent(event); + expect(imageDiff.addBadge).toHaveBeenCalled(); }); it('should register removeBadge.imageDiff event', () => { const event = new CustomEvent('removeBadge.imageDiff'); element.dispatchEvent(event); + expect(imageDiff.removeBadge).toHaveBeenCalled(); }); }); @@ -197,6 +194,7 @@ describe('ImageDiff', () => { it('should not register click.imageDiff event', () => { const event = new CustomEvent('click.imageDiff'); element.dispatchEvent(event); + expect(imageDiff.imageClicked).not.toHaveBeenCalled(); }); }); @@ -240,6 +238,7 @@ describe('ImageDiff', () => { it('should call renderBadge for each discussionEl', () => { const discussionEls = element.querySelectorAll('.note-container .discussion-notes .notes'); + expect(imageDiff.renderBadge.calls.count()).toEqual(discussionEls.length); }); }); @@ -336,6 +335,7 @@ describe('ImageDiff', () => { describe('cascade badge count', () => { it('should update next imageBadgeEl value', () => { const imageBadgeEls = imageDiff.imageFrameEl.querySelectorAll('.badge'); + expect(imageBadgeEls[0].innerText).toEqual('1'); expect(imageBadgeEls[1].innerText).toEqual('2'); expect(imageBadgeEls.length).toEqual(2); diff --git a/spec/javascripts/image_diff/init_discussion_tab_spec.js b/spec/javascripts/image_diff/init_discussion_tab_spec.js index 7c447d6f70d..7cacc45ab62 100644 --- a/spec/javascripts/image_diff/init_discussion_tab_spec.js +++ b/spec/javascripts/image_diff/init_discussion_tab_spec.js @@ -11,7 +11,7 @@ describe('initDiscussionTab', () => { `); }); - it('should pass canCreateNote as false to initImageDiff', (done) => { + it('should pass canCreateNote as false to initImageDiff', done => { spyOn(imageDiffHelper, 'initImageDiff').and.callFake((diffFileEl, canCreateNote) => { expect(canCreateNote).toEqual(false); done(); @@ -20,11 +20,13 @@ describe('initDiscussionTab', () => { initDiscussionTab(); }); - it('should pass renderCommentBadge as true to initImageDiff', (done) => { - spyOn(imageDiffHelper, 'initImageDiff').and.callFake((diffFileEl, canCreateNote, renderCommentBadge) => { - expect(renderCommentBadge).toEqual(true); - done(); - }); + it('should pass renderCommentBadge as true to initImageDiff', done => { + spyOn(imageDiffHelper, 'initImageDiff').and.callFake( + (diffFileEl, canCreateNote, renderCommentBadge) => { + expect(renderCommentBadge).toEqual(true); + done(); + }, + ); initDiscussionTab(); }); @@ -32,6 +34,7 @@ describe('initDiscussionTab', () => { it('should call initImageDiff for each diffFileEls', () => { spyOn(imageDiffHelper, 'initImageDiff').and.callFake(() => {}); initDiscussionTab(); + expect(imageDiffHelper.initImageDiff.calls.count()).toEqual(2); }); }); diff --git a/spec/javascripts/image_diff/replaced_image_diff_spec.js b/spec/javascripts/image_diff/replaced_image_diff_spec.js index 5f8cd7c531a..62e7c8b6c6a 100644 --- a/spec/javascripts/image_diff/replaced_image_diff_spec.js +++ b/spec/javascripts/image_diff/replaced_image_diff_spec.js @@ -37,16 +37,28 @@ describe('ReplacedImageDiff', () => { function setupImageFrameEls() { replacedImageDiff.imageFrameEls = []; - replacedImageDiff.imageFrameEls[viewTypes.TWO_UP] = element.querySelector('.two-up .js-image-frame'); - replacedImageDiff.imageFrameEls[viewTypes.SWIPE] = element.querySelector('.swipe .js-image-frame'); - replacedImageDiff.imageFrameEls[viewTypes.ONION_SKIN] = element.querySelector('.onion-skin .js-image-frame'); + replacedImageDiff.imageFrameEls[viewTypes.TWO_UP] = element.querySelector( + '.two-up .js-image-frame', + ); + replacedImageDiff.imageFrameEls[viewTypes.SWIPE] = element.querySelector( + '.swipe .js-image-frame', + ); + replacedImageDiff.imageFrameEls[viewTypes.ONION_SKIN] = element.querySelector( + '.onion-skin .js-image-frame', + ); } function setupViewModesEls() { replacedImageDiff.viewModesEls = []; - replacedImageDiff.viewModesEls[viewTypes.TWO_UP] = element.querySelector('.view-modes-menu .two-up'); - replacedImageDiff.viewModesEls[viewTypes.SWIPE] = element.querySelector('.view-modes-menu .swipe'); - replacedImageDiff.viewModesEls[viewTypes.ONION_SKIN] = element.querySelector('.view-modes-menu .onion-skin'); + replacedImageDiff.viewModesEls[viewTypes.TWO_UP] = element.querySelector( + '.view-modes-menu .two-up', + ); + replacedImageDiff.viewModesEls[viewTypes.SWIPE] = element.querySelector( + '.view-modes-menu .swipe', + ); + replacedImageDiff.viewModesEls[viewTypes.ONION_SKIN] = element.querySelector( + '.view-modes-menu .onion-skin', + ); } function setupImageEls() { @@ -58,6 +70,7 @@ describe('ReplacedImageDiff', () => { it('should extend ImageDiff', () => { replacedImageDiff = new ReplacedImageDiff(element); + expect(replacedImageDiff instanceof ImageDiff).toEqual(true); }); @@ -72,18 +85,36 @@ describe('ReplacedImageDiff', () => { it('should set imageFrameEls', () => { const { imageFrameEls } = replacedImageDiff; + expect(imageFrameEls).toBeDefined(); - expect(imageFrameEls[viewTypes.TWO_UP]).toEqual(element.querySelector('.two-up .js-image-frame')); - expect(imageFrameEls[viewTypes.SWIPE]).toEqual(element.querySelector('.swipe .js-image-frame')); - expect(imageFrameEls[viewTypes.ONION_SKIN]).toEqual(element.querySelector('.onion-skin .js-image-frame')); + expect(imageFrameEls[viewTypes.TWO_UP]).toEqual( + element.querySelector('.two-up .js-image-frame'), + ); + + expect(imageFrameEls[viewTypes.SWIPE]).toEqual( + element.querySelector('.swipe .js-image-frame'), + ); + + expect(imageFrameEls[viewTypes.ONION_SKIN]).toEqual( + element.querySelector('.onion-skin .js-image-frame'), + ); }); it('should set viewModesEls', () => { const { viewModesEls } = replacedImageDiff; + expect(viewModesEls).toBeDefined(); - expect(viewModesEls[viewTypes.TWO_UP]).toEqual(element.querySelector('.view-modes-menu .two-up')); - expect(viewModesEls[viewTypes.SWIPE]).toEqual(element.querySelector('.view-modes-menu .swipe')); - expect(viewModesEls[viewTypes.ONION_SKIN]).toEqual(element.querySelector('.view-modes-menu .onion-skin')); + expect(viewModesEls[viewTypes.TWO_UP]).toEqual( + element.querySelector('.view-modes-menu .two-up'), + ); + + expect(viewModesEls[viewTypes.SWIPE]).toEqual( + element.querySelector('.view-modes-menu .swipe'), + ); + + expect(viewModesEls[viewTypes.ONION_SKIN]).toEqual( + element.querySelector('.view-modes-menu .onion-skin'), + ); }); it('should generateImageEls', () => { @@ -97,6 +128,7 @@ describe('ReplacedImageDiff', () => { describe('currentView', () => { it('should set currentView', () => { replacedImageDiff.init(viewTypes.ONION_SKIN); + expect(replacedImageDiff.currentView).toEqual(viewTypes.ONION_SKIN); }); @@ -121,6 +153,7 @@ describe('ReplacedImageDiff', () => { it('should set imageEls', () => { replacedImageDiff.generateImageEls(); const { imageEls } = replacedImageDiff; + expect(imageEls).toBeDefined(); expect(imageEls[viewTypes.TWO_UP]).toEqual(element.querySelector('.two-up img')); expect(imageEls[viewTypes.SWIPE]).toEqual(element.querySelector('.swipe img')); @@ -138,11 +171,12 @@ describe('ReplacedImageDiff', () => { it('should call super.bindEvents', () => { replacedImageDiff.bindEvents(); + expect(ImageDiff.prototype.bindEvents).toHaveBeenCalled(); }); - it('should register click eventlistener to 2-up view mode', (done) => { - spyOn(ReplacedImageDiff.prototype, 'changeView').and.callFake((viewMode) => { + it('should register click eventlistener to 2-up view mode', done => { + spyOn(ReplacedImageDiff.prototype, 'changeView').and.callFake(viewMode => { expect(viewMode).toEqual(viewTypes.TWO_UP); done(); }); @@ -151,8 +185,8 @@ describe('ReplacedImageDiff', () => { replacedImageDiff.viewModesEls[viewTypes.TWO_UP].click(); }); - it('should register click eventlistener to swipe view mode', (done) => { - spyOn(ReplacedImageDiff.prototype, 'changeView').and.callFake((viewMode) => { + it('should register click eventlistener to swipe view mode', done => { + spyOn(ReplacedImageDiff.prototype, 'changeView').and.callFake(viewMode => { expect(viewMode).toEqual(viewTypes.SWIPE); done(); }); @@ -161,8 +195,8 @@ describe('ReplacedImageDiff', () => { replacedImageDiff.viewModesEls[viewTypes.SWIPE].click(); }); - it('should register click eventlistener to onion skin view mode', (done) => { - spyOn(ReplacedImageDiff.prototype, 'changeView').and.callFake((viewMode) => { + it('should register click eventlistener to onion skin view mode', done => { + spyOn(ReplacedImageDiff.prototype, 'changeView').and.callFake(viewMode => { expect(viewMode).toEqual(viewTypes.SWIPE); done(); }); @@ -184,6 +218,7 @@ describe('ReplacedImageDiff', () => { expect(replacedImageDiff.imageEl).toEqual(element.querySelector('.two-up img')); replacedImageDiff.currentView = viewTypes.SWIPE; + expect(replacedImageDiff.imageEl).toEqual(element.querySelector('.swipe img')); }); }); @@ -196,10 +231,15 @@ describe('ReplacedImageDiff', () => { }); it('should return imageFrameEl based on currentView', () => { - expect(replacedImageDiff.imageFrameEl).toEqual(element.querySelector('.two-up .js-image-frame')); + expect(replacedImageDiff.imageFrameEl).toEqual( + element.querySelector('.two-up .js-image-frame'), + ); replacedImageDiff.currentView = viewTypes.ONION_SKIN; - expect(replacedImageDiff.imageFrameEl).toEqual(element.querySelector('.onion-skin .js-image-frame')); + + expect(replacedImageDiff.imageFrameEl).toEqual( + element.querySelector('.onion-skin .js-image-frame'), + ); }); }); }); @@ -248,6 +288,7 @@ describe('ReplacedImageDiff', () => { it('should call renderNewView', () => { jasmine.clock().tick(251); + expect(replacedImageDiff.renderNewView).toHaveBeenCalled(); }); }); @@ -284,7 +325,7 @@ describe('ReplacedImageDiff', () => { setupImageFrameEls(); }); - it('should pass showCommentIndicator normalized indicator values', (done) => { + it('should pass showCommentIndicator normalized indicator values', done => { spyOn(imageDiffHelper, 'showCommentIndicator').and.callFake(() => {}); spyOn(imageDiffHelper, 'resizeCoordinatesToImageElement').and.callFake((imageEl, meta) => { expect(meta.x).toEqual(indicator.x); @@ -296,15 +337,17 @@ describe('ReplacedImageDiff', () => { replacedImageDiff.renderNewView(indicator); }); - it('should call showCommentIndicator', (done) => { + it('should call showCommentIndicator', done => { const normalized = { normalized: true, }; spyOn(imageDiffHelper, 'resizeCoordinatesToImageElement').and.returnValue(normalized); - spyOn(imageDiffHelper, 'showCommentIndicator').and.callFake((imageFrameEl, normalizedIndicator) => { - expect(normalizedIndicator).toEqual(normalized); - done(); - }); + spyOn(imageDiffHelper, 'showCommentIndicator').and.callFake( + (imageFrameEl, normalizedIndicator) => { + expect(normalizedIndicator).toEqual(normalized); + done(); + }, + ); replacedImageDiff.renderNewView(indicator); }); }); diff --git a/spec/javascripts/importer_status_spec.js b/spec/javascripts/importer_status_spec.js index 63cdb3d5114..e7f195ed57c 100644 --- a/spec/javascripts/importer_status_spec.js +++ b/spec/javascripts/importer_status_spec.js @@ -35,38 +35,43 @@ describe('Importer Status', () => { }); }); - it('sets table row to active after post request', (done) => { + it('sets table row to active after post request', done => { mock.onPost(importUrl).reply(200, { id: 1, full_path: '/full_path', }); - instance.addToImport({ - currentTarget: document.querySelector('.js-add-to-import'), - }) - .then(() => { - expect(document.querySelector('tr').classList.contains('table-active')).toEqual(true); - done(); - }) - .catch(done.fail); + instance + .addToImport({ + currentTarget: document.querySelector('.js-add-to-import'), + }) + .then(() => { + expect(document.querySelector('tr').classList.contains('table-active')).toEqual(true); + done(); + }) + .catch(done.fail); }); - it('shows error message after failed POST request', (done) => { + it('shows error message after failed POST request', done => { appendSetFixtures('<div class="flash-container"></div>'); mock.onPost(importUrl).reply(422, { errors: 'You forgot your lunch', }); - instance.addToImport({ - currentTarget: document.querySelector('.js-add-to-import'), - }) - .then(() => { - const flashMessage = document.querySelector('.flash-text'); - expect(flashMessage.textContent.trim()).toEqual('An error occurred while importing project: You forgot your lunch'); - done(); - }) - .catch(done.fail); + instance + .addToImport({ + currentTarget: document.querySelector('.js-add-to-import'), + }) + .then(() => { + const flashMessage = document.querySelector('.flash-text'); + + expect(flashMessage.textContent.trim()).toEqual( + 'An error occurred while importing project: You forgot your lunch', + ); + done(); + }) + .catch(done.fail); }); }); @@ -92,14 +97,17 @@ describe('Importer Status', () => { }); function setupMock(importStatus) { - mock.onGet(jobsUrl).reply(200, [{ - id: 1, - import_status: importStatus, - }]); + mock.onGet(jobsUrl).reply(200, [ + { + id: 1, + import_status: importStatus, + }, + ]); } function expectJobStatus(done, status) { - instance.autoUpdate() + instance + .autoUpdate() .then(() => { expect(document.querySelector('#project_1').innerText.trim()).toEqual(status); done(); @@ -107,22 +115,22 @@ describe('Importer Status', () => { .catch(done.fail); } - it('sets the job status to done', (done) => { + it('sets the job status to done', done => { setupMock('finished'); expectJobStatus(done, 'Done'); }); - it('sets the job status to scheduled', (done) => { + it('sets the job status to scheduled', done => { setupMock('scheduled'); expectJobStatus(done, 'Scheduled'); }); - it('sets the job status to started', (done) => { + it('sets the job status to started', done => { setupMock('started'); expectJobStatus(done, 'Started'); }); - it('sets the job status to custom status', (done) => { + it('sets the job status to custom status', done => { setupMock('custom status'); expectJobStatus(done, 'custom status'); }); diff --git a/spec/javascripts/integrations/integration_settings_form_spec.js b/spec/javascripts/integrations/integration_settings_form_spec.js index e07343810d2..4f4c9a7b463 100644 --- a/spec/javascripts/integrations/integration_settings_form_spec.js +++ b/spec/javascripts/integrations/integration_settings_form_spec.js @@ -68,21 +68,27 @@ describe('IntegrationSettingsForm', () => { integrationSettingsForm.canTestService = true; integrationSettingsForm.toggleSubmitBtnLabel(true); - expect(integrationSettingsForm.$submitBtnLabel.text()).toEqual('Test settings and save changes'); + + expect(integrationSettingsForm.$submitBtnLabel.text()).toEqual( + 'Test settings and save changes', + ); }); it('should set Save button label to "Save changes" when either serviceActive or canTestService (or both) is `false`', () => { integrationSettingsForm.canTestService = false; integrationSettingsForm.toggleSubmitBtnLabel(false); + expect(integrationSettingsForm.$submitBtnLabel.text()).toEqual('Save changes'); integrationSettingsForm.toggleSubmitBtnLabel(true); + expect(integrationSettingsForm.$submitBtnLabel.text()).toEqual('Save changes'); integrationSettingsForm.canTestService = true; integrationSettingsForm.toggleSubmitBtnLabel(false); + expect(integrationSettingsForm.$submitBtnLabel.text()).toEqual('Save changes'); }); }); @@ -127,8 +133,9 @@ describe('IntegrationSettingsForm', () => { mock.restore(); }); - it('should make an ajax request with provided `formData`', (done) => { - integrationSettingsForm.testSettings(formData) + it('should make an ajax request with provided `formData`', done => { + integrationSettingsForm + .testSettings(formData) .then(() => { expect(axios.put).toHaveBeenCalledWith(integrationSettingsForm.testEndPoint, formData); @@ -137,7 +144,7 @@ describe('IntegrationSettingsForm', () => { .catch(done.fail); }); - it('should show error Flash with `Save anyway` action if ajax request responds with error in test', (done) => { + it('should show error Flash with `Save anyway` action if ajax request responds with error in test', done => { const errorMessage = 'Test failed.'; mock.onPut(integrationSettingsForm.testEndPoint).reply(200, { error: true, @@ -146,19 +153,32 @@ describe('IntegrationSettingsForm', () => { test_failed: true, }); - integrationSettingsForm.testSettings(formData) + integrationSettingsForm + .testSettings(formData) .then(() => { const $flashContainer = $('.flash-container'); - expect($flashContainer.find('.flash-text').text().trim()).toEqual('Test failed. some error'); + + expect( + $flashContainer + .find('.flash-text') + .text() + .trim(), + ).toEqual('Test failed. some error'); + expect($flashContainer.find('.flash-action')).toBeDefined(); - expect($flashContainer.find('.flash-action').text().trim()).toEqual('Save anyway'); + expect( + $flashContainer + .find('.flash-action') + .text() + .trim(), + ).toEqual('Save anyway'); done(); }) .catch(done.fail); }); - it('should not show error Flash with `Save anyway` action if ajax request responds with error in validation', (done) => { + it('should not show error Flash with `Save anyway` action if ajax request responds with error in validation', done => { const errorMessage = 'Validations failed.'; mock.onPut(integrationSettingsForm.testEndPoint).reply(200, { error: true, @@ -167,26 +187,40 @@ describe('IntegrationSettingsForm', () => { test_failed: false, }); - integrationSettingsForm.testSettings(formData) + integrationSettingsForm + .testSettings(formData) .then(() => { const $flashContainer = $('.flash-container'); - expect($flashContainer.find('.flash-text').text().trim()).toEqual('Validations failed. some error'); + + expect( + $flashContainer + .find('.flash-text') + .text() + .trim(), + ).toEqual('Validations failed. some error'); + expect($flashContainer.find('.flash-action')).toBeDefined(); - expect($flashContainer.find('.flash-action').text().trim()).toEqual(''); + expect( + $flashContainer + .find('.flash-action') + .text() + .trim(), + ).toEqual(''); done(); }) .catch(done.fail); }); - it('should submit form if ajax request responds without any error in test', (done) => { + it('should submit form if ajax request responds without any error in test', done => { spyOn(integrationSettingsForm.$form, 'submit'); mock.onPut(integrationSettingsForm.testEndPoint).reply(200, { error: false, }); - integrationSettingsForm.testSettings(formData) + integrationSettingsForm + .testSettings(formData) .then(() => { expect(integrationSettingsForm.$form.submit).toHaveBeenCalled(); @@ -195,7 +229,7 @@ describe('IntegrationSettingsForm', () => { .catch(done.fail); }); - it('should submit form when clicked on `Save anyway` action of error Flash', (done) => { + it('should submit form when clicked on `Save anyway` action of error Flash', done => { spyOn(integrationSettingsForm.$form, 'submit'); const errorMessage = 'Test failed.'; @@ -205,9 +239,11 @@ describe('IntegrationSettingsForm', () => { test_failed: true, }); - integrationSettingsForm.testSettings(formData) + integrationSettingsForm + .testSettings(formData) .then(() => { const $flashAction = $('.flash-container .flash-action'); + expect($flashAction).toBeDefined(); $flashAction.get(0).click(); @@ -220,26 +256,32 @@ describe('IntegrationSettingsForm', () => { .catch(done.fail); }); - it('should show error Flash if ajax request failed', (done) => { + it('should show error Flash if ajax request failed', done => { const errorMessage = 'Something went wrong on our end.'; mock.onPut(integrationSettingsForm.testEndPoint).networkError(); - integrationSettingsForm.testSettings(formData) + integrationSettingsForm + .testSettings(formData) .then(() => { - expect($('.flash-container .flash-text').text().trim()).toEqual(errorMessage); + expect( + $('.flash-container .flash-text') + .text() + .trim(), + ).toEqual(errorMessage); done(); }) .catch(done.fail); }); - it('should always call `toggleSubmitBtnState` with `false` once request is completed', (done) => { + it('should always call `toggleSubmitBtnState` with `false` once request is completed', done => { mock.onPut(integrationSettingsForm.testEndPoint).networkError(); spyOn(integrationSettingsForm, 'toggleSubmitBtnState'); - integrationSettingsForm.testSettings(formData) + integrationSettingsForm + .testSettings(formData) .then(() => { expect(integrationSettingsForm.toggleSubmitBtnState).toHaveBeenCalledWith(false); diff --git a/spec/javascripts/issuable_spec.js b/spec/javascripts/issuable_spec.js index 57bf746f080..25543053eba 100644 --- a/spec/javascripts/issuable_spec.js +++ b/spec/javascripts/issuable_spec.js @@ -8,6 +8,7 @@ describe('Issuable', () => { describe('initBulkUpdate', () => { it('should not set bulkUpdateSidebar', () => { Issuable = new IssuableIndex('issue_'); + expect(Issuable.bulkUpdateSidebar).not.toBeDefined(); }); @@ -17,6 +18,7 @@ describe('Issuable', () => { document.body.appendChild(element); Issuable = new IssuableIndex('issue_'); + expect(Issuable.bulkUpdateSidebar).toBeDefined(); }); }); @@ -47,7 +49,7 @@ describe('Issuable', () => { mock.restore(); }); - it('should send request to reset email token', (done) => { + it('should send request to reset email token', done => { spyOn(axios, 'put').and.callThrough(); document.querySelector('.incoming-email-token-reset').click(); @@ -60,4 +62,3 @@ describe('Issuable', () => { }); }); }); - diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js index 36328382448..2bd1b3996dc 100644 --- a/spec/javascripts/issue_show/components/app_spec.js +++ b/spec/javascripts/issue_show/components/app_spec.js @@ -11,10 +11,7 @@ function formatText(text) { return text.trim().replace(/\s\s+/g, ' '); } -const REALTIME_REQUEST_STACK = [ - issueShowData.initialRequest, - issueShowData.secondRequest, -]; +const REALTIME_REQUEST_STACK = [issueShowData.initialRequest, issueShowData.secondRequest]; describe('Issuable output', () => { let mock; @@ -23,7 +20,7 @@ describe('Issuable output', () => { document.body.innerHTML = '<span id="task_status"></span>'; - beforeEach((done) => { + beforeEach(done => { spyOn(eventHub, '$emit'); const IssuableDescriptionComponent = Vue.extend(issuableApp); @@ -64,65 +61,67 @@ describe('Issuable output', () => { vm.$destroy(); }); - it('should render a title/description/edited and update title/description/edited on update', (done) => { + it('should render a title/description/edited and update title/description/edited on update', done => { let editedText; Vue.nextTick() - .then(() => { - editedText = vm.$el.querySelector('.edited-text'); - }) - .then(() => { - expect(document.querySelector('title').innerText).toContain('this is a title (#1)'); - expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>this is a title</p>'); - expect(vm.$el.querySelector('.wiki').innerHTML).toContain('<p>this is a description!</p>'); - expect(vm.$el.querySelector('.js-task-list-field').value).toContain('this is a description'); - expect(formatText(editedText.innerText)).toMatch(/Edited[\s\S]+?by Some User/); - expect(editedText.querySelector('.author-link').href).toMatch(/\/some_user$/); - expect(editedText.querySelector('time')).toBeTruthy(); - }) - .then(() => { - vm.poll.makeRequest(); - }) - .then(() => new Promise(resolve => setTimeout(resolve))) - .then(() => { - expect(document.querySelector('title').innerText).toContain('2 (#1)'); - expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>2</p>'); - expect(vm.$el.querySelector('.wiki').innerHTML).toContain('<p>42</p>'); - expect(vm.$el.querySelector('.js-task-list-field').value).toContain('42'); - expect(vm.$el.querySelector('.edited-text')).toBeTruthy(); - expect(formatText(vm.$el.querySelector('.edited-text').innerText)).toMatch(/Edited[\s\S]+?by Other User/); - expect(editedText.querySelector('.author-link').href).toMatch(/\/other_user$/); - expect(editedText.querySelector('time')).toBeTruthy(); - }) - .then(done) - .catch(done.fail); + .then(() => { + editedText = vm.$el.querySelector('.edited-text'); + }) + .then(() => { + expect(document.querySelector('title').innerText).toContain('this is a title (#1)'); + expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>this is a title</p>'); + expect(vm.$el.querySelector('.wiki').innerHTML).toContain('<p>this is a description!</p>'); + expect(vm.$el.querySelector('.js-task-list-field').value).toContain( + 'this is a description', + ); + + expect(formatText(editedText.innerText)).toMatch(/Edited[\s\S]+?by Some User/); + expect(editedText.querySelector('.author-link').href).toMatch(/\/some_user$/); + expect(editedText.querySelector('time')).toBeTruthy(); + }) + .then(() => { + vm.poll.makeRequest(); + }) + .then(() => new Promise(resolve => setTimeout(resolve))) + .then(() => { + expect(document.querySelector('title').innerText).toContain('2 (#1)'); + expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>2</p>'); + expect(vm.$el.querySelector('.wiki').innerHTML).toContain('<p>42</p>'); + expect(vm.$el.querySelector('.js-task-list-field').value).toContain('42'); + expect(vm.$el.querySelector('.edited-text')).toBeTruthy(); + expect(formatText(vm.$el.querySelector('.edited-text').innerText)).toMatch( + /Edited[\s\S]+?by Other User/, + ); + + expect(editedText.querySelector('.author-link').href).toMatch(/\/other_user$/); + expect(editedText.querySelector('time')).toBeTruthy(); + }) + .then(done) + .catch(done.fail); }); - it('shows actions if permissions are correct', (done) => { + it('shows actions if permissions are correct', done => { vm.showForm = true; Vue.nextTick(() => { - expect( - vm.$el.querySelector('.btn'), - ).not.toBeNull(); + expect(vm.$el.querySelector('.btn')).not.toBeNull(); done(); }); }); - it('does not show actions if permissions are incorrect', (done) => { + it('does not show actions if permissions are incorrect', done => { vm.showForm = true; vm.canUpdate = false; Vue.nextTick(() => { - expect( - vm.$el.querySelector('.btn'), - ).toBeNull(); + expect(vm.$el.querySelector('.btn')).toBeNull(); done(); }); }); - it('does not update formState if form is already open', (done) => { + it('does not update formState if form is already open', done => { vm.openForm(); vm.state.titleText = 'testing 123'; @@ -130,25 +129,26 @@ describe('Issuable output', () => { vm.openForm(); Vue.nextTick(() => { - expect( - vm.store.formState.title, - ).not.toBe('testing 123'); + expect(vm.store.formState.title).not.toBe('testing 123'); done(); }); }); describe('updateIssuable', () => { - it('fetches new data after update', (done) => { + it('fetches new data after update', done => { spyOn(vm.service, 'getData').and.callThrough(); - spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => { - resolve({ - data: { - confidential: false, - web_url: window.location.pathname, - }, - }); - })); + spyOn(vm.service, 'updateIssuable').and.callFake( + () => + new Promise(resolve => { + resolve({ + data: { + confidential: false, + web_url: window.location.pathname, + }, + }); + }), + ); vm.updateIssuable() .then(() => { @@ -158,10 +158,13 @@ describe('Issuable output', () => { .catch(done.fail); }); - it('correctly updates issuable data', (done) => { - spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => { - resolve(); - })); + it('correctly updates issuable data', done => { + spyOn(vm.service, 'updateIssuable').and.callFake( + () => + new Promise(resolve => { + resolve(); + }), + ); vm.updateIssuable() .then(() => { @@ -172,16 +175,19 @@ describe('Issuable output', () => { .catch(done.fail); }); - it('does not redirect if issue has not moved', (done) => { + it('does not redirect if issue has not moved', done => { const visitUrl = spyOnDependency(issuableApp, 'visitUrl'); - spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => { - resolve({ - data: { - web_url: window.location.pathname, - confidential: vm.isConfidential, - }, - }); - })); + spyOn(vm.service, 'updateIssuable').and.callFake( + () => + new Promise(resolve => { + resolve({ + data: { + web_url: window.location.pathname, + confidential: vm.isConfidential, + }, + }); + }), + ); vm.updateIssuable(); @@ -191,16 +197,19 @@ describe('Issuable output', () => { }); }); - it('redirects if returned web_url has changed', (done) => { + it('redirects if returned web_url has changed', done => { const visitUrl = spyOnDependency(issuableApp, 'visitUrl'); - spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => { - resolve({ - data: { - web_url: '/testing-issue-move', - confidential: vm.isConfidential, - }, - }); - })); + spyOn(vm.service, 'updateIssuable').and.callFake( + () => + new Promise(resolve => { + resolve({ + data: { + web_url: '/testing-issue-move', + confidential: vm.isConfidential, + }, + }); + }), + ); vm.updateIssuable(); @@ -211,7 +220,7 @@ describe('Issuable output', () => { }); describe('shows dialog when issue has unsaved changed', () => { - it('confirms on title change', (done) => { + it('confirms on title change', done => { vm.showForm = true; vm.state.titleText = 'title has changed'; const e = { returnValue: null }; @@ -222,7 +231,7 @@ describe('Issuable output', () => { }); }); - it('confirms on description change', (done) => { + it('confirms on description change', done => { vm.showForm = true; vm.state.descriptionText = 'description has changed'; const e = { returnValue: null }; @@ -233,7 +242,7 @@ describe('Issuable output', () => { }); }); - it('does nothing when nothing has changed', (done) => { + it('does nothing when nothing has changed', done => { const e = { returnValue: null }; vm.handleBeforeUnloadEvent(e); Vue.nextTick(() => { @@ -246,39 +255,36 @@ describe('Issuable output', () => { describe('error when updating', () => { beforeEach(() => { spyOn(window, 'Flash').and.callThrough(); - spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve, reject) => { - reject(); - })); + spyOn(vm.service, 'updateIssuable').and.callFake( + () => + new Promise((resolve, reject) => { + reject(); + }), + ); }); - it('closes form on error', (done) => { + it('closes form on error', done => { vm.updateIssuable(); setTimeout(() => { - expect( - eventHub.$emit, - ).toHaveBeenCalledWith('close.form'); - expect( - window.Flash, - ).toHaveBeenCalledWith('Error updating issue'); + expect(eventHub.$emit).toHaveBeenCalledWith('close.form'); + + expect(window.Flash).toHaveBeenCalledWith('Error updating issue'); done(); }); }); - it('returns the correct error message for issuableType', (done) => { + it('returns the correct error message for issuableType', done => { vm.issuableType = 'merge request'; Vue.nextTick(() => { vm.updateIssuable(); setTimeout(() => { - expect( - eventHub.$emit, - ).toHaveBeenCalledWith('close.form'); - expect( - window.Flash, - ).toHaveBeenCalledWith('Error updating merge request'); + expect(eventHub.$emit).toHaveBeenCalledWith('close.form'); + + expect(window.Flash).toHaveBeenCalledWith('Error updating merge request'); done(); }); @@ -287,16 +293,18 @@ describe('Issuable output', () => { }); }); - it('opens recaptcha modal if update rejected as spam', (done) => { + it('opens recaptcha modal if update rejected as spam', done => { function mockScriptSrc() { - const recaptchaChild = vm.$children - .find(child => child.$options._componentTag === 'recaptcha-modal'); // eslint-disable-line no-underscore-dangle + const recaptchaChild = vm.$children.find( + // eslint-disable-next-line no-underscore-dangle + child => child.$options._componentTag === 'recaptcha-modal', + ); recaptchaChild.scriptSrc = '//scriptsrc'; } let modal; - const promise = new Promise((resolve) => { + const promise = new Promise(resolve => { resolve({ data: { recaptcha_html: '<div class="g-recaptcha">recaptcha_html</div>', @@ -332,15 +340,18 @@ describe('Issuable output', () => { }); describe('deleteIssuable', () => { - it('changes URL when deleted', (done) => { + it('changes URL when deleted', done => { const visitUrl = spyOnDependency(issuableApp, 'visitUrl'); - spyOn(vm.service, 'deleteIssuable').and.callFake(() => new Promise((resolve) => { - resolve({ - data: { - web_url: '/test', - }, - }); - })); + spyOn(vm.service, 'deleteIssuable').and.callFake( + () => + new Promise(resolve => { + resolve({ + data: { + web_url: '/test', + }, + }); + }), + ); vm.deleteIssuable(); @@ -350,42 +361,43 @@ describe('Issuable output', () => { }); }); - it('stops polling when deleting', (done) => { + it('stops polling when deleting', done => { spyOnDependency(issuableApp, 'visitUrl'); spyOn(vm.poll, 'stop').and.callThrough(); - spyOn(vm.service, 'deleteIssuable').and.callFake(() => new Promise((resolve) => { - resolve({ - data: { - web_url: '/test', - }, - }); - })); + spyOn(vm.service, 'deleteIssuable').and.callFake( + () => + new Promise(resolve => { + resolve({ + data: { + web_url: '/test', + }, + }); + }), + ); vm.deleteIssuable(); setTimeout(() => { - expect( - vm.poll.stop, - ).toHaveBeenCalledWith(); + expect(vm.poll.stop).toHaveBeenCalledWith(); done(); }); }); - it('closes form on error', (done) => { + it('closes form on error', done => { spyOn(window, 'Flash').and.callThrough(); - spyOn(vm.service, 'deleteIssuable').and.callFake(() => new Promise((resolve, reject) => { - reject(); - })); + spyOn(vm.service, 'deleteIssuable').and.callFake( + () => + new Promise((resolve, reject) => { + reject(); + }), + ); vm.deleteIssuable(); setTimeout(() => { - expect( - eventHub.$emit, - ).toHaveBeenCalledWith('close.form'); - expect( - window.Flash, - ).toHaveBeenCalledWith('Error deleting issue'); + expect(eventHub.$emit).toHaveBeenCalledWith('close.form'); + + expect(window.Flash).toHaveBeenCalledWith('Error deleting issue'); done(); }); @@ -393,7 +405,7 @@ describe('Issuable output', () => { }); describe('open form', () => { - it('shows locked warning if form is open & data is different', (done) => { + it('shows locked warning if form is open & data is different', done => { vm.$nextTick() .then(() => { vm.openForm(); @@ -422,6 +434,7 @@ describe('Issuable output', () => { it('should render if showInlineEditButton', () => { vm.showInlineEditButton = true; + expect(vm.$el.querySelector('.title-container .note-action-button')).toBeDefined(); }); }); diff --git a/spec/javascripts/issue_show/components/description_spec.js b/spec/javascripts/issue_show/components/description_spec.js index 889c8545faa..463f3c89926 100644 --- a/spec/javascripts/issue_show/components/description_spec.js +++ b/spec/javascripts/issue_show/components/description_spec.js @@ -33,7 +33,7 @@ describe('Description component', () => { vm.$destroy(); }); - it('animates description changes', (done) => { + it('animates description changes', done => { vm.descriptionHtml = 'changed'; Vue.nextTick(() => { @@ -51,10 +51,12 @@ describe('Description component', () => { }); }); - it('opens recaptcha dialog if update rejected as spam', (done) => { + it('opens recaptcha dialog if update rejected as spam', done => { let modal; - const recaptchaChild = vm.$children - .find(child => child.$options._componentTag === 'recaptcha-modal'); // eslint-disable-line no-underscore-dangle + const recaptchaChild = vm.$children.find( + // eslint-disable-next-line no-underscore-dangle + child => child.$options._componentTag === 'recaptcha-modal', + ); recaptchaChild.scriptSrc = '//scriptsrc'; @@ -84,13 +86,16 @@ describe('Description component', () => { let TaskList; beforeEach(() => { - vm = mountComponent(DescriptionComponent, Object.assign({}, props, { - issuableType: 'issuableType', - })); + vm = mountComponent( + DescriptionComponent, + Object.assign({}, props, { + issuableType: 'issuableType', + }), + ); TaskList = spyOnDependency(Description, 'TaskList'); }); - it('re-inits the TaskList when description changed', (done) => { + it('re-inits the TaskList when description changed', done => { vm.descriptionHtml = 'changed'; setTimeout(() => { @@ -99,7 +104,7 @@ describe('Description component', () => { }); }); - it('does not re-init the TaskList when canUpdate is false', (done) => { + it('does not re-init the TaskList when canUpdate is false', done => { vm.canUpdate = false; vm.descriptionHtml = 'changed'; @@ -109,7 +114,7 @@ describe('Description component', () => { }); }); - it('calls with issuableType dataType', (done) => { + it('calls with issuableType dataType', done => { vm.descriptionHtml = 'changed'; setTimeout(() => { @@ -125,44 +130,42 @@ describe('Description component', () => { }); describe('taskStatus', () => { - it('adds full taskStatus', (done) => { + it('adds full taskStatus', done => { vm.taskStatus = '1 of 1'; setTimeout(() => { - expect( - document.querySelector('.issuable-meta #task_status').textContent.trim(), - ).toBe('1 of 1'); + expect(document.querySelector('.issuable-meta #task_status').textContent.trim()).toBe( + '1 of 1', + ); done(); }); }); - it('adds short taskStatus', (done) => { + it('adds short taskStatus', done => { vm.taskStatus = '1 of 1'; setTimeout(() => { - expect( - document.querySelector('.issuable-meta #task_status_short').textContent.trim(), - ).toBe('1/1 task'); + expect(document.querySelector('.issuable-meta #task_status_short').textContent.trim()).toBe( + '1/1 task', + ); done(); }); }); - it('clears task status text when no tasks are present', (done) => { + it('clears task status text when no tasks are present', done => { vm.taskStatus = '0 of 0'; setTimeout(() => { - expect( - document.querySelector('.issuable-meta #task_status').textContent.trim(), - ).toBe(''); + expect(document.querySelector('.issuable-meta #task_status').textContent.trim()).toBe(''); done(); }); }); }); - it('applies syntax highlighting and math when description changed', (done) => { + it('applies syntax highlighting and math when description changed', done => { spyOn(vm, 'renderGFM').and.callThrough(); spyOn($.prototype, 'renderGFM').and.callThrough(); vm.descriptionHtml = 'changed'; diff --git a/spec/javascripts/issue_show/components/edit_actions_spec.js b/spec/javascripts/issue_show/components/edit_actions_spec.js index 004621f488a..d92c54ea83f 100644 --- a/spec/javascripts/issue_show/components/edit_actions_spec.js +++ b/spec/javascripts/issue_show/components/edit_actions_spec.js @@ -6,7 +6,7 @@ import Store from '~/issue_show/stores'; describe('Edit Actions components', () => { let vm; - beforeEach((done) => { + beforeEach(done => { const Component = Vue.extend(editActions); const store = new Store({ titleHtml: '', @@ -29,40 +29,32 @@ describe('Edit Actions components', () => { }); it('renders all buttons as enabled', () => { - expect( - vm.$el.querySelectorAll('.disabled').length, - ).toBe(0); + expect(vm.$el.querySelectorAll('.disabled').length).toBe(0); - expect( - vm.$el.querySelectorAll('[disabled]').length, - ).toBe(0); + expect(vm.$el.querySelectorAll('[disabled]').length).toBe(0); }); - it('does not render delete button if canUpdate is false', (done) => { + it('does not render delete button if canUpdate is false', done => { vm.canDestroy = false; Vue.nextTick(() => { - expect( - vm.$el.querySelector('.btn-danger'), - ).toBeNull(); + expect(vm.$el.querySelector('.btn-danger')).toBeNull(); done(); }); }); - it('disables submit button when title is blank', (done) => { + it('disables submit button when title is blank', done => { vm.formState.title = ''; Vue.nextTick(() => { - expect( - vm.$el.querySelector('.btn-success').getAttribute('disabled'), - ).toBe('disabled'); + expect(vm.$el.querySelector('.btn-success').getAttribute('disabled')).toBe('disabled'); done(); }); }); - it('should not show delete button if showDeleteButton is false', (done) => { + it('should not show delete button if showDeleteButton is false', done => { vm.showDeleteButton = false; Vue.nextTick(() => { @@ -75,30 +67,24 @@ describe('Edit Actions components', () => { it('sends update.issauble event when clicking save button', () => { vm.$el.querySelector('.btn-success').click(); - expect( - eventHub.$emit, - ).toHaveBeenCalledWith('update.issuable'); + expect(eventHub.$emit).toHaveBeenCalledWith('update.issuable'); }); - it('shows loading icon after clicking save button', (done) => { + it('shows loading icon after clicking save button', done => { vm.$el.querySelector('.btn-success').click(); Vue.nextTick(() => { - expect( - vm.$el.querySelector('.btn-success .fa'), - ).not.toBeNull(); + expect(vm.$el.querySelector('.btn-success .fa')).not.toBeNull(); done(); }); }); - it('disabled button after clicking save button', (done) => { + it('disabled button after clicking save button', done => { vm.$el.querySelector('.btn-success').click(); Vue.nextTick(() => { - expect( - vm.$el.querySelector('.btn-success').getAttribute('disabled'), - ).toBe('disabled'); + expect(vm.$el.querySelector('.btn-success').getAttribute('disabled')).toBe('disabled'); done(); }); @@ -109,9 +95,7 @@ describe('Edit Actions components', () => { it('emits close.form when clicking cancel', () => { vm.$el.querySelector('.btn-default').click(); - expect( - eventHub.$emit, - ).toHaveBeenCalledWith('close.form'); + expect(eventHub.$emit).toHaveBeenCalledWith('close.form'); }); }); @@ -120,35 +104,28 @@ describe('Edit Actions components', () => { spyOn(window, 'confirm').and.returnValue(true); vm.$el.querySelector('.btn-danger').click(); - expect( - eventHub.$emit, - ).toHaveBeenCalledWith('delete.issuable'); + expect(eventHub.$emit).toHaveBeenCalledWith('delete.issuable'); }); - it('shows loading icon after clicking delete button', (done) => { + it('shows loading icon after clicking delete button', done => { spyOn(window, 'confirm').and.returnValue(true); vm.$el.querySelector('.btn-danger').click(); Vue.nextTick(() => { - expect( - vm.$el.querySelector('.btn-danger .fa'), - ).not.toBeNull(); + expect(vm.$el.querySelector('.btn-danger .fa')).not.toBeNull(); done(); }); }); - it('does no actions when confirm is false', (done) => { + it('does no actions when confirm is false', done => { spyOn(window, 'confirm').and.returnValue(false); vm.$el.querySelector('.btn-danger').click(); Vue.nextTick(() => { - expect( - eventHub.$emit, - ).not.toHaveBeenCalledWith('delete.issuable'); - expect( - vm.$el.querySelector('.btn-danger .fa'), - ).toBeNull(); + expect(eventHub.$emit).not.toHaveBeenCalledWith('delete.issuable'); + + expect(vm.$el.querySelector('.btn-danger .fa')).toBeNull(); done(); }); diff --git a/spec/javascripts/issue_show/components/fields/description_spec.js b/spec/javascripts/issue_show/components/fields/description_spec.js index 299f88e7778..2c3efc8d4d4 100644 --- a/spec/javascripts/issue_show/components/fields/description_spec.js +++ b/spec/javascripts/issue_show/components/fields/description_spec.js @@ -8,7 +8,7 @@ describe('Description field component', () => { let vm; let store; - beforeEach((done) => { + beforeEach(done => { const Component = Vue.extend(descriptionField); const el = document.createElement('div'); store = new Store({ @@ -35,42 +35,32 @@ describe('Description field component', () => { }); it('renders markdown field with description', () => { - expect( - vm.$el.querySelector('.md-area textarea').value, - ).toBe('test'); + expect(vm.$el.querySelector('.md-area textarea').value).toBe('test'); }); - it('renders markdown field with a markdown description', (done) => { + it('renders markdown field with a markdown description', done => { store.formState.description = '**test**'; Vue.nextTick(() => { - expect( - vm.$el.querySelector('.md-area textarea').value, - ).toBe('**test**'); + expect(vm.$el.querySelector('.md-area textarea').value).toBe('**test**'); done(); }); }); it('focuses field when mounted', () => { - expect( - document.activeElement, - ).toBe(vm.$refs.textarea); + expect(document.activeElement).toBe(vm.$refs.textarea); }); it('triggers update with meta+enter', () => { vm.$el.querySelector('.md-area textarea').dispatchEvent(keyboardDownEvent(13, true)); - expect( - eventHub.$emit, - ).toHaveBeenCalled(); + expect(eventHub.$emit).toHaveBeenCalled(); }); it('triggers update with ctrl+enter', () => { vm.$el.querySelector('.md-area textarea').dispatchEvent(keyboardDownEvent(13, false, true)); - expect( - eventHub.$emit, - ).toHaveBeenCalled(); + expect(eventHub.$emit).toHaveBeenCalled(); }); }); diff --git a/spec/javascripts/issue_show/components/fields/description_template_spec.js b/spec/javascripts/issue_show/components/fields/description_template_spec.js index 30441faf844..8d77a620d76 100644 --- a/spec/javascripts/issue_show/components/fields/description_template_spec.js +++ b/spec/javascripts/issue_show/components/fields/description_template_spec.js @@ -5,7 +5,7 @@ describe('Issue description template component', () => { let vm; let formState; - beforeEach((done) => { + beforeEach(done => { const Component = Vue.extend(descriptionTemplate); formState = { description: 'test', @@ -24,24 +24,20 @@ describe('Issue description template component', () => { }); it('renders templates as JSON array in data attribute', () => { - expect( - vm.$el.querySelector('.js-issuable-selector').getAttribute('data-data'), - ).toBe('[{"name":"test"}]'); + expect(vm.$el.querySelector('.js-issuable-selector').getAttribute('data-data')).toBe( + '[{"name":"test"}]', + ); }); it('updates formState when changing template', () => { vm.issuableTemplate.editor.setValue('test new template'); - expect( - formState.description, - ).toBe('test new template'); + expect(formState.description).toBe('test new template'); }); it('returns formState description with editor getValue', () => { formState.description = 'testing new template'; - expect( - vm.issuableTemplate.editor.getValue(), - ).toBe('testing new template'); + expect(vm.issuableTemplate.editor.getValue()).toBe('testing new template'); }); }); diff --git a/spec/javascripts/issue_show/components/fields/title_spec.js b/spec/javascripts/issue_show/components/fields/title_spec.js index a03b462689f..4b96a1feb29 100644 --- a/spec/javascripts/issue_show/components/fields/title_spec.js +++ b/spec/javascripts/issue_show/components/fields/title_spec.js @@ -27,24 +27,18 @@ describe('Title field component', () => { }); it('renders form control with formState title', () => { - expect( - vm.$el.querySelector('.form-control').value, - ).toBe('test'); + expect(vm.$el.querySelector('.form-control').value).toBe('test'); }); it('triggers update with meta+enter', () => { vm.$el.querySelector('.form-control').dispatchEvent(keyboardDownEvent(13, true)); - expect( - eventHub.$emit, - ).toHaveBeenCalled(); + expect(eventHub.$emit).toHaveBeenCalled(); }); it('triggers update with ctrl+enter', () => { vm.$el.querySelector('.form-control').dispatchEvent(keyboardDownEvent(13, false, true)); - expect( - eventHub.$emit, - ).toHaveBeenCalled(); + expect(eventHub.$emit).toHaveBeenCalled(); }); }); diff --git a/spec/javascripts/issue_show/components/form_spec.js b/spec/javascripts/issue_show/components/form_spec.js index eaac1e3536d..523954013cf 100644 --- a/spec/javascripts/issue_show/components/form_spec.js +++ b/spec/javascripts/issue_show/components/form_spec.js @@ -4,7 +4,7 @@ import formComponent from '~/issue_show/components/form.vue'; describe('Inline edit form component', () => { let vm; - beforeEach((done) => { + beforeEach(done => { const Component = Vue.extend(formComponent); vm = new Component({ @@ -27,36 +27,28 @@ describe('Inline edit form component', () => { }); it('does not render template selector if no templates exist', () => { - expect( - vm.$el.querySelector('.js-issuable-selector-wrap'), - ).toBeNull(); + expect(vm.$el.querySelector('.js-issuable-selector-wrap')).toBeNull(); }); - it('renders template selector when templates exists', (done) => { + it('renders template selector when templates exists', done => { vm.issuableTemplates = ['test']; Vue.nextTick(() => { - expect( - vm.$el.querySelector('.js-issuable-selector-wrap'), - ).not.toBeNull(); + expect(vm.$el.querySelector('.js-issuable-selector-wrap')).not.toBeNull(); done(); }); }); it('hides locked warning by default', () => { - expect( - vm.$el.querySelector('.alert'), - ).toBeNull(); + expect(vm.$el.querySelector('.alert')).toBeNull(); }); - it('shows locked warning if formState is different', (done) => { + it('shows locked warning if formState is different', done => { vm.formState.lockedWarningVisible = true; Vue.nextTick(() => { - expect( - vm.$el.querySelector('.alert'), - ).not.toBeNull(); + expect(vm.$el.querySelector('.alert')).not.toBeNull(); done(); }); diff --git a/spec/javascripts/issue_show/components/title_spec.js b/spec/javascripts/issue_show/components/title_spec.js index 5370f4e1fea..9e6f236b687 100644 --- a/spec/javascripts/issue_show/components/title_spec.js +++ b/spec/javascripts/issue_show/components/title_spec.js @@ -86,12 +86,14 @@ describe('Title component', () => { it('should not show if canUpdate is false', () => { vm.showInlineEditButton = true; vm.canUpdate = false; + expect(vm.$el.querySelector('.btn-edit')).toBeNull(); }); it('should show if showInlineEditButton and canUpdate', () => { vm.showInlineEditButton = true; vm.canUpdate = true; + expect(vm.$el.querySelector('.btn-edit')).toBeDefined(); }); @@ -101,6 +103,7 @@ describe('Title component', () => { Vue.nextTick(() => { vm.$el.querySelector('.btn-edit').click(); + expect(eventHub.$emit).toHaveBeenCalledWith('open.form'); }); }); diff --git a/spec/javascripts/issue_spec.js b/spec/javascripts/issue_spec.js index 62c71e00334..7be495d1d35 100644 --- a/spec/javascripts/issue_spec.js +++ b/spec/javascripts/issue_spec.js @@ -15,6 +15,7 @@ describe('Issue', function() { function expectErrorMessage() { const $flashMessage = $('div.flash-alert'); + expect($flashMessage).toExist(); expect($flashMessage).toBeVisible(); expect($flashMessage).toHaveText('Unable to update this issue at this time.'); @@ -23,6 +24,7 @@ describe('Issue', function() { function expectIssueState(isIssueOpen) { expectVisibility($boxClosed, !isIssueOpen); expectVisibility($boxOpen, isIssueOpen); + expect($btn).toHaveText(isIssueOpen ? 'Close issue' : 'Reopen issue'); } @@ -32,6 +34,7 @@ describe('Issue', function() { } const $available = Issue.$btnNewBranch.find('.available'); + expect($available).toHaveText('New branch'); if (!isPending && canCreate) { @@ -41,6 +44,7 @@ describe('Issue', function() { } const $unavailable = Issue.$btnNewBranch.find('.unavailable'); + expect($unavailable).toHaveText('New branch unavailable'); if (!isPending && !canCreate) { @@ -60,19 +64,22 @@ describe('Issue', function() { function findElements(isIssueInitiallyOpen) { $boxClosed = $('div.status-box-issue-closed'); + expect($boxClosed).toExist(); expect($boxClosed).toHaveText('Closed'); $boxOpen = $('div.status-box-open'); + expect($boxOpen).toExist(); expect($boxOpen).toHaveText('Open'); $btn = $('.js-issuable-close-button'); + expect($btn).toExist(); expect($btn).toHaveText(isIssueInitiallyOpen ? 'Close issue' : 'Reopen issue'); } - [true, false].forEach((isIssueInitiallyOpen) => { + [true, false].forEach(isIssueInitiallyOpen => { describe(`with ${isIssueInitiallyOpen ? 'open' : 'closed'} issue`, function() { const action = isIssueInitiallyOpen ? 'close' : 'reopen'; let mock; @@ -127,7 +134,7 @@ describe('Issue', function() { it(`${action}s the issue`, function(done) { mockCloseButtonResponseSuccess(this.$triggeredButton.attr('href'), { - id: 34 + id: 34, }); mockCanCreateBranch(!isIssueInitiallyOpen); @@ -135,6 +142,7 @@ describe('Issue', function() { setTimeout(() => { expectIssueState(!isIssueInitiallyOpen); + expect(this.$triggeredButton.get(0).getAttribute('disabled')).toBeNull(); expect(this.$projectIssuesCounter.text()).toBe(isIssueInitiallyOpen ? '1,000' : '1,002'); expectNewBranchButtonState(false, !isIssueInitiallyOpen); @@ -145,7 +153,7 @@ describe('Issue', function() { it(`fails to ${action} the issue if saved:false`, function(done) { mockCloseButtonResponseSuccess(this.$triggeredButton.attr('href'), { - saved: false + saved: false, }); mockCanCreateBranch(isIssueInitiallyOpen); @@ -153,8 +161,10 @@ describe('Issue', function() { setTimeout(() => { expectIssueState(isIssueInitiallyOpen); + expect(this.$triggeredButton.get(0).getAttribute('disabled')).toBeNull(); expectErrorMessage(); + expect(this.$projectIssuesCounter.text()).toBe('1,001'); expectNewBranchButtonState(false, isIssueInitiallyOpen); @@ -170,8 +180,10 @@ describe('Issue', function() { setTimeout(() => { expectIssueState(isIssueInitiallyOpen); + expect(this.$triggeredButton.get(0).getAttribute('disabled')).toBeNull(); expectErrorMessage(); + expect(this.$projectIssuesCounter.text()).toBe('1,001'); expectNewBranchButtonState(false, isIssueInitiallyOpen); diff --git a/spec/javascripts/job_spec.js b/spec/javascripts/job_spec.js index d6b5dec9e47..bc973407b25 100644 --- a/spec/javascripts/job_spec.js +++ b/spec/javascripts/job_spec.js @@ -1,295 +1,265 @@ -import $ from 'jquery'; -import MockAdapter from 'axios-mock-adapter'; -import axios from '~/lib/utils/axios_utils'; -import { numberToHumanSize } from '~/lib/utils/number_utils'; -import '~/lib/utils/datetime_utility'; -import Job from '~/job'; -import '~/breakpoints'; -import waitForPromises from 'spec/helpers/wait_for_promises'; - -describe('Job', () => { - const JOB_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/-/jobs/1`; - let mock; - let response; - let job; - - preloadFixtures('builds/build-with-artifacts.html.raw'); - - beforeEach(() => { - loadFixtures('builds/build-with-artifacts.html.raw'); - - spyOnDependency(Job, 'visitUrl'); - - response = {}; - - mock = new MockAdapter(axios); - - mock.onGet(new RegExp(`${JOB_URL}/trace.json?(.*)`)).reply(() => [200, response]); - }); - - afterEach(() => { - mock.restore(); - - clearTimeout(job.timeout); - }); - - describe('class constructor', () => { - beforeEach(() => { - jasmine.clock().install(); - }); - - afterEach(() => { - jasmine.clock().uninstall(); - }); - - describe('setup', () => { - beforeEach(function (done) { - job = new Job(); - - waitForPromises() - .then(done) - .catch(done.fail); - }); - - it('copies build options', function () { - expect(job.pagePath).toBe(JOB_URL); - expect(job.buildStatus).toBe('success'); - expect(job.buildStage).toBe('test'); - expect(job.state).toBe(''); - }); - }); - - describe('running build', () => { - it('updates the build trace on an interval', function (done) { - response = { - html: '<span>Update<span>', - status: 'running', - state: 'newstate', - append: true, - complete: false, - }; - - job = new Job(); - - waitForPromises() - .then(() => { - expect($('#build-trace .js-build-output').text()).toMatch(/Update/); - expect(job.state).toBe('newstate'); - - response = { - html: '<span>More</span>', - status: 'running', - state: 'finalstate', - append: true, - complete: true, - }; - }) - .then(() => jasmine.clock().tick(4001)) - .then(waitForPromises) - .then(() => { - expect($('#build-trace .js-build-output').text()).toMatch(/UpdateMore/); - expect(job.state).toBe('finalstate'); - }) - .then(done) - .catch(done.fail); - }); - - it('replaces the entire build trace', (done) => { - response = { - html: '<span>Update<span>', - status: 'running', - append: false, - complete: false, - }; - - job = new Job(); - - waitForPromises() - .then(() => { - expect($('#build-trace .js-build-output').text()).toMatch(/Update/); - - response = { - html: '<span>Different</span>', - status: 'running', - append: false, - }; - }) - .then(() => jasmine.clock().tick(4001)) - .then(waitForPromises) - .then(() => { - expect($('#build-trace .js-build-output').text()).not.toMatch(/Update/); - expect($('#build-trace .js-build-output').text()).toMatch(/Different/); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('truncated information', () => { - describe('when size is less than total', () => { - it('shows information about truncated log', (done) => { - response = { - html: '<span>Update</span>', - status: 'success', - append: false, - size: 50, - total: 100, - }; - - job = new Job(); - - waitForPromises() - .then(() => { - expect(document.querySelector('.js-truncated-info').classList).not.toContain('hidden'); - }) - .then(done) - .catch(done.fail); - }); - - it('shows the size in KiB', (done) => { - const size = 50; - - response = { - html: '<span>Update</span>', - status: 'success', - append: false, - size, - total: 100, - }; - - job = new Job(); - - waitForPromises() - .then(() => { - expect( - document.querySelector('.js-truncated-info-size').textContent.trim(), - ).toEqual(`${numberToHumanSize(size)}`); - }) - .then(done) - .catch(done.fail); - }); - - it('shows incremented size', (done) => { - response = { - html: '<span>Update</span>', - status: 'success', - append: false, - size: 50, - total: 100, - complete: false, - }; - - job = new Job(); - - waitForPromises() - .then(() => { - expect( - document.querySelector('.js-truncated-info-size').textContent.trim(), - ).toEqual(`${numberToHumanSize(50)}`); - - response = { - html: '<span>Update</span>', - status: 'success', - append: true, - size: 10, - total: 100, - complete: true, - }; - }) - .then(() => jasmine.clock().tick(4001)) - .then(waitForPromises) - .then(() => { - expect( - document.querySelector('.js-truncated-info-size').textContent.trim(), - ).toEqual(`${numberToHumanSize(60)}`); - }) - .then(done) - .catch(done.fail); - }); - - it('renders the raw link', () => { - response = { - html: '<span>Update</span>', - status: 'success', - append: false, - size: 50, - total: 100, - }; - - job = new Job(); - - expect( - document.querySelector('.js-raw-link').textContent.trim(), - ).toContain('Complete Raw'); - }); - }); - - describe('when size is equal than total', () => { - it('does not show the trunctated information', (done) => { - response = { - html: '<span>Update</span>', - status: 'success', - append: false, - size: 100, - total: 100, - }; - - job = new Job(); - - waitForPromises() - .then(() => { - expect(document.querySelector('.js-truncated-info').classList).toContain('hidden'); - }) - .then(done) - .catch(done.fail); - }); - }); - }); - - describe('output trace', () => { - beforeEach((done) => { - response = { - html: '<span>Update</span>', - status: 'success', - append: false, - size: 50, - total: 100, - }; - - job = new Job(); - - waitForPromises() - .then(done) - .catch(done.fail); - }); - - it('should render trace controls', () => { - const controllers = document.querySelector('.controllers'); - - expect(controllers.querySelector('.js-raw-link-controller')).not.toBeNull(); - expect(controllers.querySelector('.js-scroll-up')).not.toBeNull(); - expect(controllers.querySelector('.js-scroll-down')).not.toBeNull(); - }); - - it('should render received output', () => { - expect( - document.querySelector('.js-build-output').innerHTML, - ).toEqual('<span>Update</span>'); - }); - }); - }); - - describe('getBuildTrace', () => { - it('should request build trace with state parameter', (done) => { - spyOn(axios, 'get').and.callThrough(); - job = new Job(); - - setTimeout(() => { - expect(axios.get).toHaveBeenCalledWith( - `${JOB_URL}/trace.json`, { params: { state: '' } }, - ); - done(); - }, 0); - }); - }); -}); +// import $ from 'jquery'; +// import MockAdapter from 'axios-mock-adapter'; +// import axios from '~/lib/utils/axios_utils'; +// import { numberToHumanSize } from '~/lib/utils/number_utils'; +// import '~/lib/utils/datetime_utility'; +// import Job from '~/job'; +// import '~/breakpoints'; +// import waitForPromises from 'spec/helpers/wait_for_promises'; + +// describe('Job', () => { +// const JOB_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/-/jobs/1`; +// let mock; +// let response; +// let job; + +// preloadFixtures('builds/build-with-artifacts.html.raw'); + +// beforeEach(() => { +// loadFixtures('builds/build-with-artifacts.html.raw'); + +// spyOnDependency(Job, 'visitUrl'); + +// response = {}; + +// mock = new MockAdapter(axios); + +// mock.onGet(new RegExp(`${JOB_URL}/trace.json?(.*)`)).reply(() => [200, response]); +// }); + +// afterEach(() => { +// mock.restore(); + +// clearTimeout(job.timeout); +// }); + +// describe('class constructor', () => { +// beforeEach(() => { +// jasmine.clock().install(); +// }); + +// afterEach(() => { +// jasmine.clock().uninstall(); +// }); + +// describe('running build', () => { +// it('updates the build trace on an interval', function (done) { +// response = { +// html: '<span>Update<span>', +// status: 'running', +// state: 'newstate', +// append: true, +// complete: false, +// }; + +// job = new Job(); + +// waitForPromises() +// .then(() => { +// expect($('#build-trace .js-build-output').text()).toMatch(/Update/); +// expect(job.state).toBe('newstate'); + +// response = { +// html: '<span>More</span>', +// status: 'running', +// state: 'finalstate', +// append: true, +// complete: true, +// }; +// }) +// .then(() => jasmine.clock().tick(4001)) +// .then(waitForPromises) +// .then(() => { +// expect($('#build-trace .js-build-output').text()).toMatch(/UpdateMore/); +// expect(job.state).toBe('finalstate'); +// }) +// .then(done) +// .catch(done.fail); +// }); + +// it('replaces the entire build trace', (done) => { +// response = { +// html: '<span>Update<span>', +// status: 'running', +// append: false, +// complete: false, +// }; + +// job = new Job(); + +// waitForPromises() +// .then(() => { +// expect($('#build-trace .js-build-output').text()).toMatch(/Update/); + +// response = { +// html: '<span>Different</span>', +// status: 'running', +// append: false, +// }; +// }) +// .then(() => jasmine.clock().tick(4001)) +// .then(waitForPromises) +// .then(() => { +// expect($('#build-trace .js-build-output').text()).not.toMatch(/Update/); +// expect($('#build-trace .js-build-output').text()).toMatch(/Different/); +// }) +// .then(done) +// .catch(done.fail); +// }); +// }); + +// describe('truncated information', () => { +// describe('when size is less than total', () => { +// it('shows information about truncated log', (done) => { +// response = { +// html: '<span>Update</span>', +// status: 'success', +// append: false, +// size: 50, +// total: 100, +// }; + +// job = new Job(); + +// waitForPromises() +// .then(() => { +// expect(document.querySelector('.js-truncated-info').classList).not.toContain('hidden'); +// }) +// .then(done) +// .catch(done.fail); +// }); + +// it('shows the size in KiB', (done) => { +// const size = 50; + +// response = { +// html: '<span>Update</span>', +// status: 'success', +// append: false, +// size, +// total: 100, +// }; + +// job = new Job(); + +// waitForPromises() +// .then(() => { +// expect( +// document.querySelector('.js-truncated-info-size').textContent.trim(), +// ).toEqual(`${numberToHumanSize(size)}`); +// }) +// .then(done) +// .catch(done.fail); +// }); + +// it('shows incremented size', (done) => { +// response = { +// html: '<span>Update</span>', +// status: 'success', +// append: false, +// size: 50, +// total: 100, +// complete: false, +// }; + +// job = new Job(); + +// waitForPromises() +// .then(() => { +// expect( +// document.querySelector('.js-truncated-info-size').textContent.trim(), +// ).toEqual(`${numberToHumanSize(50)}`); + +// response = { +// html: '<span>Update</span>', +// status: 'success', +// append: true, +// size: 10, +// total: 100, +// complete: true, +// }; +// }) +// .then(() => jasmine.clock().tick(4001)) +// .then(waitForPromises) +// .then(() => { +// expect( +// document.querySelector('.js-truncated-info-size').textContent.trim(), +// ).toEqual(`${numberToHumanSize(60)}`); +// }) +// .then(done) +// .catch(done.fail); +// }); + +// it('renders the raw link', () => { +// response = { +// html: '<span>Update</span>', +// status: 'success', +// append: false, +// size: 50, +// total: 100, +// }; + +// job = new Job(); + +// expect( +// document.querySelector('.js-raw-link').textContent.trim(), +// ).toContain('Complete Raw'); +// }); +// }); + +// describe('when size is equal than total', () => { +// it('does not show the trunctated information', (done) => { +// response = { +// html: '<span>Update</span>', +// status: 'success', +// append: false, +// size: 100, +// total: 100, +// }; + +// job = new Job(); + +// waitForPromises() +// .then(() => { +// expect(document.querySelector('.js-truncated-info').classList).toContain('hidden'); +// }) +// .then(done) +// .catch(done.fail); +// }); +// }); +// }); + +// describe('output trace', () => { +// beforeEach((done) => { +// response = { +// html: '<span>Update</span>', +// status: 'success', +// append: false, +// size: 50, +// total: 100, +// }; + +// job = new Job(); + +// waitForPromises() +// .then(done) +// .catch(done.fail); +// }); + +// it('should render trace controls', () => { +// const controllers = document.querySelector('.controllers'); + +// expect(controllers.querySelector('.js-raw-link-controller')).not.toBeNull(); +// expect(controllers.querySelector('.js-scroll-up')).not.toBeNull(); +// expect(controllers.querySelector('.js-scroll-down')).not.toBeNull(); +// }); + +// it('should render received output', () => { +// expect( +// document.querySelector('.js-build-output').innerHTML, +// ).toEqual('<span>Update</span>'); +// }); +// }); +// }); + +// }); diff --git a/spec/javascripts/jobs/components/commit_block_spec.js b/spec/javascripts/jobs/components/commit_block_spec.js index 0bcc4ff940f..98eba3ac976 100644 --- a/spec/javascripts/jobs/components/commit_block_spec.js +++ b/spec/javascripts/jobs/components/commit_block_spec.js @@ -34,6 +34,7 @@ describe('Commit block', () => { expect(vm.$el.querySelector('.js-commit-sha').getAttribute('href')).toEqual( props.commit.commit_path, ); + expect(vm.$el.querySelector('.js-commit-sha').textContent.trim()).toEqual( props.commit.short_id, ); @@ -55,6 +56,7 @@ describe('Commit block', () => { expect(vm.$el.querySelector('.js-link-commit').getAttribute('href')).toEqual( props.mergeRequest.path, ); + expect(vm.$el.querySelector('.js-link-commit').textContent.trim()).toEqual( `!${props.mergeRequest.iid}`, ); diff --git a/spec/javascripts/jobs/components/empty_state_spec.js b/spec/javascripts/jobs/components/empty_state_spec.js index 73488eaab9b..0a39709221c 100644 --- a/spec/javascripts/jobs/components/empty_state_spec.js +++ b/spec/javascripts/jobs/components/empty_state_spec.js @@ -56,6 +56,7 @@ describe('Empty State', () => { vm = mountComponent(Component, { ...props, }); + expect(vm.$el.querySelector('.js-job-empty-state-content')).toBeNull(); }); }); @@ -84,6 +85,7 @@ describe('Empty State', () => { ...props, content, }); + expect(vm.$el.querySelector('.js-job-empty-state-action')).toBeNull(); }); }); diff --git a/spec/javascripts/jobs/components/environments_block_spec.js b/spec/javascripts/jobs/components/environments_block_spec.js index 7d836129b13..0866ddd21d8 100644 --- a/spec/javascripts/jobs/components/environments_block_spec.js +++ b/spec/javascripts/jobs/components/environments_block_spec.js @@ -54,6 +54,7 @@ describe('Environments block', () => { expect(vm.$el.textContent.trim()).toEqual( 'This job is an out-of-date deployment to environment. View the most recent deployment #deployment.', ); + expect(vm.$el.querySelector('.js-job-deployment-link').getAttribute('href')).toEqual('bar'); }); }); @@ -110,6 +111,7 @@ describe('Environments block', () => { expect(vm.$el.textContent.trim()).toEqual( 'This job is creating a deployment to environment and will overwrite the latest deployment.', ); + expect(vm.$el.querySelector('.js-job-deployment-link').getAttribute('href')).toEqual('foo'); }); }); @@ -139,6 +141,7 @@ describe('Environments block', () => { }, iconStatus: status, }); + expect(vm.$el.querySelector('.js-environment-link')).toBeNull(); }); }); diff --git a/spec/javascripts/jobs/components/job_app_spec.js b/spec/javascripts/jobs/components/job_app_spec.js index 6e0bcf801cd..e6d403dc826 100644 --- a/spec/javascripts/jobs/components/job_app_spec.js +++ b/spec/javascripts/jobs/components/job_app_spec.js @@ -1,75 +1,196 @@ import Vue from 'vue'; +import MockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; import jobApp from '~/jobs/components/job_app.vue'; import createStore from '~/jobs/store'; import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { resetStore } from '../store/helpers'; +import job from '../mock_data'; describe('Job App ', () => { const Component = Vue.extend(jobApp); let store; let vm; - - const threeWeeksAgo = new Date(); - threeWeeksAgo.setDate(threeWeeksAgo.getDate() - 21); - - const twoDaysAgo = new Date(); - twoDaysAgo.setDate(twoDaysAgo.getDate() - 2); - - const job = { - status: { - group: 'failed', - icon: 'status_failed', - label: 'failed', - text: 'failed', - details_path: 'path', - }, - id: 123, - created_at: threeWeeksAgo.toISOString(), - user: { - web_url: 'path', - name: 'Foo', - username: 'foobar', - email: 'foo@bar.com', - avatar_url: 'link', - }, - started: twoDaysAgo.toISOString(), - new_issue_path: 'path', - runners: { - available: false, - }, - tags: ['docker'], - has_trace: true, - }; + let mock; const props = { + endpoint: `${gl.TEST_HOST}jobs/123.json`, + runnerHelpUrl: 'help/runner', runnerSettingsUrl: 'settings/ci-cd/runners', + terminalPath: 'jobs/123/terminal', + pagePath: `${gl.TEST_HOST}jobs/123`, + logState: + 'eyJvZmZzZXQiOjE3NDUxLCJuX29wZW5fdGFncyI6MCwiZmdfY29sb3IiOm51bGwsImJnX2NvbG9yIjpudWxsLCJzdHlsZV9tYXNrIjowfQ%3D%3D', }; beforeEach(() => { + mock = new MockAdapter(axios); store = createStore(); }); afterEach(() => { + resetStore(store); vm.$destroy(); + mock.restore(); }); - describe('Header section', () => { - describe('job callout message', () => { - it('should not render the reason when reason is absent', () => { - store.dispatch('receiveJobSuccess', job); + describe('while loading', () => { + beforeEach(() => { + mock.onGet(props.endpoint).reply(200, job, {}); + mock.onGet(`${props.pagePath}/trace.json`).reply(200, {}); + vm = mountComponentWithStore(Component, { props, store }); + }); + + it('renders loading icon', done => { + expect(vm.$el.querySelector('.js-job-loading')).not.toBeNull(); + expect(vm.$el.querySelector('.js-job-sidebar')).toBeNull(); + expect(vm.$el.querySelector('.js-job-content')).toBeNull(); + + setTimeout(() => { + done(); + }, 0); + }); + }); + + describe('with successfull request', () => { + beforeEach(() => { + mock.onGet(`${props.pagePath}/trace.json`).replyOnce(200, {}); + }); + + describe('Header section', () => { + describe('job callout message', () => { + it('should not render the reason when reason is absent', done => { + mock.onGet(props.endpoint).replyOnce(200, job); + vm = mountComponentWithStore(Component, { props, store }); + + setTimeout(() => { + expect(vm.shouldRenderCalloutMessage).toBe(false); + + done(); + }, 0); + }); + + it('should render the reason when reason is present', done => { + mock.onGet(props.endpoint).replyOnce( + 200, + Object.assign({}, job, { + callout_message: 'There is an unknown failure, please try again', + }), + ); + + vm = mountComponentWithStore(Component, { props, store }); + setTimeout(() => { + expect(vm.shouldRenderCalloutMessage).toBe(true); + done(); + }, 0); + }); + }); + + describe('triggered job', () => { + beforeEach(() => { + mock.onGet(props.endpoint).replyOnce(200, Object.assign({}, job, { started: '2017-05-24T10:59:52.000+01:00' })); + vm = mountComponentWithStore(Component, { props, store }); + }); + + it('should render provided job information', done => { + setTimeout(() => { + expect( + vm.$el + .querySelector('.header-main-content') + .textContent.replace(/\s+/g, ' ') + .trim(), + ).toEqual('passed Job #4757 triggered 1 year ago by Root'); + done(); + }, 0); + }); + + it('should render new issue link', done => { + setTimeout(() => { + expect(vm.$el.querySelector('.js-new-issue').getAttribute('href')).toEqual( + job.new_issue_path, + ); + done(); + }, 0); + }); + }); + + describe('created job', () => { + it('should render created key', done => { + mock.onGet(props.endpoint).replyOnce(200, job); + vm = mountComponentWithStore(Component, { props, store }); + + setTimeout(() => { + expect( + vm.$el + .querySelector('.header-main-content') + .textContent.replace(/\s+/g, ' ') + .trim(), + ).toEqual('passed Job #4757 created 3 weeks ago by Root'); + done(); + }, 0); + }); + }); + }); + + describe('stuck block', () => { + it('renders stuck block when there are no runners', done => { + mock.onGet(props.endpoint).replyOnce( + 200, + Object.assign({}, job, { + status: { + group: 'pending', + icon: 'status_pending', + label: 'pending', + text: 'pending', + details_path: 'path', + }, + runners: { + available: false, + }, + }), + ); + vm = mountComponentWithStore(Component, { props, store }); + + setTimeout(() => { + expect(vm.$el.querySelector('.js-job-stuck')).not.toBeNull(); + + done(); + }, 0); + }); + + it('renders tags in stuck block when there are no runners', done => { + mock.onGet(props.endpoint).replyOnce( + 200, + Object.assign({}, job, { + status: { + group: 'pending', + icon: 'status_pending', + label: 'pending', + text: 'pending', + details_path: 'path', + }, + runners: { + available: false, + }, + }), + ); vm = mountComponentWithStore(Component, { props, store, }); - expect(vm.shouldRenderCalloutMessage).toBe(false); + setTimeout(() => { + expect(vm.$el.querySelector('.js-job-stuck').textContent).toContain(job.tags[0]); + done(); + }, 0); }); - it('should render the reason when reason is present', () => { - store.dispatch( - 'receiveJobSuccess', + it('does not renders stuck block when there are no runners', done => { + mock.onGet(props.endpoint).replyOnce( + 200, Object.assign({}, job, { - callout_message: 'There is an unknown failure, please try again', + runners: { available: true }, }), ); @@ -78,246 +199,324 @@ describe('Job App ', () => { store, }); - expect(vm.shouldRenderCalloutMessage).toBe(true); + setTimeout(() => { + expect(vm.$el.querySelector('.js-job-stuck')).toBeNull(); + + done(); + }, 0); }); }); - describe('triggered job', () => { - beforeEach(() => { - store.dispatch('receiveJobSuccess', job); + describe('environments block', () => { + it('renders environment block when job has environment', done => { + mock.onGet(props.endpoint).replyOnce( + 200, + Object.assign({}, job, { + deployment_status: { + environment: { + environment_path: '/path', + name: 'foo', + }, + }, + }), + ); vm = mountComponentWithStore(Component, { props, store, }); - }); - it('should render provided job information', () => { - expect( - vm.$el - .querySelector('.header-main-content') - .textContent.replace(/\s+/g, ' ') - .trim(), - ).toEqual('failed Job #123 triggered 2 days ago by Foo'); - }); + setTimeout(() => { + expect(vm.$el.querySelector('.js-job-environment')).not.toBeNull(); - it('should render new issue link', () => { - expect(vm.$el.querySelector('.js-new-issue').getAttribute('href')).toEqual( - job.new_issue_path, - ); + done(); + }, 0); }); - }); - describe('created job', () => { - it('should render created key', () => { - store.dispatch('receiveJobSuccess', Object.assign({}, job, { started: false })); + it('does not render environment block when job has environment', done => { + mock.onGet(props.endpoint).replyOnce(200, job); vm = mountComponentWithStore(Component, { props, store, }); - expect( - vm.$el - .querySelector('.header-main-content') - .textContent.replace(/\s+/g, ' ') - .trim(), - ).toEqual('failed Job #123 created 3 weeks ago by Foo'); + setTimeout(() => { + expect(vm.$el.querySelector('.js-job-environment')).toBeNull(); + done(); + }, 0); }); }); - }); - describe('stuck block', () => { - it('renders stuck block when there are no runners', () => { - store.dispatch( - 'receiveJobSuccess', - Object.assign({}, job, { - status: { - group: 'pending', - icon: 'status_pending', - label: 'pending', - text: 'pending', - details_path: 'path', - }, - }), - ); - - vm = mountComponentWithStore(Component, { - props, - store, - }); + describe('erased block', () => { + it('renders erased block when `erased` is true', done => { + mock.onGet(props.endpoint).replyOnce( + 200, + Object.assign({}, job, { + erased_by: { + username: 'root', + web_url: 'gitlab.com/root', + }, + erased_at: '2016-11-07T11:11:16.525Z', + }), + ); - expect(vm.$el.querySelector('.js-job-stuck')).not.toBeNull(); - }); + vm = mountComponentWithStore(Component, { + props, + store, + }); - it('renders tags in stuck block when there are no runners', () => { - store.dispatch( - 'receiveJobSuccess', - Object.assign({}, job, { - status: { - group: 'pending', - icon: 'status_pending', - label: 'pending', - text: 'pending', - details_path: 'path', - }, - }), - ); - - vm = mountComponentWithStore(Component, { - props, - store, + setTimeout(() => { + expect(vm.$el.querySelector('.js-job-erased-block')).not.toBeNull(); + + done(); + }, 0); }); - expect(vm.$el.querySelector('.js-job-stuck').textContent).toContain(job.tags[0]); - }); + it('does not render erased block when `erased` is false', done => { + mock.onGet(props.endpoint).replyOnce(200, Object.assign({}, job, { erased_at: null })); - it(' does not renders stuck block when there are no runners', () => { - store.dispatch('receiveJobSuccess', Object.assign({}, job, { runners: { available: true } })); + vm = mountComponentWithStore(Component, { + props, + store, + }); - vm = mountComponentWithStore(Component, { - props, - store, - }); + setTimeout(() => { + expect(vm.$el.querySelector('.js-job-erased-block')).toBeNull(); - expect(vm.$el.querySelector('.js-job-stuck')).toBeNull(); + done(); + }, 0); + }); }); - }); - describe('environments block', () => { - it('renders environment block when job has environment', () => { - store.dispatch( - 'receiveJobSuccess', - Object.assign({}, job, { - deployment_status: { - environment: { - environment_path: '/path', - name: 'foo', + describe('empty states block', () => { + it('renders empty state when job does not have trace and is not running', done => { + mock.onGet(props.endpoint).replyOnce( + 200, + Object.assign({}, job, { + has_trace: false, + status: { + group: 'pending', + icon: 'status_pending', + label: 'pending', + text: 'pending', + details_path: 'path', + illustration: { + image: 'path', + size: '340', + title: 'Empty State', + content: 'This is an empty state', + }, + action: { + button_title: 'Retry job', + method: 'post', + path: '/path', + }, }, - }, - }), - ); + }), + ); - vm = mountComponentWithStore(Component, { - props, - store, + vm = mountComponentWithStore(Component, { + props, + store, + }); + + setTimeout(() => { + expect(vm.$el.querySelector('.js-job-empty-state')).not.toBeNull(); + + done(); + }, 0); }); - expect(vm.$el.querySelector('.js-job-environment')).not.toBeNull(); - }); + it('does not render empty state when job does not have trace but it is running', done => { + mock.onGet(props.endpoint).replyOnce( + 200, + Object.assign({}, job, { + has_trace: false, + status: { + group: 'running', + icon: 'status_running', + label: 'running', + text: 'running', + details_path: 'path', + }, + }), + ); + + vm = mountComponentWithStore(Component, { + props, + store, + }); - it('does not render environment block when job has environment', () => { - store.dispatch('receiveJobSuccess', job); + setTimeout(() => { + expect(vm.$el.querySelector('.js-job-empty-state')).toBeNull(); - vm = mountComponentWithStore(Component, { - props, - store, + done(); + }, 0); }); - expect(vm.$el.querySelector('.js-job-environment')).toBeNull(); - }); - }); + it('does not render empty state when job has trace but it is not running', done => { + mock.onGet(props.endpoint).replyOnce(200, Object.assign({}, job, { has_trace: true })); + + vm = mountComponentWithStore(Component, { + props, + store, + }); - describe('erased block', () => { - it('renders erased block when `erased` is true', () => { - store.dispatch( - 'receiveJobSuccess', - Object.assign({}, job, { - erased_by: { - username: 'root', - web_url: 'gitlab.com/root', - }, - erased_at: '2016-11-07T11:11:16.525Z', - }), - ); - - vm = mountComponentWithStore(Component, { - props, - store, + setTimeout(() => { + expect(vm.$el.querySelector('.js-job-empty-state')).toBeNull(); + + done(); + }, 0); }); + }); + }); - expect(vm.$el.querySelector('.js-job-erased-block')).not.toBeNull(); + describe('trace output', () => { + beforeEach(() => { + mock.onGet(props.endpoint).reply(200, job, {}); }); - it('does not render erased block when `erased` is false', () => { - store.dispatch('receiveJobSuccess', Object.assign({}, job, { erased_at: null })); + describe('with append flag', () => { + it('appends the log content to the existing one', done => { + mock.onGet(`${props.pagePath}/trace.json`).reply(200, { + html: '<span>More<span>', + status: 'running', + state: 'newstate', + append: true, + complete: true, + }); - vm = mountComponentWithStore(Component, { - props, - store, - }); + vm = mountComponentWithStore(Component, { + props, + store, + }); - expect(vm.$el.querySelector('.js-job-erased-block')).toBeNull(); - }); - }); + vm.$store.state.trace = 'Update'; - describe('empty states block', () => { - it('renders empty state when job does not have trace and is not running', () => { - store.dispatch( - 'receiveJobSuccess', - Object.assign({}, job, { - has_trace: false, - status: { - group: 'pending', - icon: 'status_pending', - label: 'pending', - text: 'pending', - details_path: 'path', - illustration: { - image: 'path', - size: '340', - title: 'Empty State', - content: 'This is an empty state', - }, - action: { - button_title: 'Retry job', - method: 'post', - path: '/path', - }, - }, - }), - ); + setTimeout(() => { + expect(vm.$el.querySelector('.js-build-trace').textContent.trim()).toContain('Update'); - vm = mountComponentWithStore(Component, { - props, - store, + done(); + }, 0); }); + }); + + describe('without append flag', () => { + it('replaces the trace', done => { + mock.onGet(`${props.pagePath}/trace.json`).reply(200, { + html: '<span>Different<span>', + status: 'running', + append: false, + complete: true, + }); - expect(vm.$el.querySelector('.js-job-empty-state')).not.toBeNull(); + vm = mountComponentWithStore(Component, { + props, + store, + }); + vm.$store.state.trace = 'Update'; + + setTimeout(() => { + expect(vm.$el.querySelector('.js-build-trace').textContent.trim()).not.toContain('Update'); + expect(vm.$el.querySelector('.js-build-trace').textContent.trim()).toContain( + 'Different', + ); + done(); + }, 0); + }); }); - it('does not render empty state when job does not have trace but it is running', () => { - store.dispatch( - 'receiveJobSuccess', - Object.assign({}, job, { - has_trace: false, - status: { - group: 'running', - icon: 'status_running', - label: 'running', - text: 'running', - details_path: 'path', - }, - }), - ); - - vm = mountComponentWithStore(Component, { - props, - store, + describe('truncated information', () => { + describe('when size is less than total', () => { + it('shows information about truncated log', done => { + mock.onGet(`${props.pagePath}/trace.json`).reply(200, { + html: '<span>Update</span>', + status: 'success', + append: false, + size: 50, + total: 100, + complete: true, + }); + + vm = mountComponentWithStore(Component, { + props, + store, + }); + + setTimeout(() => { + expect(vm.$el.querySelector('.js-truncated-info').textContent.trim()).toContain( + '50 bytes', + ); + done(); + }, 0); + }); }); - expect(vm.$el.querySelector('.js-job-empty-state')).toBeNull(); + describe('when size is equal than total', () => { + it('does not show the truncated information', done => { + mock.onGet(`${props.pagePath}/trace.json`).reply(200, { + html: '<span>Update</span>', + status: 'success', + append: false, + size: 100, + total: 100, + complete: true, + }); + + vm = mountComponentWithStore(Component, { + props, + store, + }); + + setTimeout(() => { + expect(vm.$el.querySelector('.js-truncated-info').textContent.trim()).not.toContain( + '50 bytes', + ); + done(); + }, 0); + }); + }); }); - it('does not render empty state when job has trace but it is not running', () => { - store.dispatch('receiveJobSuccess', Object.assign({}, job, { has_trace: true })); + describe('trace controls', () => { + beforeEach(() => { + mock.onGet(`${props.pagePath}/trace.json`).reply(200, { + html: '<span>Update</span>', + status: 'success', + append: false, + size: 50, + total: 100, + complete: true, + }); + + vm = mountComponentWithStore(Component, { + props, + store, + }); + }); + + it('should render scroll buttons', done => { + setTimeout(() => { + expect(vm.$el.querySelector('.js-scroll-top')).not.toBeNull(); + expect(vm.$el.querySelector('.js-scroll-bottom')).not.toBeNull(); + done(); + }, 0); + }); - vm = mountComponentWithStore(Component, { - props, - store, + it('should render link to raw ouput', done => { + setTimeout(() => { + expect(vm.$el.querySelector('.js-raw-link-controller')).not.toBeNull(); + done(); + }, 0); }); - expect(vm.$el.querySelector('.js-job-empty-state')).toBeNull(); + it('should render link to erase job', done => { + setTimeout(() => { + expect(vm.$el.querySelector('.js-erase-link')).not.toBeNull(); + done(); + }, 0); + }); }); }); }); diff --git a/spec/javascripts/jobs/components/job_container_item_spec.js b/spec/javascripts/jobs/components/job_container_item_spec.js new file mode 100644 index 00000000000..8588eda19c8 --- /dev/null +++ b/spec/javascripts/jobs/components/job_container_item_spec.js @@ -0,0 +1,73 @@ +import Vue from 'vue'; +import JobContainerItem from '~/jobs/components/job_container_item.vue'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; +import job from '../mock_data'; + +describe('JobContainerItem', () => { + const Component = Vue.extend(JobContainerItem); + let vm; + + afterEach(() => { + vm.$destroy(); + }); + + const sharedTests = () => { + it('displays a status icon', () => { + expect(vm.$el).toHaveSpriteIcon(job.status.icon); + }); + + it('displays the job name', () => { + expect(vm.$el).toContainText(job.name); + }); + + it('displays a link to the job', () => { + const link = vm.$el.querySelector('.js-job-link'); + + expect(link.href).toBe(job.status.details_path); + }); + }; + + describe('when a job is not active and not retied', () => { + beforeEach(() => { + vm = mountComponent(Component, { + job, + isActive: false, + }); + }); + + sharedTests(); + }); + + describe('when a job is active', () => { + beforeEach(() => { + vm = mountComponent(Component, { + job, + isActive: true, + }); + }); + + sharedTests(); + + it('displays an arrow', () => { + expect(vm.$el).toHaveSpriteIcon('arrow-right'); + }); + }); + + describe('when a job is retried', () => { + beforeEach(() => { + vm = mountComponent(Component, { + job: { + ...job, + retried: true, + }, + isActive: false, + }); + }); + + sharedTests(); + + it('displays an icon', () => { + expect(vm.$el).toHaveSpriteIcon('retry'); + }); + }); +}); diff --git a/spec/javascripts/jobs/components/job_log_controllers_spec.js b/spec/javascripts/jobs/components/job_log_controllers_spec.js index 3dcb57d23ce..d527c6708fc 100644 --- a/spec/javascripts/jobs/components/job_log_controllers_spec.js +++ b/spec/javascripts/jobs/components/job_log_controllers_spec.js @@ -199,6 +199,7 @@ describe('Job log controllers', () => { isScrollingDown: false, isTraceSizeVisible: true, }); + expect(vm.$el.querySelector('.js-scroll-bottom').className).not.toContain('animate'); }); }); diff --git a/spec/javascripts/jobs/components/job_log_spec.js b/spec/javascripts/jobs/components/job_log_spec.js index 1011512360d..dc0f77ceb80 100644 --- a/spec/javascripts/jobs/components/job_log_spec.js +++ b/spec/javascripts/jobs/components/job_log_spec.js @@ -1,31 +1,48 @@ import Vue from 'vue'; import component from '~/jobs/components/job_log.vue'; -import mountComponent from '../../helpers/vue_mount_component_helper'; +import createStore from '~/jobs/store'; +import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; +import { resetStore } from '../store/helpers'; describe('Job Log', () => { const Component = Vue.extend(component); + let store; let vm; - const trace = 'Running with gitlab-runner 11.1.0 (081978aa)<br> on docker-auto-scale-com d5ae8d25<br>Using Docker executor with image dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git-2.18-chrome-67.0-node-8.x-yarn-1.2-postgresql-9.6-graphicsmagick-1.3.29 ...<br>'; + const trace = + 'Running with gitlab-runner 11.1.0 (081978aa)<br> on docker-auto-scale-com d5ae8d25<br>Using Docker executor with image dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git-2.18-chrome-67.0-node-8.x-yarn-1.2-postgresql-9.6-graphicsmagick-1.3.29 ...<br>'; + + beforeEach(() => { + store = createStore(); + }); afterEach(() => { + resetStore(store); vm.$destroy(); }); it('renders provided trace', () => { - vm = mountComponent(Component, { - trace, - isComplete: true, + vm = mountComponentWithStore(Component, { + props: { + trace, + isComplete: true, + }, + store, }); - expect(vm.$el.querySelector('code').textContent).toContain('Running with gitlab-runner 11.1.0 (081978aa)'); + expect(vm.$el.querySelector('code').textContent).toContain( + 'Running with gitlab-runner 11.1.0 (081978aa)', + ); }); describe('while receiving trace', () => { it('renders animation', () => { - vm = mountComponent(Component, { - trace, - isComplete: true, + vm = mountComponentWithStore(Component, { + props: { + trace, + isComplete: false, + }, + store, }); expect(vm.$el.querySelector('.js-log-animation')).not.toBeNull(); @@ -34,9 +51,12 @@ describe('Job Log', () => { describe('when build trace has finishes', () => { it('does not render animation', () => { - vm = mountComponent(Component, { - trace, - isComplete: false, + vm = mountComponentWithStore(Component, { + props: { + trace, + isComplete: true, + }, + store, }); expect(vm.$el.querySelector('.js-log-animation')).toBeNull(); diff --git a/spec/javascripts/jobs/components/sidebar_detail_row_spec.js b/spec/javascripts/jobs/components/sidebar_detail_row_spec.js index e6bfb0c4adc..42d11266dad 100644 --- a/spec/javascripts/jobs/components/sidebar_detail_row_spec.js +++ b/spec/javascripts/jobs/components/sidebar_detail_row_spec.js @@ -33,9 +33,9 @@ describe('Sidebar detail row', () => { }); it('should render provided title and value', () => { - expect( - vm.$el.textContent.replace(/\s+/g, ' ').trim(), - ).toEqual('this is the title: this is the value'); + expect(vm.$el.textContent.replace(/\s+/g, ' ').trim()).toEqual( + 'this is the title: this is the value', + ); }); describe('when helpUrl not provided', () => { diff --git a/spec/javascripts/jobs/components/sidebar_spec.js b/spec/javascripts/jobs/components/sidebar_spec.js index a113377b19f..460a2e1b5da 100644 --- a/spec/javascripts/jobs/components/sidebar_spec.js +++ b/spec/javascripts/jobs/components/sidebar_spec.js @@ -18,15 +18,6 @@ describe('Sidebar details block', () => { vm.$destroy(); }); - describe('when it is loading', () => { - it('should render a loading spinner', () => { - store.dispatch('requestJob'); - vm = mountComponentWithStore(SidebarComponent, { store }); - - expect(vm.$el.querySelector('.fa-spinner')).toBeDefined(); - }); - }); - describe('when there is no retry path retry', () => { it('should not render a retry button', () => { const copy = Object.assign({}, job); @@ -52,12 +43,12 @@ describe('Sidebar details block', () => { describe('with terminal path', () => { it('renders terminal link', () => { - store.dispatch('receiveJobSuccess', job); + store.dispatch( + 'receiveJobSuccess', + Object.assign({}, job, { terminal_path: 'job/43123/terminal' }), + ); vm = mountComponentWithStore(SidebarComponent, { store, - props: { - terminalPath: 'job/43123/terminal', - }, }); expect(vm.$el.querySelector('.js-terminal-link')).not.toBeNull(); @@ -74,6 +65,7 @@ describe('Sidebar details block', () => { expect(vm.$el.querySelector('.js-new-issue').getAttribute('href')).toEqual( job.new_issue_path, ); + expect(vm.$el.querySelector('.js-new-issue').textContent.trim()).toEqual('New issue'); }); diff --git a/spec/javascripts/jobs/components/stages_dropdown_spec.js b/spec/javascripts/jobs/components/stages_dropdown_spec.js index fcff78b943e..9c731ae2f68 100644 --- a/spec/javascripts/jobs/components/stages_dropdown_spec.js +++ b/spec/javascripts/jobs/components/stages_dropdown_spec.js @@ -31,7 +31,7 @@ describe('Stages Dropdown', () => { name: 'test', }, ], - selectedStage: 'deploy' + selectedStage: 'deploy', }); }); diff --git a/spec/javascripts/jobs/components/trigger_block_spec.js b/spec/javascripts/jobs/components/trigger_block_spec.js index e1b9898393e..7254851a9e7 100644 --- a/spec/javascripts/jobs/components/trigger_block_spec.js +++ b/spec/javascripts/jobs/components/trigger_block_spec.js @@ -45,13 +45,18 @@ describe('Trigger block', () => { vm.$el.querySelector('.js-reveal-variables').click(); - vm - .$nextTick() + vm.$nextTick() .then(() => { expect(vm.$el.querySelector('.js-build-variables')).not.toBeNull(); - expect(vm.$el.querySelector('.js-build-variables').textContent).toContain('UPLOAD_TO_GCS'); + expect(vm.$el.querySelector('.js-build-variables').textContent).toContain( + 'UPLOAD_TO_GCS', + ); + expect(vm.$el.querySelector('.js-build-variables').textContent).toContain('false'); - expect(vm.$el.querySelector('.js-build-variables').textContent).toContain('UPLOAD_TO_S3'); + expect(vm.$el.querySelector('.js-build-variables').textContent).toContain( + 'UPLOAD_TO_S3', + ); + expect(vm.$el.querySelector('.js-build-variables').textContent).toContain('true'); }) .then(done) diff --git a/spec/javascripts/jobs/mock_data.js b/spec/javascripts/jobs/mock_data.js index 4269b42e8b6..0398f184c0a 100644 --- a/spec/javascripts/jobs/mock_data.js +++ b/spec/javascripts/jobs/mock_data.js @@ -1,3 +1,5 @@ +import { TEST_HOST } from 'spec/test_constants'; + const threeWeeksAgo = new Date(); threeWeeksAgo.setDate(threeWeeksAgo.getDate() - 21); @@ -19,7 +21,7 @@ export default { label: 'passed', group: 'success', has_details: true, - details_path: '/root/ci-mock/-/jobs/4757', + details_path: `${TEST_HOST}/root/ci-mock/-/jobs/4757`, favicon: '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png', action: { @@ -31,6 +33,7 @@ export default { }, coverage: 20, erased_at: threeWeeksAgo.toISOString(), + erased: false, duration: 6.785563, tags: ['tag'], user: { @@ -131,6 +134,7 @@ export default { path: '/root/ci-mock/merge_requests/2', }, raw_path: '/root/ci-mock/builds/4757/raw', + has_trace: true, }; export const stages = [ diff --git a/spec/javascripts/jobs/store/actions_spec.js b/spec/javascripts/jobs/store/actions_spec.js index bc410ae614c..45130b983e7 100644 --- a/spec/javascripts/jobs/store/actions_spec.js +++ b/spec/javascripts/jobs/store/actions_spec.js @@ -2,9 +2,7 @@ import MockAdapter from 'axios-mock-adapter'; import axios from '~/lib/utils/axios_utils'; import { setJobEndpoint, - setTraceEndpoint, - setStagesEndpoint, - setJobsEndpoint, + setTraceOptions, clearEtagPoll, stopPolling, requestJob, @@ -18,10 +16,6 @@ import { stopPollingTrace, receiveTraceSuccess, receiveTraceError, - fetchFavicon, - requestStatusFavicon, - receiveStatusFaviconSuccess, - requestStatusFaviconError, requestStages, fetchStages, receiveStagesSuccess, @@ -30,6 +24,9 @@ import { fetchJobsForStage, receiveJobsForStageSuccess, receiveJobsForStageError, + hideSidebar, + showSidebar, + toggleSidebar, } from '~/jobs/store/actions'; import state from '~/jobs/store/state'; import * as types from '~/jobs/store/mutation_types'; @@ -56,45 +53,75 @@ describe('Job State actions', () => { }); }); - describe('setTraceEndpoint', () => { - it('should commit SET_TRACE_ENDPOINT mutation', done => { + describe('setTraceOptions', () => { + it('should commit SET_TRACE_OPTIONS mutation', done => { testAction( - setTraceEndpoint, - 'job/872324/trace.json', + setTraceOptions, + { pagePath: 'job/872324/trace.json' }, mockedState, - [{ type: types.SET_TRACE_ENDPOINT, payload: 'job/872324/trace.json' }], + [{ type: types.SET_TRACE_OPTIONS, payload: { pagePath: 'job/872324/trace.json' } }], [], done, ); }); }); - describe('setStagesEndpoint', () => { - it('should commit SET_STAGES_ENDPOINT mutation', done => { + describe('hideSidebar', () => { + it('should commit HIDE_SIDEBAR mutation', done => { testAction( - setStagesEndpoint, - 'job/872324/stages.json', + hideSidebar, + null, mockedState, - [{ type: types.SET_STAGES_ENDPOINT, payload: 'job/872324/stages.json' }], + [{ type: types.HIDE_SIDEBAR }], [], done, ); }); }); - describe('setJobsEndpoint', () => { - it('should commit SET_JOBS_ENDPOINT mutation', done => { + describe('showSidebar', () => { + it('should commit HIDE_SIDEBAR mutation', done => { testAction( - setJobsEndpoint, - 'job/872324/stages/build.json', + showSidebar, + null, mockedState, - [{ type: types.SET_JOBS_ENDPOINT, payload: 'job/872324/stages/build.json' }], + [{ type: types.SHOW_SIDEBAR }], [], done, ); }); }); + describe('toggleSidebar', () => { + describe('when isSidebarOpen is true', () => { + it('should dispatch hideSidebar', done => { + testAction( + toggleSidebar, + null, + mockedState, + [], + [{ type: 'hideSidebar' }], + done, + ); + }); + }); + + describe('when isSidebarOpen is false', () => { + it('should dispatch showSidebar', done => { + mockedState.isSidebarOpen = false; + + testAction( + toggleSidebar, + null, + mockedState, + [], + [{ type: 'showSidebar' }], + done, + ); + }); + }); + }); + describe('requestJob', () => { it('should commit REQUEST_JOB mutation', done => { testAction(requestJob, null, mockedState, [{ type: types.REQUEST_JOB }], [], done); @@ -183,14 +210,14 @@ describe('Job State actions', () => { }); describe('scrollTop', () => { - it('should commit SCROLL_TO_TOP mutation', done => { - testAction(scrollTop, null, mockedState, [{ type: types.SCROLL_TO_TOP }], [], done); + it('should dispatch toggleScrollButtons action', done => { + testAction(scrollTop, null, mockedState, [], [{ type: 'toggleScrollButtons' }], done); }); }); describe('scrollBottom', () => { - it('should commit SCROLL_TO_BOTTOM mutation', done => { - testAction(scrollBottom, null, mockedState, [{ type: types.SCROLL_TO_BOTTOM }], [], done); + it('should dispatch toggleScrollButtons action', done => { + testAction(scrollBottom, null, mockedState, [], [{ type: 'toggleScrollButtons' }], done); }); }); @@ -215,7 +242,7 @@ describe('Job State actions', () => { }); describe('success', () => { - it('dispatches requestTrace, fetchFavicon, receiveTraceSuccess and stopPollingTrace when job is complete', done => { + it('dispatches requestTrace, receiveTraceSuccess and stopPollingTrace when job is complete', done => { mock.onGet(`${TEST_HOST}/endpoint/trace.json`).replyOnce(200, { html: 'I, [2018-08-17T22:57:45.707325 #1841] INFO -- :', complete: true, @@ -228,10 +255,8 @@ describe('Job State actions', () => { [], [ { - type: 'requestTrace', - }, - { - type: 'fetchFavicon', + type: 'toggleScrollisInBottom', + payload: true, }, { payload: { @@ -262,9 +287,6 @@ describe('Job State actions', () => { [], [ { - type: 'requestTrace', - }, - { type: 'receiveTraceError', }, ], @@ -313,104 +335,6 @@ describe('Job State actions', () => { }); }); - describe('fetchFavicon', () => { - let mock; - - beforeEach(() => { - mockedState.pagePath = `${TEST_HOST}/endpoint`; - mock = new MockAdapter(axios); - }); - - afterEach(() => { - mock.restore(); - }); - - describe('success', () => { - it('dispatches requestStatusFavicon and receiveStatusFaviconSuccess ', done => { - mock.onGet(`${TEST_HOST}/endpoint/status.json`).replyOnce(200); - - testAction( - fetchFavicon, - null, - mockedState, - [], - [ - { - type: 'requestStatusFavicon', - }, - { - type: 'receiveStatusFaviconSuccess', - }, - ], - done, - ); - }); - }); - - describe('error', () => { - beforeEach(() => { - mock.onGet(`${TEST_HOST}/endpoint/status.json`).replyOnce(500); - }); - - it('dispatches requestStatusFavicon and requestStatusFaviconError ', done => { - testAction( - fetchFavicon, - null, - mockedState, - [], - [ - { - type: 'requestStatusFavicon', - }, - { - type: 'requestStatusFaviconError', - }, - ], - done, - ); - }); - }); - }); - - describe('requestStatusFavicon', () => { - it('should commit REQUEST_STATUS_FAVICON mutation ', done => { - testAction( - requestStatusFavicon, - null, - mockedState, - [{ type: types.REQUEST_STATUS_FAVICON }], - [], - done, - ); - }); - }); - - describe('receiveStatusFaviconSuccess', () => { - it('should commit RECEIVE_STATUS_FAVICON_SUCCESS mutation ', done => { - testAction( - receiveStatusFaviconSuccess, - null, - mockedState, - [{ type: types.RECEIVE_STATUS_FAVICON_SUCCESS }], - [], - done, - ); - }); - }); - - describe('requestStatusFaviconError', () => { - it('should commit RECEIVE_STATUS_FAVICON_ERROR mutation ', done => { - testAction( - requestStatusFaviconError, - null, - mockedState, - [{ type: types.RECEIVE_STATUS_FAVICON_ERROR }], - [], - done, - ); - }); - }); - describe('requestStages', () => { it('should commit REQUEST_STAGES mutation ', done => { testAction(requestStages, null, mockedState, [{ type: types.REQUEST_STAGES }], [], done); @@ -424,7 +348,7 @@ describe('Job State actions', () => { mockedState.job.pipeline = { path: `${TEST_HOST}/endpoint`, }; - mockedState.selectedStage = 'deploy' + mockedState.selectedStage = 'deploy'; mock = new MockAdapter(axios); }); diff --git a/spec/javascripts/jobs/store/getters_spec.js b/spec/javascripts/jobs/store/getters_spec.js index e262a47b837..46a20122ec8 100644 --- a/spec/javascripts/jobs/store/getters_spec.js +++ b/spec/javascripts/jobs/store/getters_spec.js @@ -46,6 +46,7 @@ describe('Job Store Getters', () => { it('returns created_at key', () => { const created = '2018-08-31T16:20:49.023Z'; localState.job.created_at = created; + expect(getters.headerTime(localState)).toEqual(created); }); }); @@ -64,6 +65,7 @@ describe('Job Store Getters', () => { describe('without status & with callout message', () => { it('returns false', () => { localState.job.callout_message = 'Callout message'; + expect(getters.shouldRenderCalloutMessage(localState)).toEqual(false); }); }); @@ -81,6 +83,7 @@ describe('Job Store Getters', () => { describe('when started equals null', () => { it('returns false', () => { localState.job.started = null; + expect(getters.shouldRenderTriggeredLabel(localState)).toEqual(false); }); }); @@ -88,6 +91,7 @@ describe('Job Store Getters', () => { describe('when started equals string', () => { it('returns true', () => { localState.job.started = '2018-08-31T16:20:49.023Z'; + expect(getters.shouldRenderTriggeredLabel(localState)).toEqual(true); }); }); @@ -103,6 +107,7 @@ describe('Job Store Getters', () => { describe('with an empty object for `deployment_status`', () => { it('returns false', () => { localState.job.deployment_status = {}; + expect(getters.hasEnvironment(localState)).toEqual(false); }); }); diff --git a/spec/javascripts/jobs/store/helpers.js b/spec/javascripts/jobs/store/helpers.js new file mode 100644 index 00000000000..81a769b4a6e --- /dev/null +++ b/spec/javascripts/jobs/store/helpers.js @@ -0,0 +1,6 @@ +import state from '~/jobs/store/state'; + +// eslint-disable-next-line import/prefer-default-export +export const resetStore = store => { + store.replaceState(state()); +}; diff --git a/spec/javascripts/jobs/store/mutations_spec.js b/spec/javascripts/jobs/store/mutations_spec.js index 701fcc7f4c8..4230a7c42cf 100644 --- a/spec/javascripts/jobs/store/mutations_spec.js +++ b/spec/javascripts/jobs/store/mutations_spec.js @@ -15,28 +15,24 @@ describe('Jobs Store Mutations', () => { describe('SET_JOB_ENDPOINT', () => { it('should set jobEndpoint', () => { mutations[types.SET_JOB_ENDPOINT](stateCopy, 'job/21312321.json'); + expect(stateCopy.jobEndpoint).toEqual('job/21312321.json'); }); }); - describe('REQUEST_STATUS_FAVICON', () => { - it('should set fetchingStatusFavicon to true', () => { - mutations[types.REQUEST_STATUS_FAVICON](stateCopy); - expect(stateCopy.fetchingStatusFavicon).toEqual(true); - }); - }); + describe('HIDE_SIDEBAR', () => { + it('should set isSidebarOpen to false', () => { + mutations[types.HIDE_SIDEBAR](stateCopy); - describe('RECEIVE_STATUS_FAVICON_SUCCESS', () => { - it('should set fetchingStatusFavicon to false', () => { - mutations[types.RECEIVE_STATUS_FAVICON_SUCCESS](stateCopy); - expect(stateCopy.fetchingStatusFavicon).toEqual(false); + expect(stateCopy.isSidebarOpen).toEqual(false); }); }); - describe('RECEIVE_STATUS_FAVICON_ERROR', () => { - it('should set fetchingStatusFavicon to false', () => { - mutations[types.RECEIVE_STATUS_FAVICON_ERROR](stateCopy); - expect(stateCopy.fetchingStatusFavicon).toEqual(false); + describe('SHOW_SIDEBAR', () => { + it('should set isSidebarOpen to true', () => { + mutations[types.SHOW_SIDEBAR](stateCopy); + + expect(stateCopy.isSidebarOpen).toEqual(true); }); }); @@ -48,6 +44,7 @@ describe('Jobs Store Mutations', () => { mutations[types.RECEIVE_TRACE_SUCCESS](stateCopy, { state: stateLog, }); + expect(stateCopy.traceState).toEqual(stateLog); }); }); @@ -77,6 +74,7 @@ describe('Jobs Store Mutations', () => { size: 511846, complete: true, }); + expect(stateCopy.trace).toEqual(html); expect(stateCopy.traceSize).toEqual(511846); expect(stateCopy.isTraceComplete).toEqual(true); @@ -86,6 +84,7 @@ describe('Jobs Store Mutations', () => { describe('STOP_POLLING_TRACE', () => { it('sets isTraceComplete to true', () => { mutations[types.STOP_POLLING_TRACE](stateCopy); + expect(stateCopy.isTraceComplete).toEqual(true); }); }); @@ -93,9 +92,8 @@ describe('Jobs Store Mutations', () => { describe('RECEIVE_TRACE_ERROR', () => { it('resets trace state and sets error to true', () => { mutations[types.RECEIVE_TRACE_ERROR](stateCopy); - expect(stateCopy.isLoadingTrace).toEqual(false); + expect(stateCopy.isTraceComplete).toEqual(true); - expect(stateCopy.hasTraceError).toEqual(true); }); }); @@ -110,29 +108,35 @@ describe('Jobs Store Mutations', () => { describe('RECEIVE_JOB_SUCCESS', () => { it('sets is loading to false', () => { mutations[types.RECEIVE_JOB_SUCCESS](stateCopy, { id: 1312321 }); + expect(stateCopy.isLoading).toEqual(false); }); it('sets hasError to false', () => { mutations[types.RECEIVE_JOB_SUCCESS](stateCopy, { id: 1312321 }); + expect(stateCopy.hasError).toEqual(false); }); it('sets job data', () => { mutations[types.RECEIVE_JOB_SUCCESS](stateCopy, { id: 1312321 }); + expect(stateCopy.job).toEqual({ id: 1312321 }); }); it('sets selectedStage when the selectedStage is More', () => { expect(stateCopy.selectedStage).toEqual('More'); mutations[types.RECEIVE_JOB_SUCCESS](stateCopy, { id: 1312321, stage: 'deploy' }); + expect(stateCopy.selectedStage).toEqual('deploy'); }); it('does not set selectedStage when the selectedStage is not More', () => { - stateCopy.selectedStage = 'notify' + stateCopy.selectedStage = 'notify'; + expect(stateCopy.selectedStage).toEqual('notify'); mutations[types.RECEIVE_JOB_SUCCESS](stateCopy, { id: 1312321, stage: 'deploy' }); + expect(stateCopy.selectedStage).toEqual('notify'); }); }); @@ -142,42 +146,14 @@ describe('Jobs Store Mutations', () => { mutations[types.RECEIVE_JOB_ERROR](stateCopy); expect(stateCopy.isLoading).toEqual(false); - expect(stateCopy.hasError).toEqual(true); expect(stateCopy.job).toEqual({}); }); }); - describe('SCROLL_TO_TOP', () => { - beforeEach(() => { - mutations[types.SCROLL_TO_TOP](stateCopy); - }); - - it('sets isTraceScrolledToBottom to false', () => { - expect(stateCopy.isTraceScrolledToBottom).toEqual(false); - }); - - it('sets hasBeenScrolled to true', () => { - expect(stateCopy.hasBeenScrolled).toEqual(true); - }); - }); - - describe('SCROLL_TO_BOTTOM', () => { - beforeEach(() => { - mutations[types.SCROLL_TO_BOTTOM](stateCopy); - }); - - it('sets isTraceScrolledToBottom to true', () => { - expect(stateCopy.isTraceScrolledToBottom).toEqual(true); - }); - - it('sets hasBeenScrolled to true', () => { - expect(stateCopy.hasBeenScrolled).toEqual(true); - }); - }); - describe('REQUEST_STAGES', () => { it('sets isLoadingStages to true', () => { mutations[types.REQUEST_STAGES](stateCopy); + expect(stateCopy.isLoadingStages).toEqual(true); }); }); @@ -213,13 +189,15 @@ describe('Jobs Store Mutations', () => { describe('REQUEST_JOBS_FOR_STAGE', () => { it('sets isLoadingStages to true', () => { mutations[types.REQUEST_JOBS_FOR_STAGE](stateCopy, { name: 'deploy' }); + expect(stateCopy.isLoadingJobs).toEqual(true); }); it('sets selectedStage', () => { mutations[types.REQUEST_JOBS_FOR_STAGE](stateCopy, { name: 'deploy' }); + expect(stateCopy.selectedStage).toEqual('deploy'); - }) + }); }); describe('RECEIVE_JOBS_FOR_STAGE_SUCCESS', () => { diff --git a/spec/javascripts/labels_issue_sidebar_spec.js b/spec/javascripts/labels_issue_sidebar_spec.js index 5aafb6ad8f0..e5678ee5379 100644 --- a/spec/javascripts/labels_issue_sidebar_spec.js +++ b/spec/javascripts/labels_issue_sidebar_spec.js @@ -12,88 +12,106 @@ import '~/api'; import '~/create_label'; import '~/users_select'; -(() => { - let saveLabelCount = 0; - let mock; +let saveLabelCount = 0; +let mock; - describe('Issue dropdown sidebar', () => { - preloadFixtures('static/issue_sidebar_label.html.raw'); +describe('Issue dropdown sidebar', () => { + preloadFixtures('static/issue_sidebar_label.html.raw'); - beforeEach(() => { - loadFixtures('static/issue_sidebar_label.html.raw'); + beforeEach(() => { + loadFixtures('static/issue_sidebar_label.html.raw'); - mock = new MockAdapter(axios); + mock = new MockAdapter(axios); - new IssuableContext('{"id":1,"name":"Administrator","username":"root"}'); - new LabelsSelect(); + new IssuableContext('{"id":1,"name":"Administrator","username":"root"}'); + new LabelsSelect(); - mock.onGet('/root/test/labels.json').reply(() => { - const labels = Array(10).fill().map((_, i) => ({ + mock.onGet('/root/test/labels.json').reply(() => { + const labels = Array(10) + .fill() + .map((_, i) => ({ id: i, title: `test ${i}`, color: '#5CB85C', })); - return [200, labels]; - }); + return [200, labels]; + }); - mock.onPut('/root/test/issues/2.json').reply(() => { - const labels = Array(saveLabelCount).fill().map((_, i) => ({ + mock.onPut('/root/test/issues/2.json').reply(() => { + const labels = Array(saveLabelCount) + .fill() + .map((_, i) => ({ id: i, title: `test ${i}`, color: '#5CB85C', })); - return [200, { labels }]; - }); - }); - - afterEach(() => { - mock.restore(); + return [200, { labels }]; }); + }); - it('changes collapsed tooltip when changing labels when less than 5', (done) => { - saveLabelCount = 5; - $('.edit-link').get(0).click(); - - setTimeout(() => { - expect($('.dropdown-content a').length).toBe(10); - - $('.dropdown-content a').each(function (i) { - if (i < saveLabelCount) { - $(this).get(0).click(); - } - }); - - $('.edit-link').get(0).click(); + afterEach(() => { + mock.restore(); + }); - setTimeout(() => { - expect($('.sidebar-collapsed-icon').attr('data-original-title')).toBe('test 0, test 1, test 2, test 3, test 4'); - done(); - }, 0); - }, 0); - }); + it('changes collapsed tooltip when changing labels when less than 5', done => { + saveLabelCount = 5; + $('.edit-link') + .get(0) + .click(); + + setTimeout(() => { + expect($('.dropdown-content a').length).toBe(10); + + $('.dropdown-content a').each(function(i) { + if (i < saveLabelCount) { + $(this) + .get(0) + .click(); + } + }); - it('changes collapsed tooltip when changing labels when more than 5', (done) => { - saveLabelCount = 6; - $('.edit-link').get(0).click(); + $('.edit-link') + .get(0) + .click(); setTimeout(() => { - expect($('.dropdown-content a').length).toBe(10); + expect($('.sidebar-collapsed-icon').attr('data-original-title')).toBe( + 'test 0, test 1, test 2, test 3, test 4', + ); + done(); + }, 0); + }, 0); + }); - $('.dropdown-content a').each(function (i) { - if (i < saveLabelCount) { - $(this).get(0).click(); - } - }); + it('changes collapsed tooltip when changing labels when more than 5', done => { + saveLabelCount = 6; + $('.edit-link') + .get(0) + .click(); + + setTimeout(() => { + expect($('.dropdown-content a').length).toBe(10); + + $('.dropdown-content a').each(function(i) { + if (i < saveLabelCount) { + $(this) + .get(0) + .click(); + } + }); - $('.edit-link').get(0).click(); + $('.edit-link') + .get(0) + .click(); - setTimeout(() => { - expect($('.sidebar-collapsed-icon').attr('data-original-title')).toBe('test 0, test 1, test 2, test 3, test 4, and 1 more'); - done(); - }, 0); + setTimeout(() => { + expect($('.sidebar-collapsed-icon').attr('data-original-title')).toBe( + 'test 0, test 1, test 2, test 3, test 4, and 1 more', + ); + done(); }, 0); - }); + }, 0); }); -})(); +}); diff --git a/spec/javascripts/labels_select_spec.js b/spec/javascripts/labels_select_spec.js index 386e00bfd0c..acfdc885032 100644 --- a/spec/javascripts/labels_select_spec.js +++ b/spec/javascripts/labels_select_spec.js @@ -19,10 +19,12 @@ describe('LabelsSelect', () => { let $labelEl; beforeEach(() => { - $labelEl = $(LabelsSelect.getLabelTemplate({ - labels: mockLabels, - issueUpdateURL: mockUrl, - })); + $labelEl = $( + LabelsSelect.getLabelTemplate({ + labels: mockLabels, + issueUpdateURL: mockUrl, + }), + ); }); it('generated label item template has correct label URL', () => { @@ -38,7 +40,9 @@ describe('LabelsSelect', () => { }); it('generated label item template has correct label styles', () => { - expect($labelEl.find('span.label').attr('style')).toBe(`background-color: ${label.color}; color: ${label.text_color};`); + expect($labelEl.find('span.label').attr('style')).toBe( + `background-color: ${label.color}; color: ${label.text_color};`, + ); }); it('generated label item has a badge class', () => { diff --git a/spec/javascripts/landing_spec.js b/spec/javascripts/landing_spec.js index 7916073190a..2fe5a47b63e 100644 --- a/spec/javascripts/landing_spec.js +++ b/spec/javascripts/landing_spec.js @@ -1,9 +1,9 @@ import Landing from '~/landing'; import Cookies from 'js-cookie'; -describe('Landing', function () { - describe('class constructor', function () { - beforeEach(function () { +describe('Landing', function() { + describe('class constructor', function() { + beforeEach(function() { this.landingElement = {}; this.dismissButton = {}; this.cookieName = 'cookie_name'; @@ -11,25 +11,25 @@ describe('Landing', function () { this.landing = new Landing(this.landingElement, this.dismissButton, this.cookieName); }); - it('should set .landing', function () { + it('should set .landing', function() { expect(this.landing.landingElement).toBe(this.landingElement); }); - it('should set .cookieName', function () { + it('should set .cookieName', function() { expect(this.landing.cookieName).toBe(this.cookieName); }); - it('should set .dismissButton', function () { + it('should set .dismissButton', function() { expect(this.landing.dismissButton).toBe(this.dismissButton); }); - it('should set .eventWrapper', function () { + it('should set .eventWrapper', function() { expect(this.landing.eventWrapper).toEqual({}); }); }); - describe('toggle', function () { - beforeEach(function () { + describe('toggle', function() { + beforeEach(function() { this.isDismissed = false; this.landingElement = { classList: jasmine.createSpyObj('classList', ['toggle']) }; this.landing = { @@ -44,20 +44,20 @@ describe('Landing', function () { Landing.prototype.toggle.call(this.landing); }); - it('should call .isDismissed', function () { + it('should call .isDismissed', function() { expect(this.landing.isDismissed).toHaveBeenCalled(); }); - it('should call .classList.toggle', function () { + it('should call .classList.toggle', function() { expect(this.landingElement.classList.toggle).toHaveBeenCalledWith('hidden', this.isDismissed); }); - it('should call .addEvents', function () { + it('should call .addEvents', function() { expect(this.landing.addEvents).toHaveBeenCalled(); }); - describe('if isDismissed is true', function () { - beforeEach(function () { + describe('if isDismissed is true', function() { + beforeEach(function() { this.isDismissed = true; this.landingElement = { classList: jasmine.createSpyObj('classList', ['toggle']) }; this.landing = { @@ -74,14 +74,14 @@ describe('Landing', function () { Landing.prototype.toggle.call(this.landing); }); - it('should not call .addEvents', function () { + it('should not call .addEvents', function() { expect(this.landing.addEvents).not.toHaveBeenCalled(); }); }); }); - describe('addEvents', function () { - beforeEach(function () { + describe('addEvents', function() { + beforeEach(function() { this.dismissButton = jasmine.createSpyObj('dismissButton', ['addEventListener']); this.eventWrapper = {}; this.landing = { @@ -93,17 +93,20 @@ describe('Landing', function () { Landing.prototype.addEvents.call(this.landing); }); - it('should set .eventWrapper.dismissLanding', function () { + it('should set .eventWrapper.dismissLanding', function() { expect(this.eventWrapper.dismissLanding).toEqual(jasmine.any(Function)); }); - it('should call .addEventListener', function () { - expect(this.dismissButton.addEventListener).toHaveBeenCalledWith('click', this.eventWrapper.dismissLanding); + it('should call .addEventListener', function() { + expect(this.dismissButton.addEventListener).toHaveBeenCalledWith( + 'click', + this.eventWrapper.dismissLanding, + ); }); }); - describe('removeEvents', function () { - beforeEach(function () { + describe('removeEvents', function() { + beforeEach(function() { this.dismissButton = jasmine.createSpyObj('dismissButton', ['removeEventListener']); this.eventWrapper = { dismissLanding: () => {} }; this.landing = { @@ -114,13 +117,16 @@ describe('Landing', function () { Landing.prototype.removeEvents.call(this.landing); }); - it('should call .removeEventListener', function () { - expect(this.dismissButton.removeEventListener).toHaveBeenCalledWith('click', this.eventWrapper.dismissLanding); + it('should call .removeEventListener', function() { + expect(this.dismissButton.removeEventListener).toHaveBeenCalledWith( + 'click', + this.eventWrapper.dismissLanding, + ); }); }); - describe('dismissLanding', function () { - beforeEach(function () { + describe('dismissLanding', function() { + beforeEach(function() { this.landingElement = { classList: jasmine.createSpyObj('classList', ['add']) }; this.cookieName = 'cookie_name'; this.landing = { landingElement: this.landingElement, cookieName: this.cookieName }; @@ -130,17 +136,17 @@ describe('Landing', function () { Landing.prototype.dismissLanding.call(this.landing); }); - it('should call .classList.add', function () { + it('should call .classList.add', function() { expect(this.landingElement.classList.add).toHaveBeenCalledWith('hidden'); }); - it('should call Cookies.set', function () { + it('should call Cookies.set', function() { expect(Cookies.set).toHaveBeenCalledWith(this.cookieName, 'true', { expires: 365 }); }); }); - describe('isDismissed', function () { - beforeEach(function () { + describe('isDismissed', function() { + beforeEach(function() { this.cookieName = 'cookie_name'; this.landing = { cookieName: this.cookieName }; @@ -149,11 +155,11 @@ describe('Landing', function () { this.isDismissed = Landing.prototype.isDismissed.call(this.landing); }); - it('should call Cookies.get', function () { + it('should call Cookies.get', function() { expect(Cookies.get).toHaveBeenCalledWith(this.cookieName); }); - it('should return a boolean', function () { + it('should return a boolean', function() { expect(typeof this.isDismissed).toEqual('boolean'); }); }); diff --git a/spec/javascripts/lib/utils/accessor_spec.js b/spec/javascripts/lib/utils/accessor_spec.js index b768d6f2a68..0045330e470 100644 --- a/spec/javascripts/lib/utils/accessor_spec.js +++ b/spec/javascripts/lib/utils/accessor_spec.js @@ -13,7 +13,11 @@ describe('AccessorUtilities', () => { }); it('should return `false` if access throws an error', () => { - base = { get testProp() { throw testError; } }; + base = { + get testProp() { + throw testError; + }, + }; expect(AccessorUtilities.isPropertyAccessSafe(base, 'testProp')).toBe(false); }); @@ -35,7 +39,9 @@ describe('AccessorUtilities', () => { }); it('should return `false` if calling throws an error', () => { - base.func = () => { throw new Error('test error'); }; + base.func = () => { + throw new Error('test error'); + }; expect(AccessorUtilities.isFunctionCallSafe(base, 'func')).toBe(false); }); @@ -58,7 +64,9 @@ describe('AccessorUtilities', () => { }); it('should return `false` if access to .setItem isnt safe', () => { - window.localStorage.setItem.and.callFake(() => { throw testError; }); + window.localStorage.setItem.and.callFake(() => { + throw testError; + }); expect(AccessorUtilities.isLocalStorageAccessSafe()).toBe(false); }); diff --git a/spec/javascripts/lib/utils/ajax_cache_spec.js b/spec/javascripts/lib/utils/ajax_cache_spec.js index 7603400b55e..dc0b04173bf 100644 --- a/spec/javascripts/lib/utils/ajax_cache_spec.js +++ b/spec/javascripts/lib/utils/ajax_cache_spec.js @@ -9,8 +9,8 @@ describe('AjaxCache', () => { }; beforeEach(() => { - AjaxCache.internalStorage = { }; - AjaxCache.pendingRequests = { }; + AjaxCache.internalStorage = {}; + AjaxCache.pendingRequests = {}; }); describe('get', () => { @@ -59,7 +59,7 @@ describe('AjaxCache', () => { it('does nothing if cache is empty', () => { AjaxCache.remove(dummyEndpoint); - expect(AjaxCache.internalStorage).toEqual({ }); + expect(AjaxCache.internalStorage).toEqual({}); }); it('does nothing if cache contains no matching data', () => { @@ -75,7 +75,7 @@ describe('AjaxCache', () => { AjaxCache.remove(dummyEndpoint); - expect(AjaxCache.internalStorage).toEqual({ }); + expect(AjaxCache.internalStorage).toEqual({}); }); }); @@ -101,61 +101,61 @@ describe('AjaxCache', () => { mock.restore(); }); - it('stores and returns data from Ajax call if cache is empty', (done) => { + it('stores and returns data from Ajax call if cache is empty', done => { mock.onGet(dummyEndpoint).reply(200, dummyResponse); AjaxCache.retrieve(dummyEndpoint) - .then((data) => { - expect(data).toEqual(dummyResponse); - expect(AjaxCache.internalStorage[dummyEndpoint]).toEqual(dummyResponse); - }) - .then(done) - .catch(fail); + .then(data => { + expect(data).toEqual(dummyResponse); + expect(AjaxCache.internalStorage[dummyEndpoint]).toEqual(dummyResponse); + }) + .then(done) + .catch(fail); }); - it('makes no Ajax call if request is pending', (done) => { + it('makes no Ajax call if request is pending', done => { mock.onGet(dummyEndpoint).reply(200, dummyResponse); AjaxCache.retrieve(dummyEndpoint) - .then(done) - .catch(fail); + .then(done) + .catch(fail); AjaxCache.retrieve(dummyEndpoint) - .then(done) - .catch(fail); + .then(done) + .catch(fail); expect(axios.get.calls.count()).toBe(1); }); - it('returns undefined if Ajax call fails and cache is empty', (done) => { + it('returns undefined if Ajax call fails and cache is empty', done => { const errorMessage = 'Network Error'; mock.onGet(dummyEndpoint).networkError(); AjaxCache.retrieve(dummyEndpoint) - .then(data => fail(`Received unexpected data: ${JSON.stringify(data)}`)) - .catch((error) => { - expect(error.message).toBe(`${dummyEndpoint}: ${errorMessage}`); - expect(error.textStatus).toBe(errorMessage); - done(); - }) - .catch(fail); + .then(data => fail(`Received unexpected data: ${JSON.stringify(data)}`)) + .catch(error => { + expect(error.message).toBe(`${dummyEndpoint}: ${errorMessage}`); + expect(error.textStatus).toBe(errorMessage); + done(); + }) + .catch(fail); }); - it('makes no Ajax call if matching data exists', (done) => { + it('makes no Ajax call if matching data exists', done => { AjaxCache.internalStorage[dummyEndpoint] = dummyResponse; mock.onGet(dummyEndpoint).reply(() => { fail(new Error('expected no Ajax call!')); }); AjaxCache.retrieve(dummyEndpoint) - .then((data) => { - expect(data).toBe(dummyResponse); - }) - .then(done) - .catch(fail); + .then(data => { + expect(data).toBe(dummyResponse); + }) + .then(done) + .catch(fail); }); - it('makes Ajax call even if matching data exists when forceRequest parameter is provided', (done) => { + it('makes Ajax call even if matching data exists when forceRequest parameter is provided', done => { const oldDummyResponse = { important: 'old dummy data', }; @@ -166,7 +166,7 @@ describe('AjaxCache', () => { // Call without forceRetrieve param AjaxCache.retrieve(dummyEndpoint) - .then((data) => { + .then(data => { expect(data).toBe(oldDummyResponse); }) .then(done) @@ -174,7 +174,7 @@ describe('AjaxCache', () => { // Call with forceRetrieve param AjaxCache.retrieve(dummyEndpoint, true) - .then((data) => { + .then(data => { expect(data).toEqual(dummyResponse); }) .then(done) diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js index c34622203f7..514d6ddeae5 100644 --- a/spec/javascripts/lib/utils/common_utils_spec.js +++ b/spec/javascripts/lib/utils/common_utils_spec.js @@ -1,4 +1,3 @@ -/* eslint-disable promise/catch-or-return */ import axios from '~/lib/utils/axios_utils'; import * as commonUtils from '~/lib/utils/common_utils'; import MockAdapter from 'axios-mock-adapter'; @@ -43,6 +42,7 @@ describe('common_utils', () => { it('should remove the question mark from the search params', () => { const paramsArray = commonUtils.urlParamsToArray('?test=thing'); + expect(paramsArray[0][0]).not.toBe('?'); }); }); @@ -122,6 +122,7 @@ describe('common_utils', () => { commonUtils.handleLocationHash(); expectGetElementIdToHaveBeenCalledWith('test'); + expect(window.scrollY).toBe(document.getElementById('test').offsetTop); document.getElementById('parent').remove(); @@ -140,6 +141,7 @@ describe('common_utils', () => { expectGetElementIdToHaveBeenCalledWith('test'); expectGetElementIdToHaveBeenCalledWith('user-content-test'); + expect(window.scrollY).toBe(document.getElementById('user-content-test').offsetTop); document.getElementById('parent').remove(); @@ -160,6 +162,7 @@ describe('common_utils', () => { expectGetElementIdToHaveBeenCalledWith('test'); expectGetElementIdToHaveBeenCalledWith('user-content-test'); + expect(window.scrollY).toBe(document.getElementById('user-content-test').offsetTop - 50); expect(window.scrollBy).toHaveBeenCalledWith(0, -50); @@ -223,20 +226,24 @@ describe('common_utils', () => { it('should return valid parameter', () => { const value = commonUtils.getParameterByName('scope'); + expect(commonUtils.getParameterByName('p')).toEqual('2'); expect(value).toBe('all'); }); it('should return invalid parameter', () => { const value = commonUtils.getParameterByName('fakeParameter'); + expect(value).toBe(null); }); it('should return valid paramentes if URL is provided', () => { let value = commonUtils.getParameterByName('foo', 'http://cocteau.twins/?foo=bar'); + expect(value).toBe('bar'); value = commonUtils.getParameterByName('manan', 'http://cocteau.twins/?foo=bar&manan=canchu'); + expect(value).toBe('canchu'); }); }); @@ -360,10 +367,10 @@ describe('common_utils', () => { }).then((resp) => { stop(resp); }) - )).then((respBackoff) => { + ).catch(done.fail)).then((respBackoff) => { expect(respBackoff).toBe(expectedResponseValue); done(); - }); + }).catch(done.fail); }); it('catches the rejected promise from the callback ', (done) => { @@ -394,18 +401,20 @@ describe('common_utils', () => { stop(resp); } }) - )).then((respBackoff) => { + ).catch(done.fail)).then((respBackoff) => { const timeouts = window.setTimeout.calls.allArgs().map(([, timeout]) => timeout); + expect(timeouts).toEqual([2000, 4000]); expect(respBackoff).toBe(expectedResponseValue); done(); - }); + }).catch(done.fail); }); it('rejects the backOff promise after timing out', (done) => { commonUtils.backOff(next => next(), 64000) .catch((errBackoffResp) => { const timeouts = window.setTimeout.calls.allArgs().map(([, timeout]) => timeout); + expect(timeouts).toEqual([2000, 4000, 8000, 16000, 32000, 32000]); expect(errBackoffResp instanceof Error).toBe(true); expect(errBackoffResp.message).toBe('BACKOFF_TIMEOUT'); @@ -451,6 +460,7 @@ describe('common_utils', () => { const favicon = document.getElementById('favicon'); favicon.setAttribute('href', 'new/favicon'); commonUtils.resetFavicon(); + expect(document.getElementById('favicon').getAttribute('href')).toEqual('default/favicon'); }); }); @@ -460,7 +470,7 @@ describe('common_utils', () => { commonUtils.createOverlayIcon(faviconDataUrl, overlayDataUrl).then((url) => { expect(url).toEqual(faviconWithOverlayDataUrl); done(); - }); + }).catch(done.fail); }); }); @@ -480,7 +490,7 @@ describe('common_utils', () => { commonUtils.setFaviconOverlay(overlayDataUrl).then(() => { expect(document.getElementById('favicon').getAttribute('href')).toEqual(faviconWithOverlayDataUrl); done(); - }); + }).catch(done.fail); }); }); @@ -508,6 +518,7 @@ describe('common_utils', () => { commonUtils.setCiStatusFavicon(BUILD_URL) .catch(() => { const favicon = document.getElementById('favicon'); + expect(favicon.getAttribute('href')).toEqual(faviconDataUrl); done(); }); @@ -521,6 +532,7 @@ describe('common_utils', () => { commonUtils.setCiStatusFavicon(BUILD_URL) .then(() => { const favicon = document.getElementById('favicon'); + expect(favicon.getAttribute('href')).toEqual(faviconWithOverlayDataUrl); done(); }) diff --git a/spec/javascripts/lib/utils/csrf_token_spec.js b/spec/javascripts/lib/utils/csrf_token_spec.js index 81a39a97a84..867bee34ee5 100644 --- a/spec/javascripts/lib/utils/csrf_token_spec.js +++ b/spec/javascripts/lib/utils/csrf_token_spec.js @@ -1,9 +1,10 @@ import csrf from '~/lib/utils/csrf'; -describe('csrf', function () { +describe('csrf', function() { beforeEach(() => { this.tokenKey = 'X-CSRF-Token'; - this.token = 'pH1cvjnP9grx2oKlhWEDvUZnJ8x2eXsIs1qzyHkF3DugSG5yTxR76CWeEZRhML2D1IeVB7NEW0t5l/axE4iJpQ=='; + this.token = + 'pH1cvjnP9grx2oKlhWEDvUZnJ8x2eXsIs1qzyHkF3DugSG5yTxR76CWeEZRhML2D1IeVB7NEW0t5l/axE4iJpQ=='; }); it('returns the correct headerKey', () => { diff --git a/spec/javascripts/lib/utils/dom_utils_spec.js b/spec/javascripts/lib/utils/dom_utils_spec.js index 867bf5912d1..1fb2e4584a0 100644 --- a/spec/javascripts/lib/utils/dom_utils_spec.js +++ b/spec/javascripts/lib/utils/dom_utils_spec.js @@ -18,6 +18,7 @@ describe('DOM Utils', () => { it('adds class if element exists', () => { const childElement = parentElement.querySelector('.child'); + expect(childElement).not.toBe(null); addClassIfElementExists(childElement, className); @@ -27,6 +28,7 @@ describe('DOM Utils', () => { it('does not throw if element does not exist', () => { const childElement = parentElement.querySelector('.other-child'); + expect(childElement).toBe(null); addClassIfElementExists(childElement, className); diff --git a/spec/javascripts/lib/utils/mock_data.js b/spec/javascripts/lib/utils/mock_data.js index 93d0d6259b9..c466b0cd1ed 100644 --- a/spec/javascripts/lib/utils/mock_data.js +++ b/spec/javascripts/lib/utils/mock_data.js @@ -1,5 +1,8 @@ -export const faviconDataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAACcFBMVEX////iQyniQyniQyniQyniQyniQyniQyniQynhRiriQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniRCniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQynhQiniQiniQiniQinhQinpUSjqUSjqTyjqTyjqTyjlSCniRCniQynjRCjqTyjsZSjrWyj8oib9kSb8pyb9pib8oyb8fyb3ZSb4Zib8fCb8oyb8oyb8oyb8pCb8cSbiQyn7bCb8cib8oyb8oSb8bSbtVSjpTij8nyb8oyb8oyb8lCb2Yyf3ZCf8mCb8oyb8oyb8oyb8iib8bSbiRCn8gyb8oyb8eCbpTinrUSj8oyb8oyb8oyb8pSb8bib4Zif0YCf8byb8oyb8oyb8oyb7oib8oyb8nCbjRSn9bib8ayb8nib8oyb8oyb8oyb8kSbpTyjpTyj8jib8oyb8oyb8oyb8fib0Xyf2ZSb8gCb8oyb6pSb8oyb8dib+cCbgQCnjRSn8cCb8oib8oyb8oyb8oybqUCjnSyn8bCb8oyb8oyb8oyb8myb2YyfyXyf8oyb8oyb8hibhQSn+bib8iSb8oyb8qCb+fSbmSSnqTyj8oib9pCb1YifxXyf7pSb8oCb8pCb+mCb0fCf8pSb7hSXvcSjiQyniQinqTyj9kCb9bib9byb+cCbqUSjiRCnsVCj+cSb8pib8bCb8bSbgQCn7bCb8bibjRSn8oyb8ayb8oib8aib8pCbjRCn8pybhQinhQSn8pSb7ayb7aSb6aib8eib///8IbM+7AAAAr3RSTlMBA3NtX2vT698HGQcRLwWLiXnv++3V+eEd/R8HE2V/Y5HjyefdFw99YWfJ+/3nwQP78/HvX1VTQ/kdA2HzbQXj9fX79/3DGf379/33T/v99/f7ba33+/f1+9/18/v59V339flzF/H9+fX3/fMhBwOh9/v5/fmvBV/z+fP3Awnp9/f38+UFgff7+/37+4c77/f7/flFz/f59dFr7/v98Wnr+/f3I5/197EDBU1ZAwUD8/kLUwAAAAFiS0dEAIgFHUgAAAAHdElNRQfhBQoLHiBV6/1lAAACHUlEQVQ4y41TZXsTQRCe4FAIUigN7m7FXY+iLRQKBG2x4g7BjhZ3Le7uMoEkFJprwyQk0CC/iZnNhUZaHt4vt6/szO7cHcD/wFKjZrJWq3YMq1M3eVc9rFzXR2yQkuA3RGxkjZLGiEk9miA2tURJs1RsnhhokYYtzaU13WZDbBVnW1sjo43J2vI6tZ0lLtFeAh1M0lECneI7dGYtrUtk3RUVIKaEJR25qw27yT0s3W0qEHuPlB4RradivXo7GX36xnbo51SQ+fWHARmCgYMGDxkaxbD3SssYPmIkwKgPLrfA87EETTg/fVaSa/SYsQDjSsd7DcGEsr+BieVKmaRNBsjUtClTfUI900y/5Mt05c8oJQKYSURZ2UqYFa0w283M588JEM2BuRwI5EqT8nmmXzZf4l8XsGNfCIv4QcHFklhiBpaqAsuC4tghj+ySyOdjeJYrP7RCCuR/E5tWAqxaLcmCNSyujdxjHZdbn8UHoA0bN/GoNm8hjQJb/ZzYpo6w3TB27JRduxxqrA7YzbWCezixN8RD2Oc2/Ptlfx7o5uT1A4XMiwzj4HfEikNe7+Ew0ZGjeuW70eEYaeHjxomTiKd++E4XnKGz8d+HDufOB3Ky3RcwdNF1qZiKLyf/B44r2tWf15wV143cwI2qfi8dbtKtX6Hbd+6G74EDqkTm/QcPH/0ufFyNLXjy9NnzF9Xb8BJevYY38C+8fZcg/AF3QTYemVkCwwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxNy0wNS0xMFQxMTozMDozMiswMjowMMzup8UAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTctMDUtMTBUMTE6MzA6MzIrMDI6MDC9sx95AAAAAElFTkSuQmCC'; +export const faviconDataUrl = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAACcFBMVEX////iQyniQyniQyniQyniQyniQyniQyniQynhRiriQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniRCniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQynhQiniQiniQiniQinhQinpUSjqUSjqTyjqTyjqTyjlSCniRCniQynjRCjqTyjsZSjrWyj8oib9kSb8pyb9pib8oyb8fyb3ZSb4Zib8fCb8oyb8oyb8oyb8pCb8cSbiQyn7bCb8cib8oyb8oSb8bSbtVSjpTij8nyb8oyb8oyb8lCb2Yyf3ZCf8mCb8oyb8oyb8oyb8iib8bSbiRCn8gyb8oyb8eCbpTinrUSj8oyb8oyb8oyb8pSb8bib4Zif0YCf8byb8oyb8oyb8oyb7oib8oyb8nCbjRSn9bib8ayb8nib8oyb8oyb8oyb8kSbpTyjpTyj8jib8oyb8oyb8oyb8fib0Xyf2ZSb8gCb8oyb6pSb8oyb8dib+cCbgQCnjRSn8cCb8oib8oyb8oyb8oybqUCjnSyn8bCb8oyb8oyb8oyb8myb2YyfyXyf8oyb8oyb8hibhQSn+bib8iSb8oyb8qCb+fSbmSSnqTyj8oib9pCb1YifxXyf7pSb8oCb8pCb+mCb0fCf8pSb7hSXvcSjiQyniQinqTyj9kCb9bib9byb+cCbqUSjiRCnsVCj+cSb8pib8bCb8bSbgQCn7bCb8bibjRSn8oyb8ayb8oib8aib8pCbjRCn8pybhQinhQSn8pSb7ayb7aSb6aib8eib///8IbM+7AAAAr3RSTlMBA3NtX2vT698HGQcRLwWLiXnv++3V+eEd/R8HE2V/Y5HjyefdFw99YWfJ+/3nwQP78/HvX1VTQ/kdA2HzbQXj9fX79/3DGf379/33T/v99/f7ba33+/f1+9/18/v59V339flzF/H9+fX3/fMhBwOh9/v5/fmvBV/z+fP3Awnp9/f38+UFgff7+/37+4c77/f7/flFz/f59dFr7/v98Wnr+/f3I5/197EDBU1ZAwUD8/kLUwAAAAFiS0dEAIgFHUgAAAAHdElNRQfhBQoLHiBV6/1lAAACHUlEQVQ4y41TZXsTQRCe4FAIUigN7m7FXY+iLRQKBG2x4g7BjhZ3Le7uMoEkFJprwyQk0CC/iZnNhUZaHt4vt6/szO7cHcD/wFKjZrJWq3YMq1M3eVc9rFzXR2yQkuA3RGxkjZLGiEk9miA2tURJs1RsnhhokYYtzaU13WZDbBVnW1sjo43J2vI6tZ0lLtFeAh1M0lECneI7dGYtrUtk3RUVIKaEJR25qw27yT0s3W0qEHuPlB4RradivXo7GX36xnbo51SQ+fWHARmCgYMGDxkaxbD3SssYPmIkwKgPLrfA87EETTg/fVaSa/SYsQDjSsd7DcGEsr+BieVKmaRNBsjUtClTfUI900y/5Mt05c8oJQKYSURZ2UqYFa0w283M588JEM2BuRwI5EqT8nmmXzZf4l8XsGNfCIv4QcHFklhiBpaqAsuC4tghj+ySyOdjeJYrP7RCCuR/E5tWAqxaLcmCNSyujdxjHZdbn8UHoA0bN/GoNm8hjQJb/ZzYpo6w3TB27JRduxxqrA7YzbWCezixN8RD2Oc2/Ptlfx7o5uT1A4XMiwzj4HfEikNe7+Ew0ZGjeuW70eEYaeHjxomTiKd++E4XnKGz8d+HDufOB3Ky3RcwdNF1qZiKLyf/B44r2tWf15wV143cwI2qfi8dbtKtX6Hbd+6G74EDqkTm/QcPH/0ufFyNLXjy9NnzF9Xb8BJevYY38C+8fZcg/AF3QTYemVkCwwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxNy0wNS0xMFQxMTozMDozMiswMjowMMzup8UAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTctMDUtMTBUMTE6MzA6MzIrMDI6MDC9sx95AAAAAElFTkSuQmCC'; -export const overlayDataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAA85JREFUWAntVllIVGEUPv/9b46O41KplYN7PeRkti8TjQlhCUGh3MmeQugpIsGKAi2soIcIooiohxYKK2daqDAlIpIiWwxtQaJcaHE0d5tMrbn37z9XRqfR0TvVW56Hudf//uec72zfEWBCJjIwkYGJDPzvGSD/KgExN3Oi2Q+2DJgSDYQEMwItVGH1iZGmJw/Si1y+/PwVAMYYib22MYc/8hVQFgKDEfYoId0KYzagAQebsos/ewMZoeB9wdffcTYpQSaCTWHKoqSQaDk7zkIt0+aCUR8BelEHrf3dUNv9AcqbnsHtT5UKB/hTASh0SLYjnjb/CIDRJi0XiFAaJOpCD8zLpdb4NB66b1OfelthX815dtdRRfiti2aAXLvVLiMQ6olGyztGDkSo4JGGXk8/QFdGpYzpHG2GBQTDhtgVhPEaVbbVpvI6GJz22rv4TcAfrYI1x7Rj5MWWAppomKFVVb2302SFzUkZHAbkG+0b1+Gh77yNYjrmqnWTrLBLRxdvBWv8qlFujH/kYjJYyvLkj71t78zAUvzMAMnHhpN4zf9UREJhd8omyssxu1IgazQDwDnHUcNuH6vhPIE1fmuBzHt74Hn7W89jWGtcAjoaIDOFrdcMYJBkgOCoaRF0Lj0oglddDbCj6tRvKjphEpgjkzEQs2YAKsNxMzjn3nKurhzK+Ly7xe28ua8TwgMMcHJZnvvT0BPtEEKM4tDJ+C8GvIIk4ylINIXVZ0EUKJxYuh3mhCeokbudl6TtVc88dfBdLwbyaWB6zQCYQJpBYSrDGQxBQ/ZWRM2B+VNmQnVnHWx7elyNuL2/R336co7KyJR8CL9oLgEuFlREevWUkEl6uGwpVEG4FBm0OEf9N10NMgPlvWYAuNVwsWDKvcUNYsHUWTCZ13ysyFEXe6TO6aC8CUr9IiK+A05TQrc8yjwmxARHeeMAPlfQJw+AQRwu0YhL/GDXi9NwufG+S8dYkuYMqIb4SsWthotlNMOUCOM6r+G9cqXxPmd1dqrBav/o1zJy2l5/NUjJA/VORwYuFnOUaTQcPs9wMqwV++Xv8oADxKAcZ8nLPr8AoGW+xR6HSqYk3GodAz2QNj0V+Gr26dT9ASNH5239Pf0gktVNWZca8ZvfAFBprWS6hSu1pqt++Y0PD+WIwDAhIWQGtzvSHDbcodfFUFB9hg1Gjs5LXqIdFL+acFBl+FddqYwdxsWC3I70OvgfUaA65zhq2O2c8VxYcyIGFTVlXegYtvCXANCQZJMobjVcLMjtSK/IcEgyOOe8Ve5w7ryKDefp2P3+C/5ohv8HZmVLAAAAAElFTkSuQmCC'; +export const overlayDataUrl = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAA85JREFUWAntVllIVGEUPv/9b46O41KplYN7PeRkti8TjQlhCUGh3MmeQugpIsGKAi2soIcIooiohxYKK2daqDAlIpIiWwxtQaJcaHE0d5tMrbn37z9XRqfR0TvVW56Hudf//uec72zfEWBCJjIwkYGJDPzvGSD/KgExN3Oi2Q+2DJgSDYQEMwItVGH1iZGmJw/Si1y+/PwVAMYYib22MYc/8hVQFgKDEfYoId0KYzagAQebsos/ewMZoeB9wdffcTYpQSaCTWHKoqSQaDk7zkIt0+aCUR8BelEHrf3dUNv9AcqbnsHtT5UKB/hTASh0SLYjnjb/CIDRJi0XiFAaJOpCD8zLpdb4NB66b1OfelthX815dtdRRfiti2aAXLvVLiMQ6olGyztGDkSo4JGGXk8/QFdGpYzpHG2GBQTDhtgVhPEaVbbVpvI6GJz22rv4TcAfrYI1x7Rj5MWWAppomKFVVb2302SFzUkZHAbkG+0b1+Gh77yNYjrmqnWTrLBLRxdvBWv8qlFujH/kYjJYyvLkj71t78zAUvzMAMnHhpN4zf9UREJhd8omyssxu1IgazQDwDnHUcNuH6vhPIE1fmuBzHt74Hn7W89jWGtcAjoaIDOFrdcMYJBkgOCoaRF0Lj0oglddDbCj6tRvKjphEpgjkzEQs2YAKsNxMzjn3nKurhzK+Ly7xe28ua8TwgMMcHJZnvvT0BPtEEKM4tDJ+C8GvIIk4ylINIXVZ0EUKJxYuh3mhCeokbudl6TtVc88dfBdLwbyaWB6zQCYQJpBYSrDGQxBQ/ZWRM2B+VNmQnVnHWx7elyNuL2/R336co7KyJR8CL9oLgEuFlREevWUkEl6uGwpVEG4FBm0OEf9N10NMgPlvWYAuNVwsWDKvcUNYsHUWTCZ13ysyFEXe6TO6aC8CUr9IiK+A05TQrc8yjwmxARHeeMAPlfQJw+AQRwu0YhL/GDXi9NwufG+S8dYkuYMqIb4SsWthotlNMOUCOM6r+G9cqXxPmd1dqrBav/o1zJy2l5/NUjJA/VORwYuFnOUaTQcPs9wMqwV++Xv8oADxKAcZ8nLPr8AoGW+xR6HSqYk3GodAz2QNj0V+Gr26dT9ASNH5239Pf0gktVNWZca8ZvfAFBprWS6hSu1pqt++Y0PD+WIwDAhIWQGtzvSHDbcodfFUFB9hg1Gjs5LXqIdFL+acFBl+FddqYwdxsWC3I70OvgfUaA65zhq2O2c8VxYcyIGFTVlXegYtvCXANCQZJMobjVcLMjtSK/IcEgyOOe8Ve5w7ryKDefp2P3+C/5ohv8HZmVLAAAAAElFTkSuQmCC'; -export const faviconWithOverlayDataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAGtElEQVRYR8WXf3CT9R3H35/nSdIQIktrCf0RStI0FYRjVBAccxTq5MDBKUoz4ZyjbPO87q4yBsPDMdExTjlvIsdQexyI0oMBeuKhdjsNHhwcMgpjIlublLIm/UlJKZSSJs/z/e6+T5v0CQ22wB/7/pPck8/383l9fj6fEOec8H88NAjAS1LwknsFSVLU8WXd1rtm85LUeKnwGQKzjj3s33azvsEAAEIlnn8ByHL4/Pa7BgAQLCm8QOBOh88vDQkQeMxjMkcQEYKqYsyJWWPhgs/80TsFafzROJtkNIXFfYI0pfXqPeennjqlxPUNikBoTuEmEF+lCRBV3G0aQiWFrwH8d30AWJubGdiEfZzdGqDEEwbICnADQGGHry7zTr0X94IlnnMACggwAWh0+PxOvb5EBGqmTTNkj7ySxWS62C+g5Usm1Zn95YXG24UQ+r5n75Li6Ux4LBkyc7/4t5YSLSr6Lgg9UvBLcKocMEYKON/gGB3YoA/bcGFCczzLQdieLE9bHL66FakBSjzCU0cSAHDa4at7aLhG9XLBEk8zAVnxZxyIEhBy+PwFgwAafpxvNzK5NZUhrX28JA07Cl6SmtvcOUwm4ZAouHj7ad+jMrN1dqb3iG7oS4EYPh2etQS+XiesC8TQ3ZD3yZJsHuUPgbMcI+ej5v3ncv5PasNlk1p7JJnzJL+I0/O5h+u0VCdqIDi78AQRHuirft3hYJzQPvawPydVdPI+/OnTnNNKBjYVXHRa8rFFGeb4w1he0wZ7d/84IXTEhxzxUsgitB2LPFGwvgGUfLSeZUpEXqEqrIdz0nr4iHOUfeOccb/tNMtutzWHPeWcJc0aMxm5lkxYDGloj1zB+Sv/RXXTSXzaeBwSY3j+bHNv2bdtMYCbpHtRkNFd36xFQN3tXkZhvgP1fdPi5kMEXL4oIXKVAA58M8aCVQs84BYLXi5aDq+zGJTqYr+i4PV2vHxmJ/7WUoOn2i/jz6yhW7JjrdSV8U4fQFV+I2Q4UIsedMCSSlcsgp72WtnSajOhzDsBNtsYfFD8e+Rbs4fdIG98uw9vnj+AX7FWvk4NHZOXXphF/INx2SpJIU2L8L4GDAoMwlP9kWSg6awcKVs83tyUnY5Dj75+W8bjutae3o5d9X/HTiWAuUtOS6RUOR8Hp48TxjgU/AMSeKJ1Ej/tMWXG1sxwGt98sBxe5+xhe64XVLiK2Z9XwNgdRLXyzQsC4ENwelIHAFxDBOdh1qdCdNLCoon8RnY+HZ6/+TtzPhTZweAxlJ94C5VqoI2U3a7rACzJjQqgBd24CGscos1kxPQZ38fqSU/jhQkDvN9lrKG7FeUnNuPVKcvwYOb4hGgvi2HSx8vwRKyJkVLl+hk43gdBAcfADBD1cA4RXIdZ1EN1Zjqem+DGoUc2oigjMUlvaV8YL/1qPVpuhOG+JwdH5m1Okn3m6Eacaz3V2jeI9uTbVYY6AKOSKw8MX0MBg2lXjh3r3Hk4s7ASdrMtSWxnoBpZIzIwP3e69lxv3Gay4q/F6zDJ5kq6s6amEnsafJ0Db8P9JKkx1w5wPJuY36IToojgNMzb8rLwmsuB2kW7YDWMSCgTg+YXx9+AQZKxdUaFZiju+a2Mi8uvnH0f2/2f9g4AVE4z4LlTilrlehag9xIpEam4jO4DXfdaV97nwtH5byW137VYD5Yc2YAz4YAGIYx2RLq0z1Sex8l//fUWfBI83jh4Kd1PEuAwqVGjWEwSS+nJJmt0sWu86d0frMQCR/LbWQ8hDAxlXMgUV69Q67ubv0q5FUNAlHKmVLnXE/gfREpUiaQHqAizXbO0UN98BMTSo39Cw7UW7E2Rc728qJGHP68ASbQyNYCQTkAUzCSwQ+CwvSjnsQPGLOnI/C0YO3Lwxq5yhhtqb1KNpGqT1TXvigJU0jh33xpAf7NymoGNDJ9sJtPkYuNkqTh7KnY8vGaoeZPy93+GA1joe4kzzv/SVLqvYngA/dFgVfnlb8tjtm6Ux+I39y/Gqone24IQM+GxL15UO3q7WrhsnhJatCs8PAC9md3OrPK0goaDyEj7uXsuXi0qg4HkIUGE52XHNqmXIl0RGOiHoUV7xb+v5K14SC39At79Ximdhc8ekjImuiyjsXryUszLnY40yThIhSi4bbUHsbfBJ6ZKE5dpQdz4HQOgf2a8tLvklY+M6cuvSnJummxSZ46+X+7biMzaRnSu84IauNYsE5HCOX+HDCPWi7DrKW8/BTcVZ2UN8Me57kc5448TaCYR5XJwC0BtHMwPjs/SgAP1pfuCqSL8Pxhr/wunLWAOAAAAAElFTkSuQmCC'; +export const faviconWithOverlayDataUrl = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAGtElEQVRYR8WXf3CT9R3H35/nSdIQIktrCf0RStI0FYRjVBAccxTq5MDBKUoz4ZyjbPO87q4yBsPDMdExTjlvIsdQexyI0oMBeuKhdjsNHhwcMgpjIlublLIm/UlJKZSSJs/z/e6+T5v0CQ22wB/7/pPck8/383l9fj6fEOec8H88NAjAS1LwknsFSVLU8WXd1rtm85LUeKnwGQKzjj3s33azvsEAAEIlnn8ByHL4/Pa7BgAQLCm8QOBOh88vDQkQeMxjMkcQEYKqYsyJWWPhgs/80TsFafzROJtkNIXFfYI0pfXqPeennjqlxPUNikBoTuEmEF+lCRBV3G0aQiWFrwH8d30AWJubGdiEfZzdGqDEEwbICnADQGGHry7zTr0X94IlnnMACggwAWh0+PxOvb5EBGqmTTNkj7ySxWS62C+g5Usm1Zn95YXG24UQ+r5n75Li6Ux4LBkyc7/4t5YSLSr6Lgg9UvBLcKocMEYKON/gGB3YoA/bcGFCczzLQdieLE9bHL66FakBSjzCU0cSAHDa4at7aLhG9XLBEk8zAVnxZxyIEhBy+PwFgwAafpxvNzK5NZUhrX28JA07Cl6SmtvcOUwm4ZAouHj7ad+jMrN1dqb3iG7oS4EYPh2etQS+XiesC8TQ3ZD3yZJsHuUPgbMcI+ej5v3ncv5PasNlk1p7JJnzJL+I0/O5h+u0VCdqIDi78AQRHuirft3hYJzQPvawPydVdPI+/OnTnNNKBjYVXHRa8rFFGeb4w1he0wZ7d/84IXTEhxzxUsgitB2LPFGwvgGUfLSeZUpEXqEqrIdz0nr4iHOUfeOccb/tNMtutzWHPeWcJc0aMxm5lkxYDGloj1zB+Sv/RXXTSXzaeBwSY3j+bHNv2bdtMYCbpHtRkNFd36xFQN3tXkZhvgP1fdPi5kMEXL4oIXKVAA58M8aCVQs84BYLXi5aDq+zGJTqYr+i4PV2vHxmJ/7WUoOn2i/jz6yhW7JjrdSV8U4fQFV+I2Q4UIsedMCSSlcsgp72WtnSajOhzDsBNtsYfFD8e+Rbs4fdIG98uw9vnj+AX7FWvk4NHZOXXphF/INx2SpJIU2L8L4GDAoMwlP9kWSg6awcKVs83tyUnY5Dj75+W8bjutae3o5d9X/HTiWAuUtOS6RUOR8Hp48TxjgU/AMSeKJ1Ej/tMWXG1sxwGt98sBxe5+xhe64XVLiK2Z9XwNgdRLXyzQsC4ENwelIHAFxDBOdh1qdCdNLCoon8RnY+HZ6/+TtzPhTZweAxlJ94C5VqoI2U3a7rACzJjQqgBd24CGscos1kxPQZ38fqSU/jhQkDvN9lrKG7FeUnNuPVKcvwYOb4hGgvi2HSx8vwRKyJkVLl+hk43gdBAcfADBD1cA4RXIdZ1EN1Zjqem+DGoUc2oigjMUlvaV8YL/1qPVpuhOG+JwdH5m1Okn3m6Eacaz3V2jeI9uTbVYY6AKOSKw8MX0MBg2lXjh3r3Hk4s7ASdrMtSWxnoBpZIzIwP3e69lxv3Gay4q/F6zDJ5kq6s6amEnsafJ0Db8P9JKkx1w5wPJuY36IToojgNMzb8rLwmsuB2kW7YDWMSCgTg+YXx9+AQZKxdUaFZiju+a2Mi8uvnH0f2/2f9g4AVE4z4LlTilrlehag9xIpEam4jO4DXfdaV97nwtH5byW137VYD5Yc2YAz4YAGIYx2RLq0z1Sex8l//fUWfBI83jh4Kd1PEuAwqVGjWEwSS+nJJmt0sWu86d0frMQCR/LbWQ8hDAxlXMgUV69Q67ubv0q5FUNAlHKmVLnXE/gfREpUiaQHqAizXbO0UN98BMTSo39Cw7UW7E2Rc728qJGHP68ASbQyNYCQTkAUzCSwQ+CwvSjnsQPGLOnI/C0YO3Lwxq5yhhtqb1KNpGqT1TXvigJU0jh33xpAf7NymoGNDJ9sJtPkYuNkqTh7KnY8vGaoeZPy93+GA1joe4kzzv/SVLqvYngA/dFgVfnlb8tjtm6Ux+I39y/Gqone24IQM+GxL15UO3q7WrhsnhJatCs8PAC9md3OrPK0goaDyEj7uXsuXi0qg4HkIUGE52XHNqmXIl0RGOiHoUV7xb+v5K14SC39At79Ximdhc8ekjImuiyjsXryUszLnY40yThIhSi4bbUHsbfBJ6ZKE5dpQdz4HQOgf2a8tLvklY+M6cuvSnJummxSZ46+X+7biMzaRnSu84IauNYsE5HCOX+HDCPWi7DrKW8/BTcVZ2UN8Me57kc5448TaCYR5XJwC0BtHMwPjs/SgAP1pfuCqSL8Pxhr/wunLWAOAAAAAElFTkSuQmCC'; diff --git a/spec/javascripts/lib/utils/number_utility_spec.js b/spec/javascripts/lib/utils/number_utility_spec.js index fcf27f6805f..a5099a2a3b8 100644 --- a/spec/javascripts/lib/utils/number_utility_spec.js +++ b/spec/javascripts/lib/utils/number_utility_spec.js @@ -10,6 +10,7 @@ describe('Number Utils', () => { const formattedNumber = formatRelevantDigits('1000.1234567'); const rightFromDecimal = formattedNumber.split('.')[1]; const leftFromDecimal = formattedNumber.split('.')[0]; + expect(rightFromDecimal.length).toBe(4); expect(leftFromDecimal.length).toBe(4); }); @@ -18,6 +19,7 @@ describe('Number Utils', () => { const formattedNumber = formatRelevantDigits('0.1234567'); const rightFromDecimal = formattedNumber.split('.')[1]; const leftFromDecimal = formattedNumber.split('.')[0]; + expect(rightFromDecimal.length).toBe(3); expect(leftFromDecimal.length).toBe(1); }); @@ -26,6 +28,7 @@ describe('Number Utils', () => { const formattedNumber = formatRelevantDigits('10.1234567'); const rightFromDecimal = formattedNumber.split('.')[1]; const leftFromDecimal = formattedNumber.split('.')[0]; + expect(rightFromDecimal.length).toBe(2); expect(leftFromDecimal.length).toBe(2); }); @@ -34,6 +37,7 @@ describe('Number Utils', () => { const formattedNumber = formatRelevantDigits('100.1234567'); const rightFromDecimal = formattedNumber.split('.')[1]; const leftFromDecimal = formattedNumber.split('.')[0]; + expect(rightFromDecimal.length).toBe(1); expect(leftFromDecimal.length).toBe(3); }); diff --git a/spec/javascripts/lib/utils/poll_spec.js b/spec/javascripts/lib/utils/poll_spec.js index b28a052902e..d0da659c3d7 100644 --- a/spec/javascripts/lib/utils/poll_spec.js +++ b/spec/javascripts/lib/utils/poll_spec.js @@ -47,7 +47,7 @@ describe('Poll', () => { service.fetch.calls.reset(); }); - it('calls the success callback when no header for interval is provided', (done) => { + it('calls the success callback when no header for interval is provided', done => { mockServiceCall(service, { status: 200 }); setup(); @@ -59,7 +59,7 @@ describe('Poll', () => { }); }); - it('calls the error callback when the http request returns an error', (done) => { + it('calls the error callback when the http request returns an error', done => { mockServiceCall(service, { status: 500 }, true); setup(); @@ -71,7 +71,7 @@ describe('Poll', () => { }); }); - it('skips the error callback when request is aborted', (done) => { + it('skips the error callback when request is aborted', done => { mockServiceCall(service, { status: 0 }, true); setup(); @@ -84,19 +84,21 @@ describe('Poll', () => { }); }); - it('should call the success callback when the interval header is -1', (done) => { + it('should call the success callback when the interval header is -1', done => { mockServiceCall(service, { status: 200, headers: { 'poll-interval': -1 } }); - setup().then(() => { - expect(callbacks.success).toHaveBeenCalled(); - expect(callbacks.error).not.toHaveBeenCalled(); + setup() + .then(() => { + expect(callbacks.success).toHaveBeenCalled(); + expect(callbacks.error).not.toHaveBeenCalled(); - done(); - }).catch(done.fail); + done(); + }) + .catch(done.fail); }); describe('for 2xx status code', () => { successCodes.forEach(httpCode => { - it(`starts polling when http status is ${httpCode} and interval header is provided`, (done) => { + it(`starts polling when http status is ${httpCode} and interval header is provided`, done => { mockServiceCall(service, { status: httpCode, headers: { 'poll-interval': 1 } }); const Polling = new Poll({ @@ -124,7 +126,7 @@ describe('Poll', () => { }); describe('stop', () => { - it('stops polling when method is called', (done) => { + it('stops polling when method is called', done => { mockServiceCall(service, { status: 200, headers: { 'poll-interval': 1 } }); const Polling = new Poll({ @@ -152,7 +154,7 @@ describe('Poll', () => { }); describe('restart', () => { - it('should restart polling when its called', (done) => { + it('should restart polling when its called', done => { mockServiceCall(service, { status: 200, headers: { 'poll-interval': 1 } }); const Polling = new Poll({ diff --git a/spec/javascripts/lib/utils/sticky_spec.js b/spec/javascripts/lib/utils/sticky_spec.js index b87c836654d..1b1e7da1ed3 100644 --- a/spec/javascripts/lib/utils/sticky_spec.js +++ b/spec/javascripts/lib/utils/sticky_spec.js @@ -22,25 +22,19 @@ describe('sticky', () => { isSticky(el, 0, el.offsetTop); isSticky(el, 0, el.offsetTop); - expect( - el.classList.contains('is-stuck'), - ).toBeTruthy(); + expect(el.classList.contains('is-stuck')).toBeTruthy(); }); it('adds is-stuck class', () => { isSticky(el, 0, el.offsetTop); - expect( - el.classList.contains('is-stuck'), - ).toBeTruthy(); + expect(el.classList.contains('is-stuck')).toBeTruthy(); }); it('inserts placeholder element', () => { isSticky(el, 0, el.offsetTop, true); - expect( - document.querySelector('.sticky-placeholder'), - ).not.toBeNull(); + expect(document.querySelector('.sticky-placeholder')).not.toBeNull(); }); }); @@ -51,29 +45,22 @@ describe('sticky', () => { isSticky(el, 0, el.offsetTop); isSticky(el, 0, 0); - expect( - el.classList.remove, - ).toHaveBeenCalledWith('is-stuck'); - expect( - el.classList.contains('is-stuck'), - ).toBeFalsy(); + expect(el.classList.remove).toHaveBeenCalledWith('is-stuck'); + + expect(el.classList.contains('is-stuck')).toBeFalsy(); }); it('does not add is-stuck class', () => { isSticky(el, 0, 0); - expect( - el.classList.contains('is-stuck'), - ).toBeFalsy(); + expect(el.classList.contains('is-stuck')).toBeFalsy(); }); it('removes placeholder', () => { isSticky(el, 0, el.offsetTop, true); isSticky(el, 0, 0, true); - expect( - document.querySelector('.sticky-placeholder'), - ).toBeNull(); + expect(document.querySelector('.sticky-placeholder')).toBeNull(); }); }); }); diff --git a/spec/javascripts/lib/utils/text_markdown_spec.js b/spec/javascripts/lib/utils/text_markdown_spec.js index 043dd018e0c..bb7a29fe30a 100644 --- a/spec/javascripts/lib/utils/text_markdown_spec.js +++ b/spec/javascripts/lib/utils/text_markdown_spec.js @@ -21,7 +21,14 @@ describe('init markdown', () => { textArea.selectionStart = 0; textArea.selectionEnd = 0; - insertMarkdownText({ textArea, text: textArea.value, tag: '*', blockTag: null, selected: '', wrap: false }); + insertMarkdownText({ + textArea, + text: textArea.value, + tag: '*', + blockTag: null, + selected: '', + wrap: false, + }); expect(textArea.value).toEqual(`${initialValue}* `); }); @@ -32,7 +39,14 @@ describe('init markdown', () => { textArea.value = initialValue; textArea.setSelectionRange(initialValue.length, initialValue.length); - insertMarkdownText({ textArea, text: textArea.value, tag: '*', blockTag: null, selected: '', wrap: false }); + insertMarkdownText({ + textArea, + text: textArea.value, + tag: '*', + blockTag: null, + selected: '', + wrap: false, + }); expect(textArea.value).toEqual(`${initialValue}\n* `); }); @@ -43,7 +57,14 @@ describe('init markdown', () => { textArea.value = initialValue; textArea.setSelectionRange(initialValue.length, initialValue.length); - insertMarkdownText({ textArea, text: textArea.value, tag: '*', blockTag: null, selected: '', wrap: false }); + insertMarkdownText({ + textArea, + text: textArea.value, + tag: '*', + blockTag: null, + selected: '', + wrap: false, + }); expect(textArea.value).toEqual(`${initialValue}* `); }); @@ -54,7 +75,14 @@ describe('init markdown', () => { textArea.value = initialValue; textArea.setSelectionRange(initialValue.length, initialValue.length); - insertMarkdownText({ textArea, text: textArea.value, tag: '*', blockTag: null, selected: '', wrap: false }); + insertMarkdownText({ + textArea, + text: textArea.value, + tag: '*', + blockTag: null, + selected: '', + wrap: false, + }); expect(textArea.value).toEqual(`${initialValue}* `); }); @@ -70,13 +98,27 @@ describe('init markdown', () => { }); it('applies the tag to the selected value', () => { - insertMarkdownText({ textArea, text: textArea.value, tag: '*', blockTag: null, selected, wrap: true }); + insertMarkdownText({ + textArea, + text: textArea.value, + tag: '*', + blockTag: null, + selected, + wrap: true, + }); expect(textArea.value).toEqual(text.replace(selected, `*${selected}*`)); }); it('replaces the placeholder in the tag', () => { - insertMarkdownText({ textArea, text: textArea.value, tag: '[{text}](url)', blockTag: null, selected, wrap: false }); + insertMarkdownText({ + textArea, + text: textArea.value, + tag: '[{text}](url)', + blockTag: null, + selected, + wrap: false, + }); expect(textArea.value).toEqual(text.replace(selected, `[${selected}](url)`)); }); @@ -86,15 +128,18 @@ describe('init markdown', () => { const select = 'url'; it('selects the text', () => { - insertMarkdownText({ textArea, + insertMarkdownText({ + textArea, text: textArea.value, tag, blockTag: null, selected, wrap: false, - select }); + select, + }); const expectedText = text.replace(selected, `[${selected}](url)`); + expect(textArea.value).toEqual(expectedText); expect(textArea.selectionStart).toEqual(expectedText.indexOf(select)); expect(textArea.selectionEnd).toEqual(expectedText.indexOf(select) + select.length); @@ -105,15 +150,18 @@ describe('init markdown', () => { textArea.value = initialValue; const selectedIndex = initialValue.indexOf(selected); textArea.setSelectionRange(selectedIndex, selectedIndex + selected.length); - insertMarkdownText({ textArea, + insertMarkdownText({ + textArea, text: textArea.value, tag, blockTag: null, selected, wrap: false, - select }); + select, + }); const expectedText = initialValue.replace(selected, `[${selected}](url)`); + expect(textArea.value).toEqual(expectedText); expect(textArea.selectionStart).toEqual(expectedText.lastIndexOf(select)); expect(textArea.selectionEnd).toEqual(expectedText.lastIndexOf(select) + select.length); diff --git a/spec/javascripts/lib/utils/users_cache_spec.js b/spec/javascripts/lib/utils/users_cache_spec.js index 50371c8c5f6..6adc19bdd51 100644 --- a/spec/javascripts/lib/utils/users_cache_spec.js +++ b/spec/javascripts/lib/utils/users_cache_spec.js @@ -6,12 +6,12 @@ describe('UsersCache', () => { const dummyUser = 'has a farm'; beforeEach(() => { - UsersCache.internalStorage = { }; + UsersCache.internalStorage = {}; }); describe('get', () => { it('returns undefined for empty cache', () => { - expect(UsersCache.internalStorage).toEqual({ }); + expect(UsersCache.internalStorage).toEqual({}); const user = UsersCache.get(dummyUsername); @@ -37,7 +37,7 @@ describe('UsersCache', () => { describe('hasData', () => { it('returns false for empty cache', () => { - expect(UsersCache.internalStorage).toEqual({ }); + expect(UsersCache.internalStorage).toEqual({}); expect(UsersCache.hasData(dummyUsername)).toBe(false); }); @@ -57,11 +57,11 @@ describe('UsersCache', () => { describe('remove', () => { it('does nothing if cache is empty', () => { - expect(UsersCache.internalStorage).toEqual({ }); + expect(UsersCache.internalStorage).toEqual({}); UsersCache.remove(dummyUsername); - expect(UsersCache.internalStorage).toEqual({ }); + expect(UsersCache.internalStorage).toEqual({}); }); it('does nothing if cache contains no matching data', () => { @@ -77,7 +77,7 @@ describe('UsersCache', () => { UsersCache.remove(dummyUsername); - expect(UsersCache.internalStorage).toEqual({ }); + expect(UsersCache.internalStorage).toEqual({}); }); }); @@ -88,7 +88,7 @@ describe('UsersCache', () => { spyOn(Api, 'users').and.callFake((query, options) => apiSpy(query, options)); }); - it('stores and returns data from API call if cache is empty', (done) => { + it('stores and returns data from API call if cache is empty', done => { apiSpy = (query, options) => { expect(query).toBe(''); expect(options).toEqual({ username: dummyUsername }); @@ -98,15 +98,15 @@ describe('UsersCache', () => { }; UsersCache.retrieve(dummyUsername) - .then((user) => { - expect(user).toBe(dummyUser); - expect(UsersCache.internalStorage[dummyUsername]).toBe(dummyUser); - }) - .then(done) - .catch(done.fail); + .then(user => { + expect(user).toBe(dummyUser); + expect(UsersCache.internalStorage[dummyUsername]).toBe(dummyUser); + }) + .then(done) + .catch(done.fail); }); - it('returns undefined if Ajax call fails and cache is empty', (done) => { + it('returns undefined if Ajax call fails and cache is empty', done => { const dummyError = new Error('server exploded'); apiSpy = (query, options) => { expect(query).toBe(''); @@ -115,24 +115,24 @@ describe('UsersCache', () => { }; UsersCache.retrieve(dummyUsername) - .then(user => fail(`Received unexpected user: ${JSON.stringify(user)}`)) - .catch((error) => { - expect(error).toBe(dummyError); - }) - .then(done) - .catch(done.fail); + .then(user => fail(`Received unexpected user: ${JSON.stringify(user)}`)) + .catch(error => { + expect(error).toBe(dummyError); + }) + .then(done) + .catch(done.fail); }); - it('makes no Ajax call if matching data exists', (done) => { + it('makes no Ajax call if matching data exists', done => { UsersCache.internalStorage[dummyUsername] = dummyUser; apiSpy = () => fail(new Error('expected no Ajax call!')); UsersCache.retrieve(dummyUsername) - .then((user) => { - expect(user).toBe(dummyUser); - }) - .then(done) - .catch(done.fail); + .then(user => { + expect(user).toBe(dummyUser); + }) + .then(done) + .catch(done.fail); }); }); }); diff --git a/spec/javascripts/line_highlighter_spec.js b/spec/javascripts/line_highlighter_spec.js index 15bb78032b2..4eea364bd69 100644 --- a/spec/javascripts/line_highlighter_spec.js +++ b/spec/javascripts/line_highlighter_spec.js @@ -1,244 +1,265 @@ -/* eslint-disable no-var, prefer-template, no-else-return, dot-notation, no-return-assign, no-new, one-var, no-underscore-dangle */ +/* eslint-disable no-var, prefer-template, no-else-return, dot-notation, no-return-assign, no-new, no-underscore-dangle */ import $ from 'jquery'; import LineHighlighter from '~/line_highlighter'; -(function() { - describe('LineHighlighter', function() { - var clickLine; - preloadFixtures('static/line_highlighter.html.raw'); - clickLine = function(number, eventData = {}) { - if ($.isEmptyObject(eventData)) { - return $("#L" + number).click(); - } else { - const e = $.Event('click', eventData); - return $("#L" + number).trigger(e); +describe('LineHighlighter', function() { + var clickLine; + preloadFixtures('static/line_highlighter.html.raw'); + clickLine = function(number, eventData = {}) { + if ($.isEmptyObject(eventData)) { + return $('#L' + number).click(); + } else { + const e = $.Event('click', eventData); + return $('#L' + number).trigger(e); + } + }; + beforeEach(function() { + loadFixtures('static/line_highlighter.html.raw'); + this['class'] = new LineHighlighter(); + this.css = this['class'].highlightLineClass; + 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({ hash: '#L13' }); + + expect($('#LC13')).toHaveClass(this.css); + }); + + it('highlights one line given in the URL hash with given CSS class name', function() { + const hiliter = new LineHighlighter({ hash: '#L13', highlightLineClass: 'hilite' }); + + expect(hiliter.highlightLineClass).toBe('hilite'); + expect($('#LC13')).toHaveClass('hilite'); + expect($('#LC13')).not.toHaveClass('hll'); + }); + + it('highlights a range of lines given in the URL hash', function() { + var line; + new LineHighlighter({ hash: '#L5-25' }); + + expect($('.' + this.css).length).toBe(21); + for (line = 5; line <= 25; line += 1) { + expect($('#LC' + line)).toHaveClass(this.css); } - }; - beforeEach(function() { - loadFixtures('static/line_highlighter.html.raw'); - this["class"] = new LineHighlighter(); - this.css = this["class"].highlightLineClass; - return this.spies = { - __setLocationHash__: spyOn(this["class"], '__setLocationHash__').and.callFake(function() {}) + }); + + it('scrolls to the first highlighted line on initial load', function() { + var spy; + spy = spyOn($, 'scrollTo'); + new LineHighlighter({ hash: '#L5-25' }); + + expect(spy).toHaveBeenCalledWith('#L5', jasmine.anything()); + }); + + it('discards click events', function() { + var spy; + spy = spyOnEvent('a[data-line-number]', 'click'); + clickLine(13); + + expect(spy).toHaveBeenPrevented(); + }); + + it('handles garbage input from the hash', function() { + var func; + func = function() { + return new LineHighlighter({ fileHolderSelector: '#blob-content-holder' }); }; + + expect(func).not.toThrow(); }); + }); - describe('behavior', function() { - it('highlights one line given in the URL hash', function() { - new LineHighlighter({ hash: '#L13' }); - return expect($('#LC13')).toHaveClass(this.css); - }); + describe('clickHandler', function() { + it('handles clicking on a child icon element', function() { + var spy; + spy = spyOn(this['class'], 'setHash').and.callThrough(); + $('#L13 i') + .mousedown() + .click(); - it('highlights one line given in the URL hash with given CSS class name', function() { - const hiliter = new LineHighlighter({ hash: '#L13', highlightLineClass: 'hilite' }); - expect(hiliter.highlightLineClass).toBe('hilite'); - expect($('#LC13')).toHaveClass('hilite'); - expect($('#LC13')).not.toHaveClass('hll'); - }); + expect(spy).toHaveBeenCalledWith(13); + expect($('#LC13')).toHaveClass(this.css); + }); + + describe('without shiftKey', function() { + it('highlights one line when clicked', function() { + clickLine(13); - it('highlights a range of lines given in the URL hash', function() { - var line, results; - new LineHighlighter({ hash: '#L5-25' }); - expect($("." + this.css).length).toBe(21); - results = []; - for (line = 5; line <= 25; line += 1) { - results.push(expect($("#LC" + line)).toHaveClass(this.css)); - } - return results; + expect($('#LC13')).toHaveClass(this.css); }); - it('scrolls to the first highlighted line on initial load', function() { - var spy; - spy = spyOn($, 'scrollTo'); - new LineHighlighter({ hash: '#L5-25' }); - return expect(spy).toHaveBeenCalledWith('#L5', jasmine.anything()); + it('unhighlights previously highlighted lines', function() { + clickLine(13); + clickLine(20); + + expect($('#LC13')).not.toHaveClass(this.css); + expect($('#LC20')).toHaveClass(this.css); }); - it('discards click events', function() { + it('sets the hash', function() { var spy; - spy = spyOnEvent('a[data-line-number]', 'click'); + spy = spyOn(this['class'], 'setHash').and.callThrough(); clickLine(13); - return expect(spy).toHaveBeenPrevented(); - }); - it('handles garbage input from the hash', function() { - var func; - func = function() { - return new LineHighlighter({ fileHolderSelector: '#blob-content-holder' }); - }; - return expect(func).not.toThrow(); + expect(spy).toHaveBeenCalledWith(13); }); }); - describe('clickHandler', function() { - it('handles clicking on a child icon element', function() { + describe('with shiftKey', function() { + it('sets the hash', function() { var spy; - spy = spyOn(this["class"], 'setHash').and.callThrough(); - $('#L13 i').mousedown().click(); + spy = spyOn(this['class'], 'setHash').and.callThrough(); + clickLine(13); + clickLine(20, { + shiftKey: true, + }); + expect(spy).toHaveBeenCalledWith(13); - return expect($('#LC13')).toHaveClass(this.css); + expect(spy).toHaveBeenCalledWith(13, 20); }); - describe('without shiftKey', function() { - it('highlights one line when clicked', function() { - clickLine(13); - return expect($('#LC13')).toHaveClass(this.css); - }); + describe('without existing highlight', function() { + it('highlights the clicked line', function() { + clickLine(13, { + shiftKey: true, + }); - 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); + expect($('#LC13')).toHaveClass(this.css); + expect($('.' + this.css).length).toBe(1); }); - }); - 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 + spy = spyOn(this['class'], 'setHash'); + clickLine(13, { + 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); + describe('with existing single-line highlight', function() { + it('uses existing line as last line when target is lesser', function() { + var line; + clickLine(20); + clickLine(15, { + shiftKey: true, }); - return it('sets the hash', function() { - var spy; - spy = spyOn(this["class"], 'setHash'); - clickLine(13, { - shiftKey: true - }); - return expect(spy).toHaveBeenCalledWith(13); + + expect($('.' + this.css).length).toBe(6); + for (line = 15; line <= 20; line += 1) { + expect($('#LC' + line)).toHaveClass(this.css); + } + }); + + it('uses existing line as first line when target is greater', function() { + var line; + clickLine(5); + clickLine(10, { + shiftKey: true, }); + + expect($('.' + this.css).length).toBe(6); + for (line = 5; line <= 10; line += 1) { + expect($('#LC' + line)).toHaveClass(this.css); + } }); + }); - describe('with existing single-line highlight', function() { - it('uses existing line as last line when target is lesser', function() { - var line, results; - clickLine(20); - clickLine(15, { - shiftKey: true - }); - expect($("." + this.css).length).toBe(6); - results = []; - for (line = 15; line <= 20; line += 1) { - results.push(expect($("#LC" + line)).toHaveClass(this.css)); - } - return results; + describe('with existing multi-line highlight', function() { + beforeEach(function() { + clickLine(10, { + shiftKey: true, }); - return it('uses existing line as first line when target is greater', function() { - var line, results; - clickLine(5); - clickLine(10, { - shiftKey: true - }); - expect($("." + this.css).length).toBe(6); - results = []; - for (line = 5; line <= 10; line += 1) { - results.push(expect($("#LC" + line)).toHaveClass(this.css)); - } - return results; + clickLine(13, { + shiftKey: true, }); }); - 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 line, results; - clickLine(5, { - shiftKey: true - }); - expect($("." + this.css).length).toBe(6); - results = []; - for (line = 5; line <= 10; line += 1) { - results.push(expect($("#LC" + line)).toHaveClass(this.css)); - } - return results; + it('uses target as first line when it is less than existing first line', function() { + var line; + clickLine(5, { + shiftKey: true, }); - return it('uses target as last line when it is greater than existing first line', function() { - var line, results; - clickLine(15, { - shiftKey: true - }); - expect($("." + this.css).length).toBe(6); - results = []; - for (line = 10; line <= 15; line += 1) { - results.push(expect($("#LC" + line)).toHaveClass(this.css)); - } - return results; + + expect($('.' + this.css).length).toBe(6); + for (line = 5; line <= 10; line += 1) { + expect($('#LC' + line)).toHaveClass(this.css); + } + }); + + it('uses target as last line when it is greater than existing first line', function() { + var line; + clickLine(15, { + shiftKey: true, }); + + expect($('.' + this.css).length).toBe(6); + for (line = 10; line <= 15; line += 1) { + expect($('#LC' + line)).toHaveClass(this.css); + } }); }); }); + }); - describe('hashToRange', function() { - beforeEach(function() { - return this.subject = this["class"].hashToRange; - }); + describe('hashToRange', function() { + beforeEach(function() { + 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 single line number from the hash', function() { + 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]); - }); + it('extracts a range of line numbers from the hash', function() { + expect(this.subject('#L5-15')).toEqual([5, 15]); }); - describe('highlightLine', function() { - beforeEach(function() { - return this.subject = this["class"].highlightLine; - }); + it('returns [null, null] when the hash is not a line number', function() { + expect(this.subject('#foo')).toEqual([null, null]); + }); + }); - 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); - }); + describe('highlightLine', function() { + beforeEach(function() { + this.subject = this['class'].highlightLine; }); - 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'); - }); + it('highlights the specified line', function() { + this.subject(13); + + expect($('#LC13')).toHaveClass(this.css); + }); + + it('accepts a String-based number', function() { + this.subject('13'); + + expect($('#LC13')).toHaveClass(this.css); + }); + }); + + describe('setHash', function() { + beforeEach(function() { + this.subject = this['class'].setHash; + }); + + it('sets the location hash for a single line', function() { + this.subject(5); + + expect(this.spies.__setLocationHash__).toHaveBeenCalledWith('#L5'); + }); + + it('sets the location hash for a range', function() { + this.subject(5, 15); + + expect(this.spies.__setLocationHash__).toHaveBeenCalledWith('#L5-15'); }); }); -}).call(window); +}); diff --git a/spec/javascripts/locale/ensure_single_line_spec.js b/spec/javascripts/locale/ensure_single_line_spec.js index bbefa8f40f3..20b04cab9c8 100644 --- a/spec/javascripts/locale/ensure_single_line_spec.js +++ b/spec/javascripts/locale/ensure_single_line_spec.js @@ -4,6 +4,7 @@ describe('locale', () => { describe('ensureSingleLine', () => { it('should remove newlines at the start of the string', () => { const result = 'Test'; + expect(ensureSingleLine(`\n${result}`)).toBe(result); expect(ensureSingleLine(`\t\n\t${result}`)).toBe(result); expect(ensureSingleLine(`\r\n${result}`)).toBe(result); @@ -14,6 +15,7 @@ describe('locale', () => { it('should remove newlines at the end of the string', () => { const result = 'Test'; + expect(ensureSingleLine(`${result}\n`)).toBe(result); expect(ensureSingleLine(`${result}\t\n\t`)).toBe(result); expect(ensureSingleLine(`${result}\r\n`)).toBe(result); @@ -24,6 +26,7 @@ describe('locale', () => { it('should replace newlines in the middle of the string with a single space', () => { const result = 'Test'; + expect(ensureSingleLine(`${result}\n${result}`)).toBe(`${result} ${result}`); expect(ensureSingleLine(`${result}\t\n\t${result}`)).toBe(`${result} ${result}`); expect(ensureSingleLine(`${result}\r\n${result}`)).toBe(`${result} ${result}`); diff --git a/spec/javascripts/merge_request_spec.js b/spec/javascripts/merge_request_spec.js index 7502f1fa2e1..1cb49b49ca7 100644 --- a/spec/javascripts/merge_request_spec.js +++ b/spec/javascripts/merge_request_spec.js @@ -7,123 +7,122 @@ import MergeRequest from '~/merge_request'; import CloseReopenReportToggle from '~/close_reopen_report_toggle'; import IssuablesHelper from '~/helpers/issuables_helper'; -(function() { - describe('MergeRequest', function() { - describe('task lists', function() { - let mock; +describe('MergeRequest', function() { + describe('task lists', function() { + let mock; - preloadFixtures('merge_requests/merge_request_with_task_list.html.raw'); - beforeEach(function() { - loadFixtures('merge_requests/merge_request_with_task_list.html.raw'); + preloadFixtures('merge_requests/merge_request_with_task_list.html.raw'); + beforeEach(function() { + loadFixtures('merge_requests/merge_request_with_task_list.html.raw'); - spyOn(axios, 'patch').and.callThrough(); - mock = new MockAdapter(axios); + spyOn(axios, 'patch').and.callThrough(); + mock = new MockAdapter(axios); - mock - .onPatch(`${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`) - .reply(200, {}); + mock + .onPatch(`${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`) + .reply(200, {}); - return (this.merge = new MergeRequest()); - }); + return (this.merge = new MergeRequest()); + }); - afterEach(() => { - mock.restore(); - }); + afterEach(() => { + mock.restore(); + }); - it('modifies the Markdown field', function() { - spyOn($, 'ajax').and.stub(); - const changeEvent = document.createEvent('HTMLEvents'); - changeEvent.initEvent('change', true, true); - $('input[type=checkbox]') - .attr('checked', true)[0] - .dispatchEvent(changeEvent); - return expect($('.js-task-list-field').val()).toBe('- [x] Task List Item'); - }); + it('modifies the Markdown field', function() { + spyOn($, 'ajax').and.stub(); + const changeEvent = document.createEvent('HTMLEvents'); + changeEvent.initEvent('change', true, true); + $('input[type=checkbox]') + .attr('checked', true)[0] + .dispatchEvent(changeEvent); - it('submits an ajax request on tasklist:changed', done => { - $('.js-task-list-field').trigger('tasklist:changed'); - - setTimeout(() => { - expect(axios.patch).toHaveBeenCalledWith( - `${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`, - { - merge_request: { description: '- [ ] Task List Item' }, - }, - ); - done(); - }); - }); + expect($('.js-task-list-field').val()).toBe('- [x] Task List Item'); }); - describe('class constructor', () => { - beforeEach(() => { - spyOn($, 'ajax').and.stub(); + it('submits an ajax request on tasklist:changed', done => { + $('.js-task-list-field').trigger('tasklist:changed'); + + setTimeout(() => { + expect(axios.patch).toHaveBeenCalledWith( + `${gl.TEST_HOST}/frontend-fixtures/merge-requests-project/merge_requests/1.json`, + { + merge_request: { description: '- [ ] Task List Item' }, + }, + ); + done(); }); + }); + }); - it('calls .initCloseReopenReport', () => { - spyOn(IssuablesHelper, 'initCloseReopenReport'); + describe('class constructor', () => { + beforeEach(() => { + spyOn($, 'ajax').and.stub(); + }); - new MergeRequest(); // eslint-disable-line no-new + it('calls .initCloseReopenReport', () => { + spyOn(IssuablesHelper, 'initCloseReopenReport'); - expect(IssuablesHelper.initCloseReopenReport).toHaveBeenCalled(); - }); + new MergeRequest(); // eslint-disable-line no-new - it('calls .initDroplab', () => { - const container = jasmine.createSpyObj('container', ['querySelector']); - const dropdownTrigger = {}; - const dropdownList = {}; - const button = {}; + expect(IssuablesHelper.initCloseReopenReport).toHaveBeenCalled(); + }); + + it('calls .initDroplab', () => { + const container = jasmine.createSpyObj('container', ['querySelector']); + const dropdownTrigger = {}; + const dropdownList = {}; + const button = {}; + + spyOn(CloseReopenReportToggle.prototype, 'initDroplab'); + spyOn(document, 'querySelector').and.returnValue(container); + container.querySelector.and.returnValues(dropdownTrigger, dropdownList, button); - spyOn(CloseReopenReportToggle.prototype, 'initDroplab'); - spyOn(document, 'querySelector').and.returnValue(container); - container.querySelector.and.returnValues(dropdownTrigger, dropdownList, button); + new MergeRequest(); // eslint-disable-line no-new + + expect(document.querySelector).toHaveBeenCalledWith('.js-issuable-close-dropdown'); + expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-toggle'); + expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-menu'); + expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-button'); + expect(CloseReopenReportToggle.prototype.initDroplab).toHaveBeenCalled(); + }); + }); + describe('hideCloseButton', () => { + describe('merge request of another user', () => { + beforeEach(() => { + loadFixtures('merge_requests/merge_request_with_task_list.html.raw'); + this.el = document.querySelector('.js-issuable-actions'); new MergeRequest(); // eslint-disable-line no-new + MergeRequest.hideCloseButton(); + }); - expect(document.querySelector).toHaveBeenCalledWith('.js-issuable-close-dropdown'); - expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-toggle'); - expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-menu'); - expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-button'); - expect(CloseReopenReportToggle.prototype.initDroplab).toHaveBeenCalled(); + it('hides the dropdown close item and selects the next item', () => { + const closeItem = this.el.querySelector('li.close-item'); + const smallCloseItem = this.el.querySelector('.js-close-item'); + const reportItem = this.el.querySelector('li.report-item'); + + expect(closeItem).toHaveClass('hidden'); + expect(smallCloseItem).toHaveClass('hidden'); + expect(reportItem).toHaveClass('droplab-item-selected'); + expect(reportItem).not.toHaveClass('hidden'); }); }); - describe('hideCloseButton', () => { - describe('merge request of another user', () => { - beforeEach(() => { - loadFixtures('merge_requests/merge_request_with_task_list.html.raw'); - this.el = document.querySelector('.js-issuable-actions'); - new MergeRequest(); // eslint-disable-line no-new - MergeRequest.hideCloseButton(); - }); - - it('hides the dropdown close item and selects the next item', () => { - const closeItem = this.el.querySelector('li.close-item'); - const smallCloseItem = this.el.querySelector('.js-close-item'); - const reportItem = this.el.querySelector('li.report-item'); - - expect(closeItem).toHaveClass('hidden'); - expect(smallCloseItem).toHaveClass('hidden'); - expect(reportItem).toHaveClass('droplab-item-selected'); - expect(reportItem).not.toHaveClass('hidden'); - }); + describe('merge request of current_user', () => { + beforeEach(() => { + loadFixtures('merge_requests/merge_request_of_current_user.html.raw'); + this.el = document.querySelector('.js-issuable-actions'); + MergeRequest.hideCloseButton(); }); - describe('merge request of current_user', () => { - beforeEach(() => { - loadFixtures('merge_requests/merge_request_of_current_user.html.raw'); - this.el = document.querySelector('.js-issuable-actions'); - MergeRequest.hideCloseButton(); - }); - - it('hides the close button', () => { - const closeButton = this.el.querySelector('.btn-close'); - const smallCloseItem = this.el.querySelector('.js-close-item'); + it('hides the close button', () => { + const closeButton = this.el.querySelector('.btn-close'); + const smallCloseItem = this.el.querySelector('.js-close-item'); - expect(closeButton).toHaveClass('hidden'); - expect(smallCloseItem).toHaveClass('hidden'); - }); + expect(closeButton).toHaveClass('hidden'); + expect(smallCloseItem).toHaveClass('hidden'); }); }); }); -}.call(window)); +}); diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js index 7251ce19a90..7714197c821 100644 --- a/spec/javascripts/merge_request_tabs_spec.js +++ b/spec/javascripts/merge_request_tabs_spec.js @@ -224,6 +224,14 @@ describe('MergeRequestTabs', function() { expect($('.content-wrapper')).not.toContainElement('.container-limited'); }); + it('does not add container-limited when fluid layout is prefered', function() { + $('.content-wrapper .container-fluid').removeClass('container-limited'); + + this.class.expandViewContainer(false); + + expect($('.content-wrapper')).not.toContainElement('.container-limited'); + }); + it('does remove container-limited from breadcrumbs', function() { $('.container-limited').addClass('breadcrumbs'); this.class.expandViewContainer(); diff --git a/spec/javascripts/mini_pipeline_graph_dropdown_spec.js b/spec/javascripts/mini_pipeline_graph_dropdown_spec.js index 1879424c629..092ca9e1dab 100644 --- a/spec/javascripts/mini_pipeline_graph_dropdown_spec.js +++ b/spec/javascripts/mini_pipeline_graph_dropdown_spec.js @@ -39,10 +39,9 @@ describe('Mini Pipeline Graph Dropdown', () => { }); it('should call getBuildsList', () => { - const getBuildsListSpy = spyOn( - MiniPipelineGraph.prototype, - 'getBuildsList', - ).and.callFake(function () {}); + const getBuildsListSpy = spyOn(MiniPipelineGraph.prototype, 'getBuildsList').and.callFake( + function() {}, + ); new MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents(); @@ -61,10 +60,11 @@ describe('Mini Pipeline Graph Dropdown', () => { new MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents(); document.querySelector('.js-builds-dropdown-button').click(); + expect(ajaxSpy.calls.allArgs()[0][0]).toEqual('foobar'); }); - it('should not close when user uses cmd/ctrl + click', (done) => { + it('should not close when user uses cmd/ctrl + click', done => { mock.onGet('foobar').reply(200, { html: `<li> <a class="mini-pipeline-graph-dropdown-item" href="#"> @@ -90,7 +90,7 @@ describe('Mini Pipeline Graph Dropdown', () => { .catch(done.fail); }); - it('should close the dropdown when request returns an error', (done) => { + it('should close the dropdown when request returns an error', done => { mock.onGet('foobar').networkError(); new MiniPipelineGraph({ container: '.js-builds-dropdown-tests' }).bindEvents(); diff --git a/spec/javascripts/monitoring/dashboard_spec.js b/spec/javascripts/monitoring/dashboard_spec.js index 732c37a24bf..a3477c5f8c6 100644 --- a/spec/javascripts/monitoring/dashboard_spec.js +++ b/spec/javascripts/monitoring/dashboard_spec.js @@ -107,6 +107,7 @@ describe('Dashboard', () => { setTimeout(() => { const dropdownMenuEnvironments = component.$el.querySelectorAll('.dropdown-menu ul li a'); + expect(dropdownMenuEnvironments.length).toEqual(component.store.environmentsData.length); done(); }); @@ -124,6 +125,7 @@ describe('Dashboard', () => { const dropdownIsActiveElement = component.$el.querySelectorAll( '.dropdown-menu ul li a.is-active', ); + expect(dropdownIsActiveElement.length).toEqual(1); expect(dropdownIsActiveElement[0].textContent.trim()).toEqual( component.currentEnvironmentName, diff --git a/spec/javascripts/monitoring/dashboard_state_spec.js b/spec/javascripts/monitoring/dashboard_state_spec.js index b4c5f4baa78..6b2be83aa8c 100644 --- a/spec/javascripts/monitoring/dashboard_state_spec.js +++ b/spec/javascripts/monitoring/dashboard_state_spec.js @@ -56,9 +56,17 @@ describe('EmptyState', () => { }); expect(component.$el.querySelector('svg')).toBeDefined(); - expect(getTextFromNode(component, '.state-title')).toEqual(component.states.gettingStarted.title); - expect(getTextFromNode(component, '.state-description')).toEqual(component.states.gettingStarted.description); - expect(getTextFromNode(component, '.btn-success')).toEqual(component.states.gettingStarted.buttonText); + expect(getTextFromNode(component, '.state-title')).toEqual( + component.states.gettingStarted.title, + ); + + expect(getTextFromNode(component, '.state-description')).toEqual( + component.states.gettingStarted.description, + ); + + expect(getTextFromNode(component, '.btn-success')).toEqual( + component.states.gettingStarted.buttonText, + ); }); it('should show the loading state', () => { @@ -68,7 +76,10 @@ describe('EmptyState', () => { expect(component.$el.querySelector('svg')).toBeDefined(); expect(getTextFromNode(component, '.state-title')).toEqual(component.states.loading.title); - expect(getTextFromNode(component, '.state-description')).toEqual(component.states.loading.description); + expect(getTextFromNode(component, '.state-description')).toEqual( + component.states.loading.description, + ); + expect(getTextFromNode(component, '.btn-success')).toEqual(component.states.loading.buttonText); }); @@ -78,8 +89,13 @@ describe('EmptyState', () => { }); expect(component.$el.querySelector('svg')).toBeDefined(); - expect(getTextFromNode(component, '.state-title')).toEqual(component.states.unableToConnect.title); + expect(getTextFromNode(component, '.state-title')).toEqual( + component.states.unableToConnect.title, + ); + expect(component.$el.querySelector('.state-description a')).toBeDefined(); - expect(getTextFromNode(component, '.btn-success')).toEqual(component.states.unableToConnect.buttonText); + expect(getTextFromNode(component, '.btn-success')).toEqual( + component.states.unableToConnect.buttonText, + ); }); }); diff --git a/spec/javascripts/monitoring/graph/deployment_spec.js b/spec/javascripts/monitoring/graph/deployment_spec.js index d07db871d69..7d39c4345d2 100644 --- a/spec/javascripts/monitoring/graph/deployment_spec.js +++ b/spec/javascripts/monitoring/graph/deployment_spec.js @@ -2,7 +2,7 @@ import Vue from 'vue'; import GraphDeployment from '~/monitoring/components/graph/deployment.vue'; import { deploymentData } from '../mock_data'; -const createComponent = (propsData) => { +const createComponent = propsData => { const Component = Vue.extend(GraphDeployment); return new Component({ @@ -33,9 +33,7 @@ describe('MonitoringDeployment', () => { graphHeightOffset: 120, }); - expect( - component.transformDeploymentGroup({ xPos: 16 }), - ).toContain('translate(11, 20)'); + expect(component.transformDeploymentGroup({ xPos: 16 })).toContain('translate(11, 20)'); }); describe('Computed props', () => { diff --git a/spec/javascripts/monitoring/graph_path_spec.js b/spec/javascripts/monitoring/graph_path_spec.js index 5f270c5cfe9..fd167b83d51 100644 --- a/spec/javascripts/monitoring/graph_path_spec.js +++ b/spec/javascripts/monitoring/graph_path_spec.js @@ -3,7 +3,7 @@ import GraphPath from '~/monitoring/components/graph/path.vue'; import createTimeSeries from '~/monitoring/utils/multiple_time_series'; import { singleRowMetricsMultipleSeries, convertDatesMultipleSeries } from './mock_data'; -const createComponent = (propsData) => { +const createComponent = propsData => { const Component = Vue.extend(GraphPath); return new Component({ @@ -45,9 +45,11 @@ describe('Monitoring Paths', () => { }); component.lineStyle = 'dashed'; + expect(component.strokeDashArray).toBe('3, 1'); component.lineStyle = 'dotted'; + expect(component.strokeDashArray).toBe('1, 1'); }); }); diff --git a/spec/javascripts/monitoring/graph_spec.js b/spec/javascripts/monitoring/graph_spec.js index 99180e4d303..4cc18afdf24 100644 --- a/spec/javascripts/monitoring/graph_spec.js +++ b/spec/javascripts/monitoring/graph_spec.js @@ -49,6 +49,7 @@ describe('Graph', () => { }); const transformedHeight = `${component.graphHeight - 100}`; + expect(component.axisTransform.indexOf(transformedHeight)).not.toEqual(-1); }); @@ -62,6 +63,7 @@ describe('Graph', () => { }); const viewBoxArray = component.outerViewBox.split(' '); + expect(typeof component.outerViewBox).toEqual('string'); expect(viewBoxArray[2]).toEqual(component.graphWidth.toString()); expect(viewBoxArray[3]).toEqual((component.graphHeight - 50).toString()); @@ -99,6 +101,7 @@ describe('Graph', () => { component.seriesUnderMouse = component.timeSeries; component.positionFlag(); + expect(component.currentData).toBe(component.timeSeries[0].values[10]); }); }); diff --git a/spec/javascripts/monitoring/monitoring_store_spec.js b/spec/javascripts/monitoring/monitoring_store_spec.js index ccdf4eda563..bf68c911549 100644 --- a/spec/javascripts/monitoring/monitoring_store_spec.js +++ b/spec/javascripts/monitoring/monitoring_store_spec.js @@ -1,7 +1,7 @@ import MonitoringStore from '~/monitoring/stores/monitoring_store'; import MonitoringMock, { deploymentData, environmentData } from './mock_data'; -describe('MonitoringStore', function () { +describe('MonitoringStore', function() { this.store = new MonitoringStore(); this.store.storeMetrics(MonitoringMock.data); @@ -17,6 +17,7 @@ describe('MonitoringStore', function () { it('contains deployment data', () => { this.store.storeDeploymentData(deploymentData); + expect(this.store.deploymentData).toBeDefined(); expect(this.store.deploymentData.length).toEqual(3); expect(typeof this.store.deploymentData[0]).toEqual('object'); @@ -24,6 +25,7 @@ describe('MonitoringStore', function () { it('only stores environment data that contains deployments', () => { this.store.storeEnvironmentsData(environmentData); + expect(this.store.environmentsData.length).toEqual(2); }); }); diff --git a/spec/javascripts/new_branch_spec.js b/spec/javascripts/new_branch_spec.js index 85419e640d8..1d7b885e64f 100644 --- a/spec/javascripts/new_branch_spec.js +++ b/spec/javascripts/new_branch_spec.js @@ -1,192 +1,199 @@ -/* eslint-disable one-var, no-var, no-return-assign */ - import $ from 'jquery'; import NewBranchForm from '~/new_branch_form'; -(function() { - describe('Branch', function() { - return describe('create a new branch', function() { - var expectToHaveError, fillNameWith; - preloadFixtures('branches/new_branch.html.raw'); - 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() { - loadFixtures('branches/new_branch.html.raw'); - $('form').on('submit', function(e) { - return e.preventDefault(); - }); - return this.form = new NewBranchForm($('.js-create-branch-form'), []); - }); +describe('Branch', function() { + describe('create a new branch', function() { + preloadFixtures('branches/new_branch.html.raw'); - it("can't start with a dot", function() { - fillNameWith('.foo'); - return expectToHaveError("can't start with '.'"); - }); + function fillNameWith(value) { + $('.js-branch-name') + .val(value) + .trigger('blur'); + } - it("can't start with a slash", function() { - fillNameWith('/foo'); - return expectToHaveError("can't start with '/'"); - }); + function expectToHaveError(error) { + expect($('.js-branch-name-error span').text()).toEqual(error); + } - it("can't have two consecutive dots", function() { - fillNameWith('foo..bar'); - return expectToHaveError("can't contain '..'"); + beforeEach(function() { + loadFixtures('branches/new_branch.html.raw'); + $('form').on('submit', function(e) { + return e.preventDefault(); }); + this.form = new NewBranchForm($('.js-create-branch-form'), []); + }); - 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 start with a dot", function() { + fillNameWith('.foo'); + expectToHaveError("can't start with '.'"); + }); - 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 start with a slash", function() { + fillNameWith('/foo'); + expectToHaveError("can't start with '/'"); + }); - 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 two consecutive dots", function() { + fillNameWith('foo..bar'); + 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 spaces anywhere", function() { + 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", 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~'); + 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 tilde anwhere", function() { + 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", 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^'); + 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 : anywhere", function() { + 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", 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?'); + 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 asterisk anywhere", function() { + 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", function() { - fillNameWith('foo//bar'); - return expectToHaveError("can't contain consecutive slashes"); - }); + it("can't have open bracket anywhere", function() { + fillNameWith('[foo'); + expectToHaveError("can't contain '['"); + fillNameWith('foo[bar'); + expectToHaveError("can't contain '['"); + fillNameWith('foo['); + expectToHaveError("can't contain '['"); + }); - it("can't end with a slash", function() { - fillNameWith('foo/'); - return expectToHaveError("can't end in '/'"); - }); + it("can't have a backslash anywhere", function() { + fillNameWith('\\foo'); + expectToHaveError("can't contain '\\'"); + fillNameWith('foo\\bar'); + expectToHaveError("can't contain '\\'"); + fillNameWith('foo\\'); + expectToHaveError("can't contain '\\'"); + }); - it("can't end with a dot", function() { - fillNameWith('foo.'); - return expectToHaveError("can't end in '.'"); - }); + it("can't contain a sequence @{ anywhere", function() { + fillNameWith('@{foo'); + expectToHaveError("can't contain '@{'"); + fillNameWith('foo@{bar'); + expectToHaveError("can't contain '@{'"); + fillNameWith('foo@{'); + expectToHaveError("can't contain '@{'"); + }); - it("can't end with .lock", function() { - fillNameWith('foo.lock'); - return expectToHaveError("can't end in '.lock'"); - }); + it("can't have consecutive slashes", function() { + fillNameWith('foo//bar'); + expectToHaveError("can't contain consecutive slashes"); + }); - it("can't be the single character @", function() { - fillNameWith('@'); - return expectToHaveError("can't be '@'"); - }); + it("can't end with a slash", function() { + fillNameWith('foo/'); + expectToHaveError("can't end in '/'"); + }); - it("concatenates all error messages", function() { - fillNameWith('/foo bar?~.'); - return expectToHaveError("can't start with '/', can't contain spaces, '?', '~', can't end in '.'"); - }); + it("can't end with a dot", function() { + fillNameWith('foo.'); + expectToHaveError("can't end in '.'"); + }); - it("doesn't duplicate error messages", function() { - fillNameWith('?foo?bar?zoo?'); - return expectToHaveError("can't contain '?'"); - }); + it("can't end with .lock", function() { + fillNameWith('foo.lock'); + expectToHaveError("can't end in '.lock'"); + }); - 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't be the single character @", function() { + fillNameWith('@'); + expectToHaveError("can't be '@'"); + }); - it("can have dashes anywhere", function() { - fillNameWith('-foo-bar-zoo-'); - return expect($('.js-branch-name-error span').length).toEqual(0); - }); + it('concatenates all error messages', function() { + fillNameWith('/foo bar?~.'); + expectToHaveError("can't start with '/', can't contain spaces, '?', '~', can't end in '.'"); + }); - it("can have underscores anywhere", function() { - fillNameWith('_foo_bar_zoo_'); - return expect($('.js-branch-name-error span').length).toEqual(0); - }); + it("doesn't duplicate error messages", function() { + fillNameWith('?foo?bar?zoo?'); + expectToHaveError("can't contain '?'"); + }); - 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); - }); + 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'); + + expect($('.js-branch-name-error span').length).toEqual(0); + }); + + it('can have dashes anywhere', function() { + fillNameWith('-foo-bar-zoo-'); + + expect($('.js-branch-name-error span').length).toEqual(0); + }); + + it('can have underscores anywhere', function() { + fillNameWith('_foo_bar_zoo_'); + + expect($('.js-branch-name-error span').length).toEqual(0); + }); + + it('can have numbers anywhere', function() { + fillNameWith('1foo2bar3zoo4'); + + expect($('.js-branch-name-error span').length).toEqual(0); + }); + + it('can be only letters', function() { + fillNameWith('foo'); + + expect($('.js-branch-name-error span').length).toEqual(0); }); }); -}).call(window); +}); diff --git a/spec/javascripts/notebook/cells/code_spec.js b/spec/javascripts/notebook/cells/code_spec.js index 0c432d73f67..4659b83d1b6 100644 --- a/spec/javascripts/notebook/cells/code_spec.js +++ b/spec/javascripts/notebook/cells/code_spec.js @@ -12,7 +12,7 @@ describe('Code component', () => { }); describe('without output', () => { - beforeEach((done) => { + beforeEach(done => { vm = new Component({ propsData: { cell: json.cells[0], @@ -31,7 +31,7 @@ describe('Code component', () => { }); describe('with output', () => { - beforeEach((done) => { + beforeEach(done => { vm = new Component({ propsData: { cell: json.cells[2], diff --git a/spec/javascripts/notebook/cells/markdown_spec.js b/spec/javascripts/notebook/cells/markdown_spec.js index 0b1b11de1fd..540fc8a21f1 100644 --- a/spec/javascripts/notebook/cells/markdown_spec.js +++ b/spec/javascripts/notebook/cells/markdown_spec.js @@ -11,7 +11,7 @@ describe('Markdown component', () => { let cell; let json; - beforeEach((done) => { + beforeEach(done => { json = getJSONFixture('blob/notebook/basic.json'); // eslint-disable-next-line prefer-destructuring @@ -34,18 +34,18 @@ describe('Markdown component', () => { }); it('does not render the markdown text', () => { - expect( - vm.$el.querySelector('.markdown').innerHTML.trim(), - ).not.toEqual(cell.source.join('')); + expect(vm.$el.querySelector('.markdown').innerHTML.trim()).not.toEqual(cell.source.join('')); }); it('renders the markdown HTML', () => { expect(vm.$el.querySelector('.markdown h1')).not.toBeNull(); }); - it('sanitizes output', (done) => { + it('sanitizes output', done => { Object.assign(cell, { - source: ['[XSS](data:text/html;base64,PHNjcmlwdD5hbGVydChkb2N1bWVudC5kb21haW4pPC9zY3JpcHQ+Cg==)\n'], + source: [ + '[XSS](data:text/html;base64,PHNjcmlwdD5hbGVydChkb2N1bWVudC5kb21haW4pPC9zY3JpcHQ+Cg==)\n', + ], }); Vue.nextTick(() => { @@ -60,7 +60,7 @@ describe('Markdown component', () => { json = getJSONFixture('blob/notebook/math.json'); }); - it('renders multi-line katex', (done) => { + it('renders multi-line katex', done => { vm = new Component({ propsData: { cell: json.cells[0], @@ -68,15 +68,13 @@ describe('Markdown component', () => { }).$mount(); Vue.nextTick(() => { - expect( - vm.$el.querySelector('.katex'), - ).not.toBeNull(); + expect(vm.$el.querySelector('.katex')).not.toBeNull(); done(); }); }); - it('renders inline katex', (done) => { + it('renders inline katex', done => { vm = new Component({ propsData: { cell: json.cells[1], @@ -84,15 +82,13 @@ describe('Markdown component', () => { }).$mount(); Vue.nextTick(() => { - expect( - vm.$el.querySelector('p:first-child .katex'), - ).not.toBeNull(); + expect(vm.$el.querySelector('p:first-child .katex')).not.toBeNull(); done(); }); }); - it('renders multiple inline katex', (done) => { + it('renders multiple inline katex', done => { vm = new Component({ propsData: { cell: json.cells[1], @@ -100,9 +96,7 @@ describe('Markdown component', () => { }).$mount(); Vue.nextTick(() => { - expect( - vm.$el.querySelectorAll('p:nth-child(2) .katex').length, - ).toBe(4); + expect(vm.$el.querySelectorAll('p:nth-child(2) .katex').length).toBe(4); done(); }); diff --git a/spec/javascripts/notebook/cells/output/html_sanitize_tests.js b/spec/javascripts/notebook/cells/output/html_sanitize_tests.js index d587573fc9e..74c48f04367 100644 --- a/spec/javascripts/notebook/cells/output/html_sanitize_tests.js +++ b/spec/javascripts/notebook/cells/output/html_sanitize_tests.js @@ -28,7 +28,8 @@ export default { output: '<a>foo</a>', }, 'protocol-based JS injection: long UTF-8 encoding without semicolons': { - input: '<a href=javascript:alert('XSS')>foo</a>', + input: + '<a href=javascript:alert('XSS')>foo</a>', output: '<a>foo</a>', }, 'protocol-based JS injection: hex encoding': { @@ -40,7 +41,8 @@ export default { output: '<a>foo</a>', }, 'protocol-based JS injection: hex encoding without semicolons': { - input: '<a href=javascript:alert('XSS')>foo</a>', + input: + '<a href=javascript:alert('XSS')>foo</a>', output: '<a>foo</a>', }, 'protocol-based JS injection: null char': { @@ -48,7 +50,7 @@ export default { output: '<a>foo</a>', }, 'protocol-based JS injection: invalid URL char': { - input: '<img src=java\script:alert("XSS")>', // eslint-disable-line no-useless-escape + input: '<img src=javascript:alert("XSS")>', output: '<img>', }, 'protocol-based JS injection: Unicode': { diff --git a/spec/javascripts/notebook/cells/output/html_spec.js b/spec/javascripts/notebook/cells/output/html_spec.js index 9c5385f2922..bea62f54634 100644 --- a/spec/javascripts/notebook/cells/output/html_spec.js +++ b/spec/javascripts/notebook/cells/output/html_spec.js @@ -14,7 +14,7 @@ describe('html output cell', () => { } describe('sanitizes output', () => { - Object.keys(sanitizeTests).forEach((key) => { + Object.keys(sanitizeTests).forEach(key => { it(key, () => { const test = sanitizeTests[key]; const vm = createComponent(test.input); diff --git a/spec/javascripts/notebook/cells/output/index_spec.js b/spec/javascripts/notebook/cells/output/index_spec.js index dbf79f85c7c..feab7ad4212 100644 --- a/spec/javascripts/notebook/cells/output/index_spec.js +++ b/spec/javascripts/notebook/cells/output/index_spec.js @@ -7,7 +7,7 @@ describe('Output component', () => { let vm; let json; - const createComponent = (output) => { + const createComponent = output => { vm = new Component({ propsData: { output, @@ -22,7 +22,7 @@ describe('Output component', () => { }); describe('text output', () => { - beforeEach((done) => { + beforeEach(done => { createComponent(json.cells[2].outputs[0]); setTimeout(() => { @@ -40,7 +40,7 @@ describe('Output component', () => { }); describe('image output', () => { - beforeEach((done) => { + beforeEach(done => { createComponent(json.cells[3].outputs[0]); setTimeout(() => { @@ -58,7 +58,7 @@ describe('Output component', () => { }); describe('html output', () => { - beforeEach((done) => { + beforeEach(done => { createComponent(json.cells[4].outputs[0]); setTimeout(() => { @@ -77,7 +77,7 @@ describe('Output component', () => { }); describe('svg output', () => { - beforeEach((done) => { + beforeEach(done => { createComponent(json.cells[5].outputs[0]); setTimeout(() => { @@ -95,7 +95,7 @@ describe('Output component', () => { }); describe('default to plain text', () => { - beforeEach((done) => { + beforeEach(done => { createComponent(json.cells[6].outputs[0]); setTimeout(() => { @@ -112,7 +112,7 @@ describe('Output component', () => { expect(vm.$el.querySelector('.prompt span')).not.toBeNull(); }); - it('renders as plain text when doesn\'t recognise other types', (done) => { + it("renders as plain text when doesn't recognise other types", done => { createComponent(json.cells[7].outputs[0]); setTimeout(() => { diff --git a/spec/javascripts/notebook/cells/prompt_spec.js b/spec/javascripts/notebook/cells/prompt_spec.js index 207fa433a59..cbbcb1e68e3 100644 --- a/spec/javascripts/notebook/cells/prompt_spec.js +++ b/spec/javascripts/notebook/cells/prompt_spec.js @@ -7,7 +7,7 @@ describe('Prompt component', () => { let vm; describe('input', () => { - beforeEach((done) => { + beforeEach(done => { vm = new Component({ propsData: { type: 'In', @@ -31,7 +31,7 @@ describe('Prompt component', () => { }); describe('output', () => { - beforeEach((done) => { + beforeEach(done => { vm = new Component({ propsData: { type: 'Out', diff --git a/spec/javascripts/notebook/index_spec.js b/spec/javascripts/notebook/index_spec.js index bd63ab35426..2e2ea5ad8af 100644 --- a/spec/javascripts/notebook/index_spec.js +++ b/spec/javascripts/notebook/index_spec.js @@ -14,7 +14,7 @@ describe('Notebook component', () => { }); describe('without JSON', () => { - beforeEach((done) => { + beforeEach(done => { vm = new Component({ propsData: { notebook: {}, @@ -33,7 +33,7 @@ describe('Notebook component', () => { }); describe('with JSON', () => { - beforeEach((done) => { + beforeEach(done => { vm = new Component({ propsData: { notebook: json, @@ -65,7 +65,7 @@ describe('Notebook component', () => { }); describe('with worksheets', () => { - beforeEach((done) => { + beforeEach(done => { vm = new Component({ propsData: { notebook: jsonWithWorksheet, @@ -80,7 +80,9 @@ describe('Notebook component', () => { }); it('renders cells', () => { - expect(vm.$el.querySelectorAll('.cell').length).toBe(jsonWithWorksheet.worksheets[0].cells.length); + expect(vm.$el.querySelectorAll('.cell').length).toBe( + jsonWithWorksheet.worksheets[0].cells.length, + ); }); it('renders markdown cell', () => { diff --git a/spec/javascripts/notes/components/comment_form_spec.js b/spec/javascripts/notes/components/comment_form_spec.js index 155c91dcc46..3c57fe51352 100644 --- a/spec/javascripts/notes/components/comment_form_spec.js +++ b/spec/javascripts/notes/components/comment_form_spec.js @@ -51,6 +51,7 @@ describe('issue_comment_form component', () => { spyOn(vm, 'stopPolling'); vm.handleSave(); + expect(vm.isSubmitting).toEqual(true); expect(vm.note).toEqual(''); expect(vm.saveNote).toHaveBeenCalled(); @@ -77,10 +78,14 @@ describe('issue_comment_form component', () => { vm.handleSave(); Vue.nextTick() - .then(() => expect(actionButton.disabled).toBeTruthy()) + .then(() => { + expect(actionButton.disabled).toBeTruthy(); + }) .then(saveNotePromise) .then(Vue.nextTick) - .then(() => expect(actionButton.disabled).toBeFalsy()) + .then(() => { + expect(actionButton.disabled).toBeFalsy(); + }) .then(done) .catch(done.fail); }); @@ -121,6 +126,7 @@ describe('issue_comment_form component', () => { it('should link to markdown docs', () => { const { markdownDocsPath } = notesDataMock; + expect(vm.$el.querySelector(`a[href="${markdownDocsPath}"]`).textContent.trim()).toEqual( 'Markdown', ); @@ -128,6 +134,7 @@ describe('issue_comment_form component', () => { it('should link to quick actions docs', () => { const { quickActionsDocsPath } = notesDataMock; + expect( vm.$el.querySelector(`a[href="${quickActionsDocsPath}"]`).textContent.trim(), ).toEqual('quick actions'); @@ -215,6 +222,7 @@ describe('issue_comment_form component', () => { expect(vm.$el.querySelector('.btn-comment-and-close').textContent.trim()).toEqual( 'Comment & close issue', ); + expect(vm.$el.querySelector('.js-note-discard')).toBeDefined(); done(); }); diff --git a/spec/javascripts/notes/components/note_app_spec.js b/spec/javascripts/notes/components/note_app_spec.js index b607e147b94..06b30375306 100644 --- a/spec/javascripts/notes/components/note_app_spec.js +++ b/spec/javascripts/notes/components/note_app_spec.js @@ -222,6 +222,7 @@ describe('note_app', () => { it('should render markdown docs url', () => { const { markdownDocsPath } = mockData.notesDataMock; + expect(vm.$el.querySelector(`a[href="${markdownDocsPath}"]`).textContent.trim()).toEqual( 'Markdown', ); @@ -229,6 +230,7 @@ describe('note_app', () => { it('should render quick action docs url', () => { const { quickActionsDocsPath } = mockData.notesDataMock; + expect(vm.$el.querySelector(`a[href="${quickActionsDocsPath}"]`).textContent.trim()).toEqual( 'quick actions', ); diff --git a/spec/javascripts/notes/components/note_form_spec.js b/spec/javascripts/notes/components/note_form_spec.js index eefd9ddd63c..5db20fd285f 100644 --- a/spec/javascripts/notes/components/note_form_spec.js +++ b/spec/javascripts/notes/components/note_form_spec.js @@ -76,6 +76,7 @@ describe('issue_note_form component', () => { it('should link to markdown docs', () => { const { markdownDocsPath } = notesDataMock; + expect(vm.$el.querySelector(`a[href="${markdownDocsPath}"]`).textContent.trim()).toEqual( 'Markdown', ); diff --git a/spec/javascripts/notes/components/noteable_discussion_spec.js b/spec/javascripts/notes/components/noteable_discussion_spec.js index 40b5f009ceb..b447e79b0df 100644 --- a/spec/javascripts/notes/components/noteable_discussion_spec.js +++ b/spec/javascripts/notes/components/noteable_discussion_spec.js @@ -138,23 +138,21 @@ describe('noteable_discussion component', () => { it('should return first note object for placeholder note', () => { const data = { isPlaceholderNote: true, - notes: [ - { body: 'hello world!' }, - ], + notes: [{ body: 'hello world!' }], }; const note = vm.componentData(data); + expect(note).toEqual(data.notes[0]); }); it('should return given note for nonplaceholder notes', () => { const data = { - notes: [ - { id: 12 }, - ], + notes: [{ id: 12 }], }; const note = vm.componentData(data); + expect(note).toEqual(data); }); }); diff --git a/spec/javascripts/notes/components/noteable_note_spec.js b/spec/javascripts/notes/components/noteable_note_spec.js index a31d17cacbb..8ade6fc2ced 100644 --- a/spec/javascripts/notes/components/noteable_note_spec.js +++ b/spec/javascripts/notes/components/noteable_note_spec.js @@ -36,6 +36,7 @@ describe('issue_note', () => { it('should render note header content', () => { const el = vm.$el.querySelector('.note-header .note-header-author-name'); + expect(el.textContent.trim()).toEqual(note.author.name); }); diff --git a/spec/javascripts/notes/helpers.js b/spec/javascripts/notes/helpers.js index a7663710a56..3f349b40ba5 100644 --- a/spec/javascripts/notes/helpers.js +++ b/spec/javascripts/notes/helpers.js @@ -1,5 +1,5 @@ // eslint-disable-next-line import/prefer-default-export -export const resetStore = (store) => { +export const resetStore = store => { store.replaceState({ notes: [], targetNoteHash: null, diff --git a/spec/javascripts/notes/stores/collapse_utils_spec.js b/spec/javascripts/notes/stores/collapse_utils_spec.js index 06a6aab932a..8ede9319088 100644 --- a/spec/javascripts/notes/stores/collapse_utils_spec.js +++ b/spec/javascripts/notes/stores/collapse_utils_spec.js @@ -4,10 +4,7 @@ import { getTimeDifferenceMinutes, collapseSystemNotes, } from '~/notes/stores/collapse_utils'; -import { - notesWithDescriptionChanges, - collapsedSystemNotes, -} from '../mock_data'; +import { notesWithDescriptionChanges, collapsedSystemNotes } from '../mock_data'; describe('Collapse utils', () => { const mockSystemNote = { @@ -22,14 +19,18 @@ describe('Collapse utils', () => { }); it('returns false when a system note is not a description type', () => { - expect(isDescriptionSystemNote(Object.assign({}, mockSystemNote, { note: 'foo' }))).toEqual(false); + expect(isDescriptionSystemNote(Object.assign({}, mockSystemNote, { note: 'foo' }))).toEqual( + false, + ); }); it('changes the description to contain the number of changed times', () => { const changedNote = changeDescriptionNote(mockSystemNote, 3, 5); expect(changedNote.times_updated).toEqual(3); - expect(changedNote.note_html.trim()).toContain('<p dir="auto">changed the description 3 times within 5 minutes </p>'); + expect(changedNote.note_html.trim()).toContain( + '<p dir="auto">changed the description 3 times within 5 minutes </p>', + ); }); it('gets the time difference between two notes', () => { diff --git a/spec/javascripts/notes/stores/getters_spec.js b/spec/javascripts/notes/stores/getters_spec.js index 7f8ede51508..f853f9ff088 100644 --- a/spec/javascripts/notes/stores/getters_spec.js +++ b/spec/javascripts/notes/stores/getters_spec.js @@ -205,6 +205,7 @@ describe('Getters Notes Store', () => { '123', '456', ]); + expect(getters.unresolvedDiscussionsIdsOrdered(state, localGetters)(undefined)).toEqual([ '123', '456', diff --git a/spec/javascripts/notes/stores/mutation_spec.js b/spec/javascripts/notes/stores/mutation_spec.js index 1ecfe914859..9d652ba9f1e 100644 --- a/spec/javascripts/notes/stores/mutation_spec.js +++ b/spec/javascripts/notes/stores/mutation_spec.js @@ -30,11 +30,13 @@ describe('Notes Store mutations', () => { expect(state).toEqual({ discussions: [noteData], }); + expect(state.discussions.length).toBe(1); }); it('should not add the same note to the notes array', () => { mutations.ADD_NEW_NOTE(state, note); + expect(state.discussions.length).toBe(1); }); }); @@ -106,6 +108,7 @@ describe('Notes Store mutations', () => { }; mutations.SET_NOTES_DATA(state, notesDataMock); + expect(state.notesData).toEqual(notesDataMock); }); }); @@ -117,6 +120,7 @@ describe('Notes Store mutations', () => { }; mutations.SET_NOTEABLE_DATA(state, noteableDataMock); + expect(state.noteableData).toEqual(noteableDataMock); }); }); @@ -128,6 +132,7 @@ describe('Notes Store mutations', () => { }; mutations.SET_USER_DATA(state, userDataMock); + expect(state.userData).toEqual(userDataMock); }); }); @@ -151,6 +156,7 @@ describe('Notes Store mutations', () => { }; mutations.SET_INITIAL_DISCUSSIONS(state, [note, legacyNote]); + expect(state.discussions[0].id).toEqual(note.id); expect(state.discussions[1].notes[0].note).toBe(legacyNote.notes[0].note); expect(state.discussions[2].notes[0].note).toBe(legacyNote.notes[1].note); @@ -200,6 +206,7 @@ describe('Notes Store mutations', () => { }; mutations.SET_LAST_FETCHED_AT(state, 'timestamp'); + expect(state.lastFetchedAt).toEqual('timestamp'); }); }); @@ -211,6 +218,7 @@ describe('Notes Store mutations', () => { }; mutations.SET_TARGET_NOTE_HASH(state, 'hash'); + expect(state.targetNoteHash).toEqual('hash'); }); }); @@ -221,6 +229,7 @@ describe('Notes Store mutations', () => { discussions: [], }; mutations.SHOW_PLACEHOLDER_NOTE(state, note); + expect(state.discussions[0].isPlaceholderNote).toEqual(true); }); }); @@ -261,6 +270,7 @@ describe('Notes Store mutations', () => { awardName: 'bath_tone3', }; mutations.TOGGLE_AWARD(state, data); + expect(state.discussions[0].award_emoji.length).toEqual(2); }); }); @@ -316,6 +326,7 @@ describe('Notes Store mutations', () => { }; mutations.CLOSE_ISSUE(state); + expect(state.noteableData.state).toEqual('closed'); }); }); @@ -333,6 +344,7 @@ describe('Notes Store mutations', () => { }; mutations.REOPEN_ISSUE(state); + expect(state.noteableData.state).toEqual('reopened'); }); }); @@ -350,6 +362,7 @@ describe('Notes Store mutations', () => { }; mutations.TOGGLE_STATE_BUTTON_LOADING(state, true); + expect(state.isToggleStateButtonLoading).toEqual(true); }); @@ -365,6 +378,7 @@ describe('Notes Store mutations', () => { }; mutations.TOGGLE_STATE_BUTTON_LOADING(state, false); + expect(state.isToggleStateButtonLoading).toEqual(false); }); }); @@ -376,6 +390,7 @@ describe('Notes Store mutations', () => { }; mutations.SET_NOTES_FETCHED_STATE(state, true); + expect(state.isNotesFetched).toEqual(true); }); }); diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js index 7bfbca83c77..694f581150f 100644 --- a/spec/javascripts/notes_spec.js +++ b/spec/javascripts/notes_spec.js @@ -10,1011 +10,1024 @@ import '~/behaviors/markdown/render_gfm'; import Notes from '~/notes'; import timeoutPromise from './helpers/set_timeout_promise_helper'; -(function() { - window.gon || (window.gon = {}); - window.gl = window.gl || {}; - gl.utils = gl.utils || {}; - - const htmlEscape = comment => { - const escapedString = comment.replace(/["&'<>]/g, a => { - const escapedToken = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''', - '`': '`', - }[a]; - - return escapedToken; - }); - - return escapedString; - }; - - describe('Notes', function() { - const FLASH_TYPE_ALERT = 'alert'; - const NOTES_POST_PATH = /(.*)\/notes\?html=true$/; - var fixture = 'snippets/show.html.raw'; - preloadFixtures(fixture); +window.gon || (window.gon = {}); +window.gl = window.gl || {}; +gl.utils = gl.utils || {}; + +const htmlEscape = comment => { + const escapedString = comment.replace(/["&'<>]/g, a => { + const escapedToken = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '`': '`', + }[a]; + + return escapedToken; + }); - beforeEach(function() { - loadFixtures(fixture); - gl.utils.disableButtonIfEmptyField = _.noop; - window.project_uploads_path = 'http://test.host/uploads'; - $('body').attr('data-page', 'projects:merge_requets:show'); - }); + return escapedString; +}; - afterEach(() => { - // Undo what we did to the shared <body> - $('body').removeAttr('data-page'); - }); +describe('Notes', function() { + const FLASH_TYPE_ALERT = 'alert'; + const NOTES_POST_PATH = /(.*)\/notes\?html=true$/; + var fixture = 'snippets/show.html.raw'; + preloadFixtures(fixture); - describe('addBinding', () => { - it('calls postComment when comment button is clicked', () => { - spyOn(Notes.prototype, 'postComment'); - this.notes = new Notes('', []); + beforeEach(function() { + loadFixtures(fixture); + gl.utils.disableButtonIfEmptyField = _.noop; + window.project_uploads_path = 'http://test.host/uploads'; + $('body').attr('data-page', 'projects:merge_requets:show'); + }); - $('.js-comment-button').click(); + afterEach(() => { + // Undo what we did to the shared <body> + $('body').removeAttr('data-page'); + }); - expect(Notes.prototype.postComment).toHaveBeenCalled(); - }); + describe('addBinding', () => { + it('calls postComment when comment button is clicked', () => { + spyOn(Notes.prototype, 'postComment'); + this.notes = new Notes('', []); + + $('.js-comment-button').click(); + + expect(Notes.prototype.postComment).toHaveBeenCalled(); }); + }); - describe('task lists', function() { - let mock; + describe('task lists', function() { + let mock; - beforeEach(function() { - spyOn(axios, 'patch').and.callFake(() => new Promise(() => {})); - mock = new MockAdapter(axios); - mock.onAny().reply(200, {}); + beforeEach(function() { + spyOn(axios, 'patch').and.callFake(() => new Promise(() => {})); + mock = new MockAdapter(axios); + mock.onAny().reply(200, {}); - $('.js-comment-button').on('click', function(e) { - e.preventDefault(); - }); - this.notes = new Notes('', []); + $('.js-comment-button').on('click', function(e) { + e.preventDefault(); }); + this.notes = new Notes('', []); + }); - afterEach(() => { - mock.restore(); - }); + afterEach(() => { + mock.restore(); + }); - it('modifies the Markdown field', function() { - const changeEvent = document.createEvent('HTMLEvents'); - changeEvent.initEvent('change', true, true); - $('input[type=checkbox]') - .attr('checked', true)[0] - .dispatchEvent(changeEvent); + it('modifies the Markdown field', function() { + const changeEvent = document.createEvent('HTMLEvents'); + changeEvent.initEvent('change', true, true); + $('input[type=checkbox]') + .attr('checked', true)[0] + .dispatchEvent(changeEvent); - expect($('.js-task-list-field.original-task-list').val()).toBe('- [x] Task List Item'); - }); + expect($('.js-task-list-field.original-task-list').val()).toBe('- [x] Task List Item'); + }); - it('submits an ajax request on tasklist:changed', function(done) { - $('.js-task-list-container').trigger('tasklist:changed'); + it('submits an ajax request on tasklist:changed', function(done) { + $('.js-task-list-container').trigger('tasklist:changed'); - setTimeout(() => { - expect(axios.patch).toHaveBeenCalled(); - done(); - }); + setTimeout(() => { + expect(axios.patch).toHaveBeenCalled(); + done(); }); }); + }); - describe('comments', function() { - var textarea = '.js-note-text'; + describe('comments', function() { + var textarea = '.js-note-text'; - beforeEach(function() { - this.notes = new Notes('', []); + beforeEach(function() { + this.notes = new Notes('', []); - this.autoSizeSpy = spyOnEvent($(textarea), 'autosize:update'); - spyOn(this.notes, 'renderNote').and.stub(); + this.autoSizeSpy = spyOnEvent($(textarea), 'autosize:update'); + spyOn(this.notes, 'renderNote').and.stub(); - $(textarea).data('autosave', { - reset: function() {}, - }); + $(textarea).data('autosave', { + reset: function() {}, + }); - $('.js-comment-button').on('click', e => { - const $form = $(this); - e.preventDefault(); - this.notes.addNote($form); - this.notes.reenableTargetFormSubmitButton(e); - this.notes.resetMainTargetForm(e); - }); + $('.js-comment-button').on('click', e => { + const $form = $(this); + e.preventDefault(); + this.notes.addNote($form); + this.notes.reenableTargetFormSubmitButton(e); + this.notes.resetMainTargetForm(e); }); + }); - it('autosizes after comment submission', function() { - $(textarea).text('This is an example comment note'); - expect(this.autoSizeSpy).not.toHaveBeenTriggered(); + it('autosizes after comment submission', function() { + $(textarea).text('This is an example comment note'); - $('.js-comment-button').click(); - expect(this.autoSizeSpy).toHaveBeenTriggered(); - }); + expect(this.autoSizeSpy).not.toHaveBeenTriggered(); - it('should not place escaped text in the comment box in case of error', function() { - const deferred = $.Deferred(); - spyOn($, 'ajax').and.returnValue(deferred.promise()); - $(textarea).text('A comment with `markup`.'); + $('.js-comment-button').click(); - deferred.reject(); - $('.js-comment-button').click(); - expect($(textarea).val()).toEqual('A comment with `markup`.'); - }); + expect(this.autoSizeSpy).toHaveBeenTriggered(); }); - describe('updateNote', () => { - let sampleComment; - let noteEntity; - let $form; - let $notesContainer; - let mock; + it('should not place escaped text in the comment box in case of error', function() { + const deferred = $.Deferred(); + spyOn($, 'ajax').and.returnValue(deferred.promise()); + $(textarea).text('A comment with `markup`.'); - beforeEach(() => { - this.notes = new Notes('', []); - window.gon.current_username = 'root'; - window.gon.current_user_fullname = 'Administrator'; - sampleComment = 'foo'; - noteEntity = { - id: 1234, - html: `<li class="note note-row-1234 timeline-entry" id="note_1234"> - <div class="note-text">${sampleComment}</div> - </li>`, - note: sampleComment, - valid: true, - }; - $form = $('form.js-main-target-form'); - $notesContainer = $('ul.main-notes-list'); - $form.find('textarea.js-note-text').val(sampleComment); + deferred.reject(); + $('.js-comment-button').click(); - mock = new MockAdapter(axios); - mock.onPost(NOTES_POST_PATH).reply(200, noteEntity); - }); + expect($(textarea).val()).toEqual('A comment with `markup`.'); + }); + }); - afterEach(() => { - mock.restore(); - }); + describe('updateNote', () => { + let sampleComment; + let noteEntity; + let $form; + let $notesContainer; + let mock; + + beforeEach(() => { + this.notes = new Notes('', []); + window.gon.current_username = 'root'; + window.gon.current_user_fullname = 'Administrator'; + sampleComment = 'foo'; + noteEntity = { + id: 1234, + html: `<li class="note note-row-1234 timeline-entry" id="note_1234"> + <div class="note-text">${sampleComment}</div> + </li>`, + note: sampleComment, + valid: true, + }; + $form = $('form.js-main-target-form'); + $notesContainer = $('ul.main-notes-list'); + $form.find('textarea.js-note-text').val(sampleComment); + + mock = new MockAdapter(axios); + mock.onPost(NOTES_POST_PATH).reply(200, noteEntity); + }); - it('updates note and resets edit form', done => { - spyOn(this.notes, 'revertNoteEditForm'); - spyOn(this.notes, 'setupNewNote'); + afterEach(() => { + mock.restore(); + }); - $('.js-comment-button').click(); + it('updates note and resets edit form', done => { + spyOn(this.notes, 'revertNoteEditForm'); + spyOn(this.notes, 'setupNewNote'); - setTimeout(() => { - const $targetNote = $notesContainer.find(`#note_${noteEntity.id}`); - const updatedNote = Object.assign({}, noteEntity); - updatedNote.note = 'bar'; - this.notes.updateNote(updatedNote, $targetNote); + $('.js-comment-button').click(); - expect(this.notes.revertNoteEditForm).toHaveBeenCalledWith($targetNote); - expect(this.notes.setupNewNote).toHaveBeenCalled(); + setTimeout(() => { + const $targetNote = $notesContainer.find(`#note_${noteEntity.id}`); + const updatedNote = Object.assign({}, noteEntity); + updatedNote.note = 'bar'; + this.notes.updateNote(updatedNote, $targetNote); - done(); - }); + expect(this.notes.revertNoteEditForm).toHaveBeenCalledWith($targetNote); + expect(this.notes.setupNewNote).toHaveBeenCalled(); + + done(); }); }); + }); - describe('updateNoteTargetSelector', () => { - const hash = 'note_foo'; - let $note; + describe('updateNoteTargetSelector', () => { + const hash = 'note_foo'; + let $note; - beforeEach(() => { - $note = $(`<div id="${hash}"></div>`); - spyOn($note, 'filter').and.callThrough(); - spyOn($note, 'toggleClass').and.callThrough(); - }); + beforeEach(() => { + $note = $(`<div id="${hash}"></div>`); + spyOn($note, 'filter').and.callThrough(); + spyOn($note, 'toggleClass').and.callThrough(); + }); - it('sets target when hash matches', () => { - spyOnDependency(Notes, 'getLocationHash').and.returnValue(hash); + it('sets target when hash matches', () => { + spyOnDependency(Notes, 'getLocationHash').and.returnValue(hash); - Notes.updateNoteTargetSelector($note); + Notes.updateNoteTargetSelector($note); - expect($note.filter).toHaveBeenCalledWith(`#${hash}`); - expect($note.toggleClass).toHaveBeenCalledWith('target', true); - }); + expect($note.filter).toHaveBeenCalledWith(`#${hash}`); + expect($note.toggleClass).toHaveBeenCalledWith('target', true); + }); - it('unsets target when hash does not match', () => { - spyOnDependency(Notes, 'getLocationHash').and.returnValue('note_doesnotexist'); + it('unsets target when hash does not match', () => { + spyOnDependency(Notes, 'getLocationHash').and.returnValue('note_doesnotexist'); - Notes.updateNoteTargetSelector($note); + Notes.updateNoteTargetSelector($note); - expect($note.toggleClass).toHaveBeenCalledWith('target', false); - }); + expect($note.toggleClass).toHaveBeenCalledWith('target', false); + }); - it('unsets target when there is not a hash fragment anymore', () => { - spyOnDependency(Notes, 'getLocationHash').and.returnValue(null); + it('unsets target when there is not a hash fragment anymore', () => { + spyOnDependency(Notes, 'getLocationHash').and.returnValue(null); - Notes.updateNoteTargetSelector($note); + Notes.updateNoteTargetSelector($note); - expect($note.toggleClass).toHaveBeenCalledWith('target', false); - }); + expect($note.toggleClass).toHaveBeenCalledWith('target', false); }); + }); - describe('renderNote', () => { - let notes; - let note; - let $notesList; + describe('renderNote', () => { + let notes; + let note; + let $notesList; - beforeEach(() => { - note = { - id: 1, - valid: true, - note: 'heya', - html: '<div>heya</div>', - }; - $notesList = jasmine.createSpyObj('$notesList', ['find', 'append']); - - notes = jasmine.createSpyObj('notes', [ - 'setupNewNote', - 'refresh', - 'collapseLongCommitList', - 'updateNotesCount', - 'putConflictEditWarningInPlace', - ]); - notes.taskList = jasmine.createSpyObj('tasklist', ['init']); - notes.note_ids = []; - notes.updatedNotesTrackingMap = {}; - - spyOn(Notes, 'isNewNote').and.callThrough(); - spyOn(Notes, 'isUpdatedNote').and.callThrough(); - spyOn(Notes, 'animateAppendNote').and.callThrough(); - spyOn(Notes, 'animateUpdateNote').and.callThrough(); + beforeEach(() => { + note = { + id: 1, + valid: true, + note: 'heya', + html: '<div>heya</div>', + }; + $notesList = jasmine.createSpyObj('$notesList', ['find', 'append']); + + notes = jasmine.createSpyObj('notes', [ + 'setupNewNote', + 'refresh', + 'collapseLongCommitList', + 'updateNotesCount', + 'putConflictEditWarningInPlace', + ]); + notes.taskList = jasmine.createSpyObj('tasklist', ['init']); + notes.note_ids = []; + notes.updatedNotesTrackingMap = {}; + + spyOn(Notes, 'isNewNote').and.callThrough(); + spyOn(Notes, 'isUpdatedNote').and.callThrough(); + spyOn(Notes, 'animateAppendNote').and.callThrough(); + spyOn(Notes, 'animateUpdateNote').and.callThrough(); + }); + + describe('when adding note', () => { + it('should call .animateAppendNote', () => { + Notes.isNewNote.and.returnValue(true); + Notes.prototype.renderNote.call(notes, note, null, $notesList); + + expect(Notes.animateAppendNote).toHaveBeenCalledWith(note.html, $notesList); }); + }); - describe('when adding note', () => { - it('should call .animateAppendNote', () => { - Notes.isNewNote.and.returnValue(true); - Notes.prototype.renderNote.call(notes, note, null, $notesList); + describe('when note was edited', () => { + it('should call .animateUpdateNote', () => { + Notes.isNewNote.and.returnValue(false); + Notes.isUpdatedNote.and.returnValue(true); + const $note = $('<div>'); + $notesList.find.and.returnValue($note); + const $newNote = $(note.html); + Notes.animateUpdateNote.and.returnValue($newNote); - expect(Notes.animateAppendNote).toHaveBeenCalledWith(note.html, $notesList); - }); + Notes.prototype.renderNote.call(notes, note, null, $notesList); + + expect(Notes.animateUpdateNote).toHaveBeenCalledWith(note.html, $note); + expect(notes.setupNewNote).toHaveBeenCalledWith($newNote); }); - describe('when note was edited', () => { - it('should call .animateUpdateNote', () => { + describe('while editing', () => { + it('should update textarea if nothing has been touched', () => { Notes.isNewNote.and.returnValue(false); Notes.isUpdatedNote.and.returnValue(true); - const $note = $('<div>'); + const $note = $(`<div class="is-editing"> + <div class="original-note-content">initial</div> + <textarea class="js-note-text">initial</textarea> + </div>`); $notesList.find.and.returnValue($note); - const $newNote = $(note.html); - Notes.animateUpdateNote.and.returnValue($newNote); - Notes.prototype.renderNote.call(notes, note, null, $notesList); - expect(Notes.animateUpdateNote).toHaveBeenCalledWith(note.html, $note); - expect(notes.setupNewNote).toHaveBeenCalledWith($newNote); + expect($note.find('.js-note-text').val()).toEqual(note.note); }); - describe('while editing', () => { - it('should update textarea if nothing has been touched', () => { - Notes.isNewNote.and.returnValue(false); - Notes.isUpdatedNote.and.returnValue(true); - const $note = $(`<div class="is-editing"> - <div class="original-note-content">initial</div> - <textarea class="js-note-text">initial</textarea> - </div>`); - $notesList.find.and.returnValue($note); - Notes.prototype.renderNote.call(notes, note, null, $notesList); - - expect($note.find('.js-note-text').val()).toEqual(note.note); - }); - - it('should call .putConflictEditWarningInPlace', () => { - Notes.isNewNote.and.returnValue(false); - Notes.isUpdatedNote.and.returnValue(true); - const $note = $(`<div class="is-editing"> - <div class="original-note-content">initial</div> - <textarea class="js-note-text">different</textarea> - </div>`); - $notesList.find.and.returnValue($note); - Notes.prototype.renderNote.call(notes, note, null, $notesList); - - expect(notes.putConflictEditWarningInPlace).toHaveBeenCalledWith(note, $note); - }); + it('should call .putConflictEditWarningInPlace', () => { + Notes.isNewNote.and.returnValue(false); + Notes.isUpdatedNote.and.returnValue(true); + const $note = $(`<div class="is-editing"> + <div class="original-note-content">initial</div> + <textarea class="js-note-text">different</textarea> + </div>`); + $notesList.find.and.returnValue($note); + Notes.prototype.renderNote.call(notes, note, null, $notesList); + + expect(notes.putConflictEditWarningInPlace).toHaveBeenCalledWith(note, $note); }); }); }); + }); - describe('isUpdatedNote', () => { - it('should consider same note text as the same', () => { - const result = Notes.isUpdatedNote( - { - note: 'initial', - }, - $(`<div> - <div class="original-note-content">initial</div> - </div>`), - ); + describe('isUpdatedNote', () => { + it('should consider same note text as the same', () => { + const result = Notes.isUpdatedNote( + { + note: 'initial', + }, + $(`<div> + <div class="original-note-content">initial</div> + </div>`), + ); - expect(result).toEqual(false); - }); + expect(result).toEqual(false); + }); - it('should consider same note with trailing newline as the same', () => { - const result = Notes.isUpdatedNote( - { - note: 'initial\n', - }, - $(`<div> - <div class="original-note-content">initial\n</div> - </div>`), - ); + it('should consider same note with trailing newline as the same', () => { + const result = Notes.isUpdatedNote( + { + note: 'initial\n', + }, + $(`<div> + <div class="original-note-content">initial\n</div> + </div>`), + ); - expect(result).toEqual(false); - }); + expect(result).toEqual(false); + }); - it('should consider different notes as different', () => { - const result = Notes.isUpdatedNote( - { - note: 'foo', - }, - $(`<div> - <div class="original-note-content">bar</div> - </div>`), - ); + it('should consider different notes as different', () => { + const result = Notes.isUpdatedNote( + { + note: 'foo', + }, + $(`<div> + <div class="original-note-content">bar</div> + </div>`), + ); - expect(result).toEqual(true); - }); + expect(result).toEqual(true); + }); + }); + + describe('renderDiscussionNote', () => { + let discussionContainer; + let note; + let notes; + let $form; + let row; + + beforeEach(() => { + note = { + html: '<li></li>', + discussion_html: '<div></div>', + discussion_id: 1, + discussion_resolvable: false, + diff_discussion_html: false, + }; + $form = jasmine.createSpyObj('$form', ['closest', 'find']); + $form.length = 1; + row = jasmine.createSpyObj('row', ['prevAll', 'first', 'find']); + + notes = jasmine.createSpyObj('notes', ['isParallelView', 'updateNotesCount']); + notes.note_ids = []; + + spyOn(Notes, 'isNewNote'); + spyOn(Notes, 'animateAppendNote'); + Notes.isNewNote.and.returnValue(true); + notes.isParallelView.and.returnValue(false); + row.prevAll.and.returnValue(row); + row.first.and.returnValue(row); + row.find.and.returnValue(row); }); - describe('renderDiscussionNote', () => { - let discussionContainer; - let note; - let notes; - let $form; - let row; + describe('Discussion root note', () => { + let body; beforeEach(() => { - note = { - html: '<li></li>', - discussion_html: '<div></div>', - discussion_id: 1, - discussion_resolvable: false, - diff_discussion_html: false, - }; - $form = jasmine.createSpyObj('$form', ['closest', 'find']); - $form.length = 1; - row = jasmine.createSpyObj('row', ['prevAll', 'first', 'find']); - - notes = jasmine.createSpyObj('notes', ['isParallelView', 'updateNotesCount']); - notes.note_ids = []; + body = jasmine.createSpyObj('body', ['attr']); + discussionContainer = { length: 0 }; - spyOn(Notes, 'isNewNote'); - spyOn(Notes, 'animateAppendNote'); - Notes.isNewNote.and.returnValue(true); - notes.isParallelView.and.returnValue(false); - row.prevAll.and.returnValue(row); - row.first.and.returnValue(row); - row.find.and.returnValue(row); + $form.closest.and.returnValues(row, $form); + $form.find.and.returnValues(discussionContainer); + body.attr.and.returnValue(''); }); - describe('Discussion root note', () => { - let body; - - beforeEach(() => { - body = jasmine.createSpyObj('body', ['attr']); - discussionContainer = { length: 0 }; + it('should call Notes.animateAppendNote', () => { + Notes.prototype.renderDiscussionNote.call(notes, note, $form); - $form.closest.and.returnValues(row, $form); - $form.find.and.returnValues(discussionContainer); - body.attr.and.returnValue(''); - }); - - it('should call Notes.animateAppendNote', () => { - Notes.prototype.renderDiscussionNote.call(notes, note, $form); - - expect(Notes.animateAppendNote).toHaveBeenCalledWith( - note.discussion_html, - $('.main-notes-list'), - ); - }); + expect(Notes.animateAppendNote).toHaveBeenCalledWith( + note.discussion_html, + $('.main-notes-list'), + ); + }); - it('should append to row selected with line_code', () => { - $form.length = 0; - note.discussion_line_code = 'line_code'; - note.diff_discussion_html = '<tr></tr>'; + it('should append to row selected with line_code', () => { + $form.length = 0; + note.discussion_line_code = 'line_code'; + note.diff_discussion_html = '<tr></tr>'; - const line = document.createElement('div'); - line.id = note.discussion_line_code; - document.body.appendChild(line); + const line = document.createElement('div'); + line.id = note.discussion_line_code; + document.body.appendChild(line); - $form.closest.and.returnValues($form); + $form.closest.and.returnValues($form); - Notes.prototype.renderDiscussionNote.call(notes, note, $form); + Notes.prototype.renderDiscussionNote.call(notes, note, $form); - expect(line.nextSibling.outerHTML).toEqual(note.diff_discussion_html); - }); + expect(line.nextSibling.outerHTML).toEqual(note.diff_discussion_html); }); + }); - describe('Discussion sub note', () => { - beforeEach(() => { - discussionContainer = { length: 1 }; + describe('Discussion sub note', () => { + beforeEach(() => { + discussionContainer = { length: 1 }; - $form.closest.and.returnValues(row, $form); - $form.find.and.returnValues(discussionContainer); + $form.closest.and.returnValues(row, $form); + $form.find.and.returnValues(discussionContainer); - Notes.prototype.renderDiscussionNote.call(notes, note, $form); - }); + Notes.prototype.renderDiscussionNote.call(notes, note, $form); + }); - it('should call Notes.animateAppendNote', () => { - expect(Notes.animateAppendNote).toHaveBeenCalledWith(note.html, discussionContainer); - }); + it('should call Notes.animateAppendNote', () => { + expect(Notes.animateAppendNote).toHaveBeenCalledWith(note.html, discussionContainer); }); }); + }); - describe('animateAppendNote', () => { - let noteHTML; - let $notesList; - let $resultantNote; + describe('animateAppendNote', () => { + let noteHTML; + let $notesList; + let $resultantNote; - beforeEach(() => { - noteHTML = '<div></div>'; - $notesList = jasmine.createSpyObj('$notesList', ['append']); + beforeEach(() => { + noteHTML = '<div></div>'; + $notesList = jasmine.createSpyObj('$notesList', ['append']); - $resultantNote = Notes.animateAppendNote(noteHTML, $notesList); - }); + $resultantNote = Notes.animateAppendNote(noteHTML, $notesList); + }); - it('should have `fade-in-full` class', () => { - expect($resultantNote.hasClass('fade-in-full')).toEqual(true); - }); + it('should have `fade-in-full` class', () => { + expect($resultantNote.hasClass('fade-in-full')).toEqual(true); + }); - it('should append note to the notes list', () => { - expect($notesList.append).toHaveBeenCalledWith($resultantNote); - }); + it('should append note to the notes list', () => { + expect($notesList.append).toHaveBeenCalledWith($resultantNote); }); + }); - describe('animateUpdateNote', () => { - let noteHTML; - let $note; - let $updatedNote; + describe('animateUpdateNote', () => { + let noteHTML; + let $note; + let $updatedNote; - beforeEach(() => { - noteHTML = '<div></div>'; - $note = jasmine.createSpyObj('$note', ['replaceWith']); + beforeEach(() => { + noteHTML = '<div></div>'; + $note = jasmine.createSpyObj('$note', ['replaceWith']); - $updatedNote = Notes.animateUpdateNote(noteHTML, $note); - }); + $updatedNote = Notes.animateUpdateNote(noteHTML, $note); + }); - it('should have `fade-in` class', () => { - expect($updatedNote.hasClass('fade-in')).toEqual(true); - }); + it('should have `fade-in` class', () => { + expect($updatedNote.hasClass('fade-in')).toEqual(true); + }); - it('should call replaceWith on $note', () => { - expect($note.replaceWith).toHaveBeenCalledWith($updatedNote); - }); + it('should call replaceWith on $note', () => { + expect($note.replaceWith).toHaveBeenCalledWith($updatedNote); }); + }); - describe('putEditFormInPlace', () => { - it('should call GLForm with GFM parameter passed through', () => { - const notes = new Notes('', []); - const $el = $(` - <div> - <form></form> - </div> - `); + describe('putEditFormInPlace', () => { + it('should call GLForm with GFM parameter passed through', () => { + const notes = new Notes('', []); + const $el = $(` + <div> + <form></form> + </div> + `); - notes.putEditFormInPlace($el); + notes.putEditFormInPlace($el); - expect(notes.glForm.enableGFM).toBeTruthy(); - }); + expect(notes.glForm.enableGFM).toBeTruthy(); }); + }); - describe('postComment & updateComment', () => { - const sampleComment = 'foo'; - const updatedComment = 'bar'; - const note = { - id: 1234, - html: `<li class="note note-row-1234 timeline-entry" id="note_1234"> - <div class="note-text">${sampleComment}</div> - </li>`, - note: sampleComment, - valid: true, - }; - let $form; - let $notesContainer; - let mock; + describe('postComment & updateComment', () => { + const sampleComment = 'foo'; + const updatedComment = 'bar'; + const note = { + id: 1234, + html: `<li class="note note-row-1234 timeline-entry" id="note_1234"> + <div class="note-text">${sampleComment}</div> + </li>`, + note: sampleComment, + valid: true, + }; + let $form; + let $notesContainer; + let mock; + + function mockNotesPost() { + mock.onPost(NOTES_POST_PATH).reply(200, note); + } + + function mockNotesPostError() { + mock.onPost(NOTES_POST_PATH).networkError(); + } + + beforeEach(() => { + mock = new MockAdapter(axios); + + this.notes = new Notes('', []); + window.gon.current_username = 'root'; + window.gon.current_user_fullname = 'Administrator'; + $form = $('form.js-main-target-form'); + $notesContainer = $('ul.main-notes-list'); + $form.find('textarea.js-note-text').val(sampleComment); + }); - function mockNotesPost() { - mock.onPost(NOTES_POST_PATH).reply(200, note); - } + afterEach(() => { + mock.restore(); + }); - function mockNotesPostError() { - mock.onPost(NOTES_POST_PATH).networkError(); - } + it('should show placeholder note while new comment is being posted', () => { + mockNotesPost(); - beforeEach(() => { - mock = new MockAdapter(axios); - - this.notes = new Notes('', []); - window.gon.current_username = 'root'; - window.gon.current_user_fullname = 'Administrator'; - $form = $('form.js-main-target-form'); - $notesContainer = $('ul.main-notes-list'); - $form.find('textarea.js-note-text').val(sampleComment); - }); + $('.js-comment-button').click(); - afterEach(() => { - mock.restore(); - }); + expect($notesContainer.find('.note.being-posted').length).toBeGreaterThan(0); + }); - it('should show placeholder note while new comment is being posted', () => { - mockNotesPost(); + it('should remove placeholder note when new comment is done posting', done => { + mockNotesPost(); - $('.js-comment-button').click(); - expect($notesContainer.find('.note.being-posted').length).toBeGreaterThan(0); - }); + $('.js-comment-button').click(); - it('should remove placeholder note when new comment is done posting', done => { - mockNotesPost(); + setTimeout(() => { + expect($notesContainer.find('.note.being-posted').length).toEqual(0); - $('.js-comment-button').click(); + done(); + }); + }); - setTimeout(() => { - expect($notesContainer.find('.note.being-posted').length).toEqual(0); + describe('postComment', () => { + it('disables the submit button', done => { + const $submitButton = $form.find('.js-comment-submit-button'); - done(); + expect($submitButton).not.toBeDisabled(); + const dummyEvent = { + preventDefault() {}, + target: $submitButton, + }; + mock.onPost(NOTES_POST_PATH).replyOnce(() => { + expect($submitButton).toBeDisabled(); + return [200, note]; }); - }); - describe('postComment', () => { - it('disables the submit button', done => { - const $submitButton = $form.find('.js-comment-submit-button'); - expect($submitButton).not.toBeDisabled(); - const dummyEvent = { - preventDefault() {}, - target: $submitButton, - }; - mock.onPost(NOTES_POST_PATH).replyOnce(() => { - expect($submitButton).toBeDisabled(); - return [200, note]; - }); - - this.notes - .postComment(dummyEvent) - .then(() => { - expect($submitButton).not.toBeDisabled(); - }) - .then(done) - .catch(done.fail); - }); + this.notes + .postComment(dummyEvent) + .then(() => { + expect($submitButton).not.toBeDisabled(); + }) + .then(done) + .catch(done.fail); }); + }); - it('should show actual note element when new comment is done posting', done => { - mockNotesPost(); + it('should show actual note element when new comment is done posting', done => { + mockNotesPost(); - $('.js-comment-button').click(); + $('.js-comment-button').click(); - setTimeout(() => { - expect($notesContainer.find(`#note_${note.id}`).length).toBeGreaterThan(0); + setTimeout(() => { + expect($notesContainer.find(`#note_${note.id}`).length).toBeGreaterThan(0); - done(); - }); + done(); }); + }); - it('should reset Form when new comment is done posting', done => { - mockNotesPost(); + it('should reset Form when new comment is done posting', done => { + mockNotesPost(); - $('.js-comment-button').click(); + $('.js-comment-button').click(); - setTimeout(() => { - expect($form.find('textarea.js-note-text').val()).toEqual(''); + setTimeout(() => { + expect($form.find('textarea.js-note-text').val()).toEqual(''); - done(); - }); + done(); }); + }); - it('should show flash error message when new comment failed to be posted', done => { - mockNotesPostError(); + it('should show flash error message when new comment failed to be posted', done => { + mockNotesPostError(); - $('.js-comment-button').click(); + $('.js-comment-button').click(); - setTimeout(() => { - expect( - $notesContainer - .parent() - .find('.flash-container .flash-text') - .is(':visible'), - ).toEqual(true); + setTimeout(() => { + expect( + $notesContainer + .parent() + .find('.flash-container .flash-text') + .is(':visible'), + ).toEqual(true); - done(); - }); + done(); }); + }); - it('should show flash error message when comment failed to be updated', done => { - mockNotesPost(); + it('should show flash error message when comment failed to be updated', done => { + mockNotesPost(); - $('.js-comment-button').click(); + $('.js-comment-button').click(); - timeoutPromise() - .then(() => { - const $noteEl = $notesContainer.find(`#note_${note.id}`); - $noteEl.find('.js-note-edit').click(); - $noteEl.find('textarea.js-note-text').val(updatedComment); + timeoutPromise() + .then(() => { + const $noteEl = $notesContainer.find(`#note_${note.id}`); + $noteEl.find('.js-note-edit').click(); + $noteEl.find('textarea.js-note-text').val(updatedComment); - mock.restore(); + mock.restore(); - mockNotesPostError(); + mockNotesPostError(); - $noteEl.find('.js-comment-save-button').click(); - }) - .then(timeoutPromise) - .then(() => { - const $updatedNoteEl = $notesContainer.find(`#note_${note.id}`); - expect($updatedNoteEl.hasClass('.being-posted')).toEqual(false); // Remove being-posted visuals - expect( - $updatedNoteEl - .find('.note-text') - .text() - .trim(), - ).toEqual(sampleComment); // See if comment reverted back to original - expect($('.flash-container').is(':visible')).toEqual(true); // Flash error message shown - - done(); - }) - .catch(done.fail); - }); + $noteEl.find('.js-comment-save-button').click(); + }) + .then(timeoutPromise) + .then(() => { + const $updatedNoteEl = $notesContainer.find(`#note_${note.id}`); + + expect($updatedNoteEl.hasClass('.being-posted')).toEqual(false); // Remove being-posted visuals + expect( + $updatedNoteEl + .find('.note-text') + .text() + .trim(), + ).toEqual(sampleComment); // See if comment reverted back to original + + expect($('.flash-container').is(':visible')).toEqual(true); // Flash error message shown + + done(); + }) + .catch(done.fail); }); + }); - describe('postComment with Slash commands', () => { - const sampleComment = '/assign @root\n/award :100:'; - const note = { - commands_changes: { - assignee_id: 1, - emoji_award: '100', - }, - errors: { - commands_only: ['Commands applied'], + describe('postComment with Slash commands', () => { + const sampleComment = '/assign @root\n/award :100:'; + const note = { + commands_changes: { + assignee_id: 1, + emoji_award: '100', + }, + errors: { + commands_only: ['Commands applied'], + }, + valid: false, + }; + let $form; + let $notesContainer; + let mock; + + beforeEach(() => { + mock = new MockAdapter(axios); + mock.onPost(NOTES_POST_PATH).reply(200, note); + + this.notes = new Notes('', []); + window.gon.current_username = 'root'; + window.gon.current_user_fullname = 'Administrator'; + gl.awardsHandler = { + addAwardToEmojiBar: () => {}, + scrollToAwards: () => {}, + }; + gl.GfmAutoComplete = { + dataSources: { + commands: '/root/test-project/autocomplete_sources/commands', }, - valid: false, }; - let $form; - let $notesContainer; - let mock; - - beforeEach(() => { - mock = new MockAdapter(axios); - mock.onPost(NOTES_POST_PATH).reply(200, note); - - this.notes = new Notes('', []); - window.gon.current_username = 'root'; - window.gon.current_user_fullname = 'Administrator'; - gl.awardsHandler = { - addAwardToEmojiBar: () => {}, - scrollToAwards: () => {}, - }; - gl.GfmAutoComplete = { - dataSources: { - commands: '/root/test-project/autocomplete_sources/commands', - }, - }; - $form = $('form.js-main-target-form'); - $notesContainer = $('ul.main-notes-list'); - $form.find('textarea.js-note-text').val(sampleComment); - }); + $form = $('form.js-main-target-form'); + $notesContainer = $('ul.main-notes-list'); + $form.find('textarea.js-note-text').val(sampleComment); + }); - afterEach(() => { - mock.restore(); - }); + afterEach(() => { + mock.restore(); + }); - it('should remove slash command placeholder when comment with slash commands is done posting', done => { - spyOn(gl.awardsHandler, 'addAwardToEmojiBar').and.callThrough(); - $('.js-comment-button').click(); + it('should remove slash command placeholder when comment with slash commands is done posting', done => { + spyOn(gl.awardsHandler, 'addAwardToEmojiBar').and.callThrough(); + $('.js-comment-button').click(); - expect($notesContainer.find('.system-note.being-posted').length).toEqual(1); // Placeholder shown + expect($notesContainer.find('.system-note.being-posted').length).toEqual(1); // Placeholder shown - setTimeout(() => { - expect($notesContainer.find('.system-note.being-posted').length).toEqual(0); // Placeholder removed - done(); - }); + setTimeout(() => { + expect($notesContainer.find('.system-note.being-posted').length).toEqual(0); // Placeholder removed + done(); }); }); + }); - describe('update comment with script tags', () => { - const sampleComment = '<script></script>'; - const updatedComment = '<script></script>'; - const note = { - id: 1234, - html: `<li class="note note-row-1234 timeline-entry" id="note_1234"> - <div class="note-text">${sampleComment}</div> - </li>`, - note: sampleComment, - valid: true, - }; - let $form; - let $notesContainer; - let mock; + describe('update comment with script tags', () => { + const sampleComment = '<script></script>'; + const updatedComment = '<script></script>'; + const note = { + id: 1234, + html: `<li class="note note-row-1234 timeline-entry" id="note_1234"> + <div class="note-text">${sampleComment}</div> + </li>`, + note: sampleComment, + valid: true, + }; + let $form; + let $notesContainer; + let mock; + + beforeEach(() => { + mock = new MockAdapter(axios); + mock.onPost(NOTES_POST_PATH).reply(200, note); + + this.notes = new Notes('', []); + window.gon.current_username = 'root'; + window.gon.current_user_fullname = 'Administrator'; + $form = $('form.js-main-target-form'); + $notesContainer = $('ul.main-notes-list'); + $form.find('textarea.js-note-text').html(sampleComment); + }); - beforeEach(() => { - mock = new MockAdapter(axios); - mock.onPost(NOTES_POST_PATH).reply(200, note); - - this.notes = new Notes('', []); - window.gon.current_username = 'root'; - window.gon.current_user_fullname = 'Administrator'; - $form = $('form.js-main-target-form'); - $notesContainer = $('ul.main-notes-list'); - $form.find('textarea.js-note-text').html(sampleComment); - }); + afterEach(() => { + mock.restore(); + }); - afterEach(() => { - mock.restore(); - }); + it('should not render a script tag', done => { + $('.js-comment-button').click(); - it('should not render a script tag', done => { - $('.js-comment-button').click(); + setTimeout(() => { + const $noteEl = $notesContainer.find(`#note_${note.id}`); + $noteEl.find('.js-note-edit').click(); + $noteEl.find('textarea.js-note-text').html(updatedComment); + $noteEl.find('.js-comment-save-button').click(); - setTimeout(() => { - const $noteEl = $notesContainer.find(`#note_${note.id}`); - $noteEl.find('.js-note-edit').click(); - $noteEl.find('textarea.js-note-text').html(updatedComment); - $noteEl.find('.js-comment-save-button').click(); + const $updatedNoteEl = $notesContainer + .find(`#note_${note.id}`) + .find('.js-task-list-container'); - const $updatedNoteEl = $notesContainer - .find(`#note_${note.id}`) - .find('.js-task-list-container'); - expect( - $updatedNoteEl - .find('.note-text') - .text() - .trim(), - ).toEqual(''); + expect( + $updatedNoteEl + .find('.note-text') + .text() + .trim(), + ).toEqual(''); - done(); - }); + done(); }); }); + }); - describe('getFormData', () => { - let $form; - let sampleComment; + describe('getFormData', () => { + let $form; + let sampleComment; - beforeEach(() => { - this.notes = new Notes('', []); + beforeEach(() => { + this.notes = new Notes('', []); - $form = $('form'); - sampleComment = 'foobar'; - }); + $form = $('form'); + sampleComment = 'foobar'; + }); - it('should return form metadata object from form reference', () => { - $form.find('textarea.js-note-text').val(sampleComment); - const { formData, formContent, formAction } = this.notes.getFormData($form); + it('should return form metadata object from form reference', () => { + $form.find('textarea.js-note-text').val(sampleComment); + const { formData, formContent, formAction } = this.notes.getFormData($form); - expect(formData.indexOf(sampleComment)).toBeGreaterThan(-1); - expect(formContent).toEqual(sampleComment); - expect(formAction).toEqual($form.attr('action')); - }); + expect(formData.indexOf(sampleComment)).toBeGreaterThan(-1); + expect(formContent).toEqual(sampleComment); + expect(formAction).toEqual($form.attr('action')); + }); - it('should return form metadata with sanitized formContent from form reference', () => { - spyOn(_, 'escape').and.callFake(htmlEscape); + it('should return form metadata with sanitized formContent from form reference', () => { + spyOn(_, 'escape').and.callFake(htmlEscape); - sampleComment = '<script>alert("Boom!");</script>'; - $form.find('textarea.js-note-text').val(sampleComment); + sampleComment = '<script>alert("Boom!");</script>'; + $form.find('textarea.js-note-text').val(sampleComment); - const { formContent } = this.notes.getFormData($form); + const { formContent } = this.notes.getFormData($form); - expect(_.escape).toHaveBeenCalledWith(sampleComment); - expect(formContent).toEqual('<script>alert("Boom!");</script>'); - }); + expect(_.escape).toHaveBeenCalledWith(sampleComment); + expect(formContent).toEqual('<script>alert("Boom!");</script>'); }); + }); - describe('hasQuickActions', () => { - beforeEach(() => { - this.notes = new Notes('', []); - }); + describe('hasQuickActions', () => { + beforeEach(() => { + this.notes = new Notes('', []); + }); - it('should return true when comment begins with a quick action', () => { - const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign Merging this'; - const hasQuickActions = this.notes.hasQuickActions(sampleComment); + it('should return true when comment begins with a quick action', () => { + const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign Merging this'; + const hasQuickActions = this.notes.hasQuickActions(sampleComment); - expect(hasQuickActions).toBeTruthy(); - }); + expect(hasQuickActions).toBeTruthy(); + }); - it('should return false when comment does NOT begin with a quick action', () => { - const sampleComment = 'Hey, /unassign Merging this'; - const hasQuickActions = this.notes.hasQuickActions(sampleComment); + it('should return false when comment does NOT begin with a quick action', () => { + const sampleComment = 'Hey, /unassign Merging this'; + const hasQuickActions = this.notes.hasQuickActions(sampleComment); - expect(hasQuickActions).toBeFalsy(); - }); + expect(hasQuickActions).toBeFalsy(); + }); - it('should return false when comment does NOT have any quick actions', () => { - const sampleComment = 'Looking good, Awesome!'; - const hasQuickActions = this.notes.hasQuickActions(sampleComment); + it('should return false when comment does NOT have any quick actions', () => { + const sampleComment = 'Looking good, Awesome!'; + const hasQuickActions = this.notes.hasQuickActions(sampleComment); - expect(hasQuickActions).toBeFalsy(); - }); + expect(hasQuickActions).toBeFalsy(); }); + }); - describe('stripQuickActions', () => { - it('should strip quick actions from the comment which begins with a quick action', () => { - this.notes = new Notes(); - const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign Merging this'; - const stripedComment = this.notes.stripQuickActions(sampleComment); + describe('stripQuickActions', () => { + it('should strip quick actions from the comment which begins with a quick action', () => { + this.notes = new Notes(); + const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign Merging this'; + const stripedComment = this.notes.stripQuickActions(sampleComment); - expect(stripedComment).toBe(''); - }); + expect(stripedComment).toBe(''); + }); - it('should strip quick actions from the comment but leaves plain comment if it is present', () => { - this.notes = new Notes(); - const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign\nMerging this'; - const stripedComment = this.notes.stripQuickActions(sampleComment); + it('should strip quick actions from the comment but leaves plain comment if it is present', () => { + this.notes = new Notes(); + const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign\nMerging this'; + const stripedComment = this.notes.stripQuickActions(sampleComment); - expect(stripedComment).toBe('Merging this'); - }); + expect(stripedComment).toBe('Merging this'); + }); - it('should NOT strip string that has slashes within', () => { - this.notes = new Notes(); - const sampleComment = 'http://127.0.0.1:3000/root/gitlab-shell/issues/1'; - const stripedComment = this.notes.stripQuickActions(sampleComment); + it('should NOT strip string that has slashes within', () => { + this.notes = new Notes(); + const sampleComment = 'http://127.0.0.1:3000/root/gitlab-shell/issues/1'; + const stripedComment = this.notes.stripQuickActions(sampleComment); - expect(stripedComment).toBe(sampleComment); - }); + expect(stripedComment).toBe(sampleComment); }); + }); - describe('getQuickActionDescription', () => { - const availableQuickActions = [ - { name: 'close', description: 'Close this issue', params: [] }, - { name: 'title', description: 'Change title', params: [{}] }, - { name: 'estimate', description: 'Set time estimate', params: [{}] }, - ]; + describe('getQuickActionDescription', () => { + const availableQuickActions = [ + { name: 'close', description: 'Close this issue', params: [] }, + { name: 'title', description: 'Change title', params: [{}] }, + { name: 'estimate', description: 'Set time estimate', params: [{}] }, + ]; - beforeEach(() => { - this.notes = new Notes(); - }); + beforeEach(() => { + this.notes = new Notes(); + }); - it('should return executing quick action description when note has single quick action', () => { - const sampleComment = '/close'; - expect(this.notes.getQuickActionDescription(sampleComment, availableQuickActions)).toBe( - 'Applying command to close this issue', - ); - }); + it('should return executing quick action description when note has single quick action', () => { + const sampleComment = '/close'; - it('should return generic multiple quick action description when note has multiple quick actions', () => { - const sampleComment = '/close\n/title [Duplicate] Issue foobar'; - expect(this.notes.getQuickActionDescription(sampleComment, availableQuickActions)).toBe( - 'Applying multiple commands', - ); - }); + expect(this.notes.getQuickActionDescription(sampleComment, availableQuickActions)).toBe( + 'Applying command to close this issue', + ); + }); - it('should return generic quick action description when available quick actions list is not populated', () => { - const sampleComment = '/close\n/title [Duplicate] Issue foobar'; - expect(this.notes.getQuickActionDescription(sampleComment)).toBe('Applying command'); - }); + it('should return generic multiple quick action description when note has multiple quick actions', () => { + const sampleComment = '/close\n/title [Duplicate] Issue foobar'; + + expect(this.notes.getQuickActionDescription(sampleComment, availableQuickActions)).toBe( + 'Applying multiple commands', + ); }); - describe('createPlaceholderNote', () => { - const sampleComment = 'foobar'; - const uniqueId = 'b1234-a4567'; - const currentUsername = 'root'; - const currentUserFullname = 'Administrator'; - const currentUserAvatar = 'avatar_url'; + it('should return generic quick action description when available quick actions list is not populated', () => { + const sampleComment = '/close\n/title [Duplicate] Issue foobar'; - beforeEach(() => { - this.notes = new Notes('', []); - }); + expect(this.notes.getQuickActionDescription(sampleComment)).toBe('Applying command'); + }); + }); - it('should return constructed placeholder element for regular note based on form contents', () => { - const $tempNote = this.notes.createPlaceholderNote({ - formContent: sampleComment, - uniqueId, - isDiscussionNote: false, - currentUsername, - currentUserFullname, - currentUserAvatar, - }); - const $tempNoteHeader = $tempNote.find('.note-header'); - - expect($tempNote.prop('nodeName')).toEqual('LI'); - expect($tempNote.attr('id')).toEqual(uniqueId); - expect($tempNote.hasClass('being-posted')).toBeTruthy(); - expect($tempNote.hasClass('fade-in-half')).toBeTruthy(); - $tempNote.find('.timeline-icon > a, .note-header-info > a').each(function() { - expect($(this).attr('href')).toEqual(`/${currentUsername}`); - }); - expect($tempNote.find('.timeline-icon .avatar').attr('src')).toEqual(currentUserAvatar); - expect($tempNote.find('.timeline-content').hasClass('discussion')).toBeFalsy(); - expect( - $tempNoteHeader - .find('.d-none.d-sm-inline-block') - .text() - .trim(), - ).toEqual(currentUserFullname); - expect( - $tempNoteHeader - .find('.note-headline-light') - .text() - .trim(), - ).toEqual(`@${currentUsername}`); - expect( - $tempNote - .find('.note-body .note-text p') - .text() - .trim(), - ).toEqual(sampleComment); - }); + describe('createPlaceholderNote', () => { + const sampleComment = 'foobar'; + const uniqueId = 'b1234-a4567'; + const currentUsername = 'root'; + const currentUserFullname = 'Administrator'; + const currentUserAvatar = 'avatar_url'; - it('should return constructed placeholder element for discussion note based on form contents', () => { - const $tempNote = this.notes.createPlaceholderNote({ - formContent: sampleComment, - uniqueId, - isDiscussionNote: true, - currentUsername, - currentUserFullname, - }); + beforeEach(() => { + this.notes = new Notes('', []); + }); - expect($tempNote.prop('nodeName')).toEqual('LI'); - expect($tempNote.find('.timeline-content').hasClass('discussion')).toBeTruthy(); - }); + it('should return constructed placeholder element for regular note based on form contents', () => { + const $tempNote = this.notes.createPlaceholderNote({ + formContent: sampleComment, + uniqueId, + isDiscussionNote: false, + currentUsername, + currentUserFullname, + currentUserAvatar, + }); + const $tempNoteHeader = $tempNote.find('.note-header'); + + expect($tempNote.prop('nodeName')).toEqual('LI'); + expect($tempNote.attr('id')).toEqual(uniqueId); + expect($tempNote.hasClass('being-posted')).toBeTruthy(); + expect($tempNote.hasClass('fade-in-half')).toBeTruthy(); + $tempNote.find('.timeline-icon > a, .note-header-info > a').each(function() { + expect($(this).attr('href')).toEqual(`/${currentUsername}`); + }); + + expect($tempNote.find('.timeline-icon .avatar').attr('src')).toEqual(currentUserAvatar); + expect($tempNote.find('.timeline-content').hasClass('discussion')).toBeFalsy(); + expect( + $tempNoteHeader + .find('.d-none.d-sm-inline-block') + .text() + .trim(), + ).toEqual(currentUserFullname); + + expect( + $tempNoteHeader + .find('.note-headline-light') + .text() + .trim(), + ).toEqual(`@${currentUsername}`); + + expect( + $tempNote + .find('.note-body .note-text p') + .text() + .trim(), + ).toEqual(sampleComment); + }); - it('should return a escaped user name', () => { - const currentUserFullnameXSS = 'Foo <script>alert("XSS")</script>'; - const $tempNote = this.notes.createPlaceholderNote({ - formContent: sampleComment, - uniqueId, - isDiscussionNote: false, - currentUsername, - currentUserFullname: currentUserFullnameXSS, - currentUserAvatar, - }); - const $tempNoteHeader = $tempNote.find('.note-header'); - expect( - $tempNoteHeader - .find('.d-none.d-sm-inline-block') - .text() - .trim(), - ).toEqual('Foo <script>alert("XSS")</script>'); + it('should return constructed placeholder element for discussion note based on form contents', () => { + const $tempNote = this.notes.createPlaceholderNote({ + formContent: sampleComment, + uniqueId, + isDiscussionNote: true, + currentUsername, + currentUserFullname, }); + + expect($tempNote.prop('nodeName')).toEqual('LI'); + expect($tempNote.find('.timeline-content').hasClass('discussion')).toBeTruthy(); }); - describe('createPlaceholderSystemNote', () => { - const sampleCommandDescription = 'Applying command to close this issue'; - const uniqueId = 'b1234-a4567'; + it('should return a escaped user name', () => { + const currentUserFullnameXSS = 'Foo <script>alert("XSS")</script>'; + const $tempNote = this.notes.createPlaceholderNote({ + formContent: sampleComment, + uniqueId, + isDiscussionNote: false, + currentUsername, + currentUserFullname: currentUserFullnameXSS, + currentUserAvatar, + }); + const $tempNoteHeader = $tempNote.find('.note-header'); + + expect( + $tempNoteHeader + .find('.d-none.d-sm-inline-block') + .text() + .trim(), + ).toEqual('Foo <script>alert("XSS")</script>'); + }); + }); - beforeEach(() => { - this.notes = new Notes('', []); - spyOn(_, 'escape').and.callFake(htmlEscape); - }); + describe('createPlaceholderSystemNote', () => { + const sampleCommandDescription = 'Applying command to close this issue'; + const uniqueId = 'b1234-a4567'; - it('should return constructed placeholder element for system note based on form contents', () => { - const $tempNote = this.notes.createPlaceholderSystemNote({ - formContent: sampleCommandDescription, - uniqueId, - }); + beforeEach(() => { + this.notes = new Notes('', []); + spyOn(_, 'escape').and.callFake(htmlEscape); + }); - expect($tempNote.prop('nodeName')).toEqual('LI'); - expect($tempNote.attr('id')).toEqual(uniqueId); - expect($tempNote.hasClass('being-posted')).toBeTruthy(); - expect($tempNote.hasClass('fade-in-half')).toBeTruthy(); - expect( - $tempNote - .find('.timeline-content i') - .text() - .trim(), - ).toEqual(sampleCommandDescription); - }); + it('should return constructed placeholder element for system note based on form contents', () => { + const $tempNote = this.notes.createPlaceholderSystemNote({ + formContent: sampleCommandDescription, + uniqueId, + }); + + expect($tempNote.prop('nodeName')).toEqual('LI'); + expect($tempNote.attr('id')).toEqual(uniqueId); + expect($tempNote.hasClass('being-posted')).toBeTruthy(); + expect($tempNote.hasClass('fade-in-half')).toBeTruthy(); + expect( + $tempNote + .find('.timeline-content i') + .text() + .trim(), + ).toEqual(sampleCommandDescription); }); + }); - describe('appendFlash', () => { - beforeEach(() => { - this.notes = new Notes(); - }); + describe('appendFlash', () => { + beforeEach(() => { + this.notes = new Notes(); + }); - it('shows a flash message', () => { - this.notes.addFlash('Error message', FLASH_TYPE_ALERT, this.notes.parentTimeline.get(0)); + it('shows a flash message', () => { + this.notes.addFlash('Error message', FLASH_TYPE_ALERT, this.notes.parentTimeline.get(0)); - expect($('.flash-alert').is(':visible')).toBeTruthy(); - }); + expect($('.flash-alert').is(':visible')).toBeTruthy(); }); + }); - describe('clearFlash', () => { - beforeEach(() => { - $(document).off('ajax:success'); - this.notes = new Notes(); - }); + describe('clearFlash', () => { + beforeEach(() => { + $(document).off('ajax:success'); + this.notes = new Notes(); + }); - it('hides visible flash message', () => { - this.notes.addFlash('Error message 1', FLASH_TYPE_ALERT, this.notes.parentTimeline.get(0)); + it('hides visible flash message', () => { + this.notes.addFlash('Error message 1', FLASH_TYPE_ALERT, this.notes.parentTimeline.get(0)); - this.notes.clearFlash(); + this.notes.clearFlash(); - expect($('.flash-alert').is(':visible')).toBeFalsy(); - }); + expect($('.flash-alert').is(':visible')).toBeFalsy(); }); }); -}.call(window)); +}); diff --git a/spec/javascripts/oauth_remember_me_spec.js b/spec/javascripts/oauth_remember_me_spec.js index 8816fe6defb..2caa266b85f 100644 --- a/spec/javascripts/oauth_remember_me_spec.js +++ b/spec/javascripts/oauth_remember_me_spec.js @@ -13,8 +13,13 @@ describe('OAuthRememberMe', () => { it('adds the "remember_me" query parameter to all OAuth login buttons', () => { $('#oauth-container #remember_me').click(); - expect($('#oauth-container .oauth-login.twitter').attr('href')).toBe('http://example.com/?remember_me=1'); - expect($('#oauth-container .oauth-login.github').attr('href')).toBe('http://example.com/?remember_me=1'); + expect($('#oauth-container .oauth-login.twitter').attr('href')).toBe( + 'http://example.com/?remember_me=1', + ); + + expect($('#oauth-container .oauth-login.github').attr('href')).toBe( + 'http://example.com/?remember_me=1', + ); }); it('removes the "remember_me" query parameter from all OAuth login buttons', () => { diff --git a/spec/javascripts/pager_spec.js b/spec/javascripts/pager_spec.js index 04f2e7ef4f9..93efc139254 100644 --- a/spec/javascripts/pager_spec.js +++ b/spec/javascripts/pager_spec.js @@ -30,6 +30,7 @@ describe('pager', () => { const href = `${gl.TEST_HOST}/some_list.json`; setFixtures(`<div class="content_list" data-href="${href}"></div>`); Pager.init(); + expect(Pager.url).toBe(href); }); @@ -37,12 +38,14 @@ describe('pager', () => { const href = `${gl.TEST_HOST}/some_list`; spyOnDependency(Pager, 'removeParams').and.returnValue(href); Pager.init(); + expect(Pager.url).toBe(href); }); it('should get initial offset from query parameter', () => { window.history.replaceState({}, null, '?offset=100'); Pager.init(); + expect(Pager.offset).toBe(100); }); @@ -51,6 +54,7 @@ describe('pager', () => { const href = `${gl.TEST_HOST}/some_list?filter=test`; const removeParams = spyOnDependency(Pager, 'removeParams').and.returnValue(href); Pager.init(); + expect(removeParams).toHaveBeenCalledWith(['limit', 'offset']); expect(Pager.url).toEqual(href); }); @@ -132,6 +136,7 @@ describe('pager', () => { offset: 100, }, }); + expect(url).toBe('/some_list'); done(); diff --git a/spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js b/spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js index b0dc6ccc3d4..23d07056925 100644 --- a/spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js +++ b/spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js @@ -8,14 +8,15 @@ describe('Abuse Reports', () => { let $messages; - const assertMaxLength = $message => expect($message.text().length).toEqual(MAX_MESSAGE_LENGTH); - const findMessage = searchText => $messages.filter( - (index, element) => element.innerText.indexOf(searchText) > -1, - ).first(); + const assertMaxLength = $message => { + expect($message.text().length).toEqual(MAX_MESSAGE_LENGTH); + }; + const findMessage = searchText => + $messages.filter((index, element) => element.innerText.indexOf(searchText) > -1).first(); preloadFixtures(FIXTURE); - beforeEach(function () { + beforeEach(function() { loadFixtures(FIXTURE); this.abuseReports = new AbuseReports(); $messages = $('.abuse-reports .message'); @@ -23,18 +24,21 @@ describe('Abuse Reports', () => { it('should truncate long messages', () => { const $longMessage = findMessage('LONG MESSAGE'); + expect($longMessage.data('originalMessage')).toEqual(jasmine.anything()); assertMaxLength($longMessage); }); it('should not truncate short messages', () => { const $shortMessage = findMessage('SHORT MESSAGE'); + expect($shortMessage.data('originalMessage')).not.toEqual(jasmine.anything()); }); it('should allow clicking a truncated message to expand and collapse the full message', () => { const $longMessage = findMessage('LONG MESSAGE'); $longMessage.click(); + expect($longMessage.data('originalMessage').length).toEqual($longMessage.text().length); $longMessage.click(); assertMaxLength($longMessage); diff --git a/spec/javascripts/pages/admin/application_settings/account_and_limits_spec.js b/spec/javascripts/pages/admin/application_settings/account_and_limits_spec.js index 4dbfd8f0eaa..561bd2c96cb 100644 --- a/spec/javascripts/pages/admin/application_settings/account_and_limits_spec.js +++ b/spec/javascripts/pages/admin/application_settings/account_and_limits_spec.js @@ -1,6 +1,8 @@ import $ from 'jquery'; -import initUserInternalRegexPlaceholder, { PLACEHOLDER_USER_EXTERNAL_DEFAULT_FALSE, - PLACEHOLDER_USER_EXTERNAL_DEFAULT_TRUE } from '~/pages/admin/application_settings/account_and_limits'; +import initUserInternalRegexPlaceholder, { + PLACEHOLDER_USER_EXTERNAL_DEFAULT_FALSE, + PLACEHOLDER_USER_EXTERNAL_DEFAULT_TRUE, +} from '~/pages/admin/application_settings/account_and_limits'; describe('AccountAndLimits', () => { const FIXTURE = 'application_settings/accounts_and_limit.html.raw'; @@ -22,8 +24,9 @@ describe('AccountAndLimits', () => { expect($userInternalRegex.readOnly).toBeTruthy(); }); - it('is checked', (done) => { + it('is checked', done => { if (!$userDefaultExternal.prop('checked')) $userDefaultExternal.click(); + expect($userDefaultExternal.prop('checked')).toBeTruthy(); expect($userInternalRegex.placeholder).toEqual(PLACEHOLDER_USER_EXTERNAL_DEFAULT_TRUE); expect($userInternalRegex.readOnly).toBeFalsy(); diff --git a/spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js b/spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js index b69e5f9a3a0..6bfb3f5ca21 100644 --- a/spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js +++ b/spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js @@ -21,10 +21,10 @@ describe('stop_jobs_modal.vue', () => { }); describe('onSubmit', () => { - it('stops jobs and redirects to overview page', (done) => { + it('stops jobs and redirects to overview page', done => { const responseURL = `${gl.TEST_HOST}/stop_jobs_modal.vue/jobs`; const redirectSpy = spyOnDependency(stopJobsModal, 'redirectTo'); - spyOn(axios, 'post').and.callFake((url) => { + spyOn(axios, 'post').and.callFake(url => { expect(url).toBe(props.url); return Promise.resolve({ request: { @@ -34,24 +34,24 @@ describe('stop_jobs_modal.vue', () => { }); vm.onSubmit() - .then(() => { - expect(redirectSpy).toHaveBeenCalledWith(responseURL); - }) - .then(done) - .catch(done.fail); + .then(() => { + expect(redirectSpy).toHaveBeenCalledWith(responseURL); + }) + .then(done) + .catch(done.fail); }); - it('displays error if stopping jobs failed', (done) => { + it('displays error if stopping jobs failed', done => { const dummyError = new Error('stopping jobs failed'); const redirectSpy = spyOnDependency(stopJobsModal, 'redirectTo'); - spyOn(axios, 'post').and.callFake((url) => { + spyOn(axios, 'post').and.callFake(url => { expect(url).toBe(props.url); return Promise.reject(dummyError); }); vm.onSubmit() .then(done.fail) - .catch((error) => { + .catch(error => { expect(error).toBe(dummyError); expect(redirectSpy).not.toHaveBeenCalled(); }) diff --git a/spec/javascripts/pages/admin/users/new/index_spec.js b/spec/javascripts/pages/admin/users/new/index_spec.js index 2bac3951c3a..5a849f34bc3 100644 --- a/spec/javascripts/pages/admin/users/new/index_spec.js +++ b/spec/javascripts/pages/admin/users/new/index_spec.js @@ -20,7 +20,7 @@ describe('UserInternalRegexHandler', () => { }); describe('Behaviour of userExternal checkbox when', () => { - it('matches email as internal', (done) => { + it('matches email as internal', done => { expect($warningMessage.hasClass('hidden')).toBeTruthy(); $userEmail.val('test@').trigger('input'); @@ -30,7 +30,7 @@ describe('UserInternalRegexHandler', () => { done(); }); - it('matches email as external', (done) => { + it('matches email as external', done => { expect($warningMessage.hasClass('hidden')).toBeTruthy(); $userEmail.val('test.ext@').trigger('input'); diff --git a/spec/javascripts/pages/labels/components/promote_label_modal_spec.js b/spec/javascripts/pages/labels/components/promote_label_modal_spec.js index a24f8204fe1..08a8362797b 100644 --- a/spec/javascripts/pages/labels/components/promote_label_modal_spec.js +++ b/spec/javascripts/pages/labels/components/promote_label_modal_spec.js @@ -25,7 +25,11 @@ describe('Promote label modal', () => { }); it('contains the proper description', () => { - expect(vm.text).toContain(`Promoting ${labelMockData.labelTitle} will make it available for all projects inside ${labelMockData.groupName}`); + expect(vm.text).toContain( + `Promoting ${labelMockData.labelTitle} will make it available for all projects inside ${ + labelMockData.groupName + }`, + ); }); it('contains a label span with the color', () => { @@ -48,11 +52,14 @@ describe('Promote label modal', () => { vm.$destroy(); }); - it('redirects when a label is promoted', (done) => { + it('redirects when a label is promoted', done => { const responseURL = `${gl.TEST_HOST}/dummy/endpoint`; - spyOn(axios, 'post').and.callFake((url) => { + spyOn(axios, 'post').and.callFake(url => { expect(url).toBe(labelMockData.url); - expect(eventHub.$emit).toHaveBeenCalledWith('promoteLabelModal.requestStarted', labelMockData.url); + expect(eventHub.$emit).toHaveBeenCalledWith( + 'promoteLabelModal.requestStarted', + labelMockData.url, + ); return Promise.resolve({ request: { responseURL, @@ -62,25 +69,34 @@ describe('Promote label modal', () => { vm.onSubmit() .then(() => { - expect(eventHub.$emit).toHaveBeenCalledWith('promoteLabelModal.requestFinished', { labelUrl: labelMockData.url, successful: true }); + expect(eventHub.$emit).toHaveBeenCalledWith('promoteLabelModal.requestFinished', { + labelUrl: labelMockData.url, + successful: true, + }); }) .then(done) .catch(done.fail); }); - it('displays an error if promoting a label failed', (done) => { + it('displays an error if promoting a label failed', done => { const dummyError = new Error('promoting label failed'); dummyError.response = { status: 500 }; - spyOn(axios, 'post').and.callFake((url) => { + spyOn(axios, 'post').and.callFake(url => { expect(url).toBe(labelMockData.url); - expect(eventHub.$emit).toHaveBeenCalledWith('promoteLabelModal.requestStarted', labelMockData.url); + expect(eventHub.$emit).toHaveBeenCalledWith( + 'promoteLabelModal.requestStarted', + labelMockData.url, + ); return Promise.reject(dummyError); }); vm.onSubmit() - .catch((error) => { + .catch(error => { expect(error).toBe(dummyError); - expect(eventHub.$emit).toHaveBeenCalledWith('promoteLabelModal.requestFinished', { labelUrl: labelMockData.url, successful: false }); + expect(eventHub.$emit).toHaveBeenCalledWith('promoteLabelModal.requestFinished', { + labelUrl: labelMockData.url, + successful: false, + }); }) .then(done) .catch(done.fail); diff --git a/spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js b/spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js index 94401beb5c9..fe293083e4c 100644 --- a/spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js +++ b/spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js @@ -27,11 +27,14 @@ describe('delete_milestone_modal.vue', () => { spyOn(eventHub, '$emit'); }); - it('deletes milestone and redirects to overview page', (done) => { + it('deletes milestone and redirects to overview page', done => { const responseURL = `${gl.TEST_HOST}/delete_milestone_modal.vue/milestoneOverview`; - spyOn(axios, 'delete').and.callFake((url) => { + spyOn(axios, 'delete').and.callFake(url => { expect(url).toBe(props.milestoneUrl); - expect(eventHub.$emit).toHaveBeenCalledWith('deleteMilestoneModal.requestStarted', props.milestoneUrl); + expect(eventHub.$emit).toHaveBeenCalledWith( + 'deleteMilestoneModal.requestStarted', + props.milestoneUrl, + ); eventHub.$emit.calls.reset(); return Promise.resolve({ request: { @@ -42,30 +45,39 @@ describe('delete_milestone_modal.vue', () => { const redirectSpy = spyOnDependency(deleteMilestoneModal, 'redirectTo'); vm.onSubmit() - .then(() => { - expect(redirectSpy).toHaveBeenCalledWith(responseURL); - expect(eventHub.$emit).toHaveBeenCalledWith('deleteMilestoneModal.requestFinished', { milestoneUrl: props.milestoneUrl, successful: true }); - }) - .then(done) - .catch(done.fail); + .then(() => { + expect(redirectSpy).toHaveBeenCalledWith(responseURL); + expect(eventHub.$emit).toHaveBeenCalledWith('deleteMilestoneModal.requestFinished', { + milestoneUrl: props.milestoneUrl, + successful: true, + }); + }) + .then(done) + .catch(done.fail); }); - it('displays error if deleting milestone failed', (done) => { + it('displays error if deleting milestone failed', done => { const dummyError = new Error('deleting milestone failed'); dummyError.response = { status: 418 }; - spyOn(axios, 'delete').and.callFake((url) => { + spyOn(axios, 'delete').and.callFake(url => { expect(url).toBe(props.milestoneUrl); - expect(eventHub.$emit).toHaveBeenCalledWith('deleteMilestoneModal.requestStarted', props.milestoneUrl); + expect(eventHub.$emit).toHaveBeenCalledWith( + 'deleteMilestoneModal.requestStarted', + props.milestoneUrl, + ); eventHub.$emit.calls.reset(); return Promise.reject(dummyError); }); const redirectSpy = spyOnDependency(deleteMilestoneModal, 'redirectTo'); vm.onSubmit() - .catch((error) => { + .catch(error => { expect(error).toBe(dummyError); expect(redirectSpy).not.toHaveBeenCalled(); - expect(eventHub.$emit).toHaveBeenCalledWith('deleteMilestoneModal.requestFinished', { milestoneUrl: props.milestoneUrl, successful: false }); + expect(eventHub.$emit).toHaveBeenCalledWith('deleteMilestoneModal.requestFinished', { + milestoneUrl: props.milestoneUrl, + successful: false, + }); }) .then(done) .catch(done.fail); @@ -81,7 +93,8 @@ describe('delete_milestone_modal.vue', () => { }); it('contains neither issue nor milestone count', () => { - vm = mountComponent(Component, { ...props, + vm = mountComponent(Component, { + ...props, issueCount: 0, mergeRequestCount: 0, }); diff --git a/spec/javascripts/pages/milestones/shared/components/promote_milestone_modal_spec.js b/spec/javascripts/pages/milestones/shared/components/promote_milestone_modal_spec.js index 8b220423637..2ac73ef3024 100644 --- a/spec/javascripts/pages/milestones/shared/components/promote_milestone_modal_spec.js +++ b/spec/javascripts/pages/milestones/shared/components/promote_milestone_modal_spec.js @@ -23,7 +23,11 @@ describe('Promote milestone modal', () => { }); it('contains the proper description', () => { - expect(vm.text).toContain(`Promoting ${milestoneMockData.milestoneTitle} will make it available for all projects inside ${milestoneMockData.groupName}.`); + expect(vm.text).toContain( + `Promoting ${ + milestoneMockData.milestoneTitle + } will make it available for all projects inside ${milestoneMockData.groupName}.`, + ); }); it('contains the correct title', () => { @@ -43,11 +47,14 @@ describe('Promote milestone modal', () => { vm.$destroy(); }); - it('redirects when a milestone is promoted', (done) => { + it('redirects when a milestone is promoted', done => { const responseURL = `${gl.TEST_HOST}/dummy/endpoint`; - spyOn(axios, 'post').and.callFake((url) => { + spyOn(axios, 'post').and.callFake(url => { expect(url).toBe(milestoneMockData.url); - expect(eventHub.$emit).toHaveBeenCalledWith('promoteMilestoneModal.requestStarted', milestoneMockData.url); + expect(eventHub.$emit).toHaveBeenCalledWith( + 'promoteMilestoneModal.requestStarted', + milestoneMockData.url, + ); return Promise.resolve({ request: { responseURL, @@ -57,25 +64,34 @@ describe('Promote milestone modal', () => { vm.onSubmit() .then(() => { - expect(eventHub.$emit).toHaveBeenCalledWith('promoteMilestoneModal.requestFinished', { milestoneUrl: milestoneMockData.url, successful: true }); + expect(eventHub.$emit).toHaveBeenCalledWith('promoteMilestoneModal.requestFinished', { + milestoneUrl: milestoneMockData.url, + successful: true, + }); }) .then(done) .catch(done.fail); }); - it('displays an error if promoting a milestone failed', (done) => { + it('displays an error if promoting a milestone failed', done => { const dummyError = new Error('promoting milestone failed'); dummyError.response = { status: 500 }; - spyOn(axios, 'post').and.callFake((url) => { + spyOn(axios, 'post').and.callFake(url => { expect(url).toBe(milestoneMockData.url); - expect(eventHub.$emit).toHaveBeenCalledWith('promoteMilestoneModal.requestStarted', milestoneMockData.url); + expect(eventHub.$emit).toHaveBeenCalledWith( + 'promoteMilestoneModal.requestStarted', + milestoneMockData.url, + ); return Promise.reject(dummyError); }); vm.onSubmit() - .catch((error) => { + .catch(error => { expect(error).toBe(dummyError); - expect(eventHub.$emit).toHaveBeenCalledWith('promoteMilestoneModal.requestFinished', { milestoneUrl: milestoneMockData.url, successful: false }); + expect(eventHub.$emit).toHaveBeenCalledWith('promoteMilestoneModal.requestFinished', { + milestoneUrl: milestoneMockData.url, + successful: false, + }); }) .then(done) .catch(done.fail); diff --git a/spec/javascripts/pages/profiles/show/emoji_menu_spec.js b/spec/javascripts/pages/profiles/show/emoji_menu_spec.js index b70368fc92f..864bda65736 100644 --- a/spec/javascripts/pages/profiles/show/emoji_menu_spec.js +++ b/spec/javascripts/pages/profiles/show/emoji_menu_spec.js @@ -81,6 +81,7 @@ describe('EmojiMenu', () => { 'mouseenter focus', jasmine.anything(), ); + expect(emojiMenu.registerEventListener).toHaveBeenCalledWith( 'on', jasmine.anything(), @@ -107,6 +108,7 @@ describe('EmojiMenu', () => { it('renders the menu with custom menu class', () => { const menuElement = () => document.body.querySelector(`.emoji-menu.${dummyMenuClass} .emoji-menu-content`); + expect(menuElement()).toBe(null); emojiMenu.createEmojiMenu(); diff --git a/spec/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js b/spec/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js index 4655e29eed0..b20bc96f9be 100644 --- a/spec/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js +++ b/spec/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js @@ -19,10 +19,10 @@ window.gl.pipelineScheduleFieldErrors = { updateFormValidityState: () => {}, }; -describe('Interval Pattern Input Component', function () { - describe('when prop initialCronInterval is passed (edit)', function () { - describe('when prop initialCronInterval is custom', function () { - beforeEach(function () { +describe('Interval Pattern Input Component', function() { + describe('when prop initialCronInterval is passed (edit)', function() { + describe('when prop initialCronInterval is custom', function() { + beforeEach(function() { this.initialCronInterval = '1 2 3 4 5'; this.intervalPatternComponent = new IntervalPatternInputComponent({ propsData: { @@ -31,15 +31,15 @@ describe('Interval Pattern Input Component', function () { }).$mount(); }); - it('is initialized as a Vue component', function () { + it('is initialized as a Vue component', function() { expect(this.intervalPatternComponent).toBeDefined(); }); - it('prop initialCronInterval is set', function () { + it('prop initialCronInterval is set', function() { expect(this.intervalPatternComponent.initialCronInterval).toBe(this.initialCronInterval); }); - it('sets isEditable to true', function (done) { + it('sets isEditable to true', function(done) { Vue.nextTick(() => { expect(this.intervalPatternComponent.isEditable).toBe(true); done(); @@ -47,8 +47,8 @@ describe('Interval Pattern Input Component', function () { }); }); - describe('when prop initialCronInterval is preset', function () { - beforeEach(function () { + describe('when prop initialCronInterval is preset', function() { + beforeEach(function() { this.intervalPatternComponent = new IntervalPatternInputComponent({ propsData: { inputNameAttribute, @@ -57,11 +57,11 @@ describe('Interval Pattern Input Component', function () { }).$mount(); }); - it('is initialized as a Vue component', function () { + it('is initialized as a Vue component', function() { expect(this.intervalPatternComponent).toBeDefined(); }); - it('sets isEditable to false', function (done) { + it('sets isEditable to false', function(done) { Vue.nextTick(() => { expect(this.intervalPatternComponent.isEditable).toBe(false); done(); @@ -70,8 +70,8 @@ describe('Interval Pattern Input Component', function () { }); }); - describe('when prop initialCronInterval is not passed (new)', function () { - beforeEach(function () { + describe('when prop initialCronInterval is not passed (new)', function() { + beforeEach(function() { this.intervalPatternComponent = new IntervalPatternInputComponent({ propsData: { inputNameAttribute, @@ -79,16 +79,17 @@ describe('Interval Pattern Input Component', function () { }).$mount(); }); - it('is initialized as a Vue component', function () { + it('is initialized as a Vue component', function() { expect(this.intervalPatternComponent).toBeDefined(); }); - it('prop initialCronInterval is set', function () { + it('prop initialCronInterval is set', function() { const defaultInitialCronInterval = ''; + expect(this.intervalPatternComponent.initialCronInterval).toBe(defaultInitialCronInterval); }); - it('sets isEditable to true', function (done) { + it('sets isEditable to true', function(done) { Vue.nextTick(() => { expect(this.intervalPatternComponent.isEditable).toBe(true); done(); @@ -96,8 +97,8 @@ describe('Interval Pattern Input Component', function () { }); }); - describe('User Actions', function () { - beforeEach(function () { + describe('User Actions', function() { + beforeEach(function() { // For an unknown reason, some browsers do not propagate click events // on radio buttons in a way Vue can register. So, we have to mount // to a fixture. @@ -111,66 +112,79 @@ describe('Interval Pattern Input Component', function () { }).$mount('#my-mount'); }); - it('cronInterval is updated when everyday preset interval is selected', function (done) { + it('cronInterval is updated when everyday preset interval is selected', function(done) { this.intervalPatternComponent.$el.querySelector('#every-day').click(); Vue.nextTick(() => { expect(this.intervalPatternComponent.cronInterval).toBe(cronIntervalPresets.everyDay); - expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').value).toBe(cronIntervalPresets.everyDay); + expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').value).toBe( + cronIntervalPresets.everyDay, + ); done(); }); }); - it('cronInterval is updated when everyweek preset interval is selected', function (done) { + it('cronInterval is updated when everyweek preset interval is selected', function(done) { this.intervalPatternComponent.$el.querySelector('#every-week').click(); Vue.nextTick(() => { expect(this.intervalPatternComponent.cronInterval).toBe(cronIntervalPresets.everyWeek); - expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').value).toBe(cronIntervalPresets.everyWeek); + expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').value).toBe( + cronIntervalPresets.everyWeek, + ); done(); }); }); - it('cronInterval is updated when everymonth preset interval is selected', function (done) { + it('cronInterval is updated when everymonth preset interval is selected', function(done) { this.intervalPatternComponent.$el.querySelector('#every-month').click(); Vue.nextTick(() => { expect(this.intervalPatternComponent.cronInterval).toBe(cronIntervalPresets.everyMonth); - expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').value).toBe(cronIntervalPresets.everyMonth); + expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').value).toBe( + cronIntervalPresets.everyMonth, + ); done(); }); }); - it('only a space is added to cronInterval (trimmed later) when custom radio is selected', function (done) { + it('only a space is added to cronInterval (trimmed later) when custom radio is selected', function(done) { this.intervalPatternComponent.$el.querySelector('#every-month').click(); this.intervalPatternComponent.$el.querySelector('#custom').click(); Vue.nextTick(() => { const intervalWithSpaceAppended = `${cronIntervalPresets.everyMonth} `; + expect(this.intervalPatternComponent.cronInterval).toBe(intervalWithSpaceAppended); - expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').value).toBe(intervalWithSpaceAppended); + expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').value).toBe( + intervalWithSpaceAppended, + ); done(); }); }); - it('text input is disabled when preset interval is selected', function (done) { + it('text input is disabled when preset interval is selected', function(done) { this.intervalPatternComponent.$el.querySelector('#every-month').click(); Vue.nextTick(() => { expect(this.intervalPatternComponent.isEditable).toBe(false); - expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').disabled).toBe(true); + expect( + this.intervalPatternComponent.$el.querySelector('.cron-interval-input').disabled, + ).toBe(true); done(); }); }); - it('text input is enabled when custom is selected', function (done) { + it('text input is enabled when custom is selected', function(done) { this.intervalPatternComponent.$el.querySelector('#every-month').click(); this.intervalPatternComponent.$el.querySelector('#custom').click(); Vue.nextTick(() => { expect(this.intervalPatternComponent.isEditable).toBe(true); - expect(this.intervalPatternComponent.$el.querySelector('.cron-interval-input').disabled).toBe(false); + expect( + this.intervalPatternComponent.$el.querySelector('.cron-interval-input').disabled, + ).toBe(false); done(); }); }); diff --git a/spec/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js b/spec/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js index fb7d2763b49..ea809e1f170 100644 --- a/spec/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js +++ b/spec/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js @@ -6,7 +6,7 @@ const PipelineSchedulesCalloutComponent = Vue.extend(PipelineSchedulesCallout); const cookieKey = 'pipeline_schedules_callout_dismissed'; const docsUrl = 'help/ci/scheduled_pipelines'; -describe('Pipeline Schedule Callout', function () { +describe('Pipeline Schedule Callout', function() { beforeEach(() => { setFixtures(` <div id='pipeline-schedules-callout' data-docs-url=${docsUrl}></div> @@ -76,7 +76,7 @@ describe('Pipeline Schedule Callout', function () { expect(this.calloutComponent.$el.outerHTML).toContain(docsUrl); }); - it('updates calloutDismissed when close button is clicked', (done) => { + it('updates calloutDismissed when close button is clicked', done => { this.calloutComponent.$el.querySelector('#dismiss-callout-btn').click(); Vue.nextTick(() => { @@ -85,7 +85,7 @@ describe('Pipeline Schedule Callout', function () { }); }); - it('#dismissCallout updates calloutDismissed', (done) => { + it('#dismissCallout updates calloutDismissed', done => { this.calloutComponent.dismissCallout(); Vue.nextTick(() => { @@ -94,7 +94,7 @@ describe('Pipeline Schedule Callout', function () { }); }); - it('is hidden when close button is clicked', (done) => { + it('is hidden when close button is clicked', done => { this.calloutComponent.$el.querySelector('#dismiss-callout-btn').click(); Vue.nextTick(() => { diff --git a/spec/javascripts/pdf/index_spec.js b/spec/javascripts/pdf/index_spec.js index 69230bb0937..699cf4871aa 100644 --- a/spec/javascripts/pdf/index_spec.js +++ b/spec/javascripts/pdf/index_spec.js @@ -11,7 +11,7 @@ const Component = Vue.extend(PDFLab); describe('PDF component', () => { let vm; - const checkLoaded = (done) => { + const checkLoaded = done => { if (vm.loading) { setTimeout(() => { checkLoaded(done); @@ -22,7 +22,7 @@ describe('PDF component', () => { }; describe('without PDF data', () => { - beforeEach((done) => { + beforeEach(done => { vm = new Component({ propsData: { pdf: '', @@ -40,7 +40,7 @@ describe('PDF component', () => { }); describe('with PDF data', () => { - beforeEach((done) => { + beforeEach(done => { vm = new Component({ propsData: { pdf, diff --git a/spec/javascripts/pdf/page_spec.js b/spec/javascripts/pdf/page_spec.js index a207f2afce6..ef967210b65 100644 --- a/spec/javascripts/pdf/page_spec.js +++ b/spec/javascripts/pdf/page_spec.js @@ -34,6 +34,7 @@ describe('Page component', () => { page: testPage, number: 1, }); + expect(vm.rendering).toBe(true); promise diff --git a/spec/javascripts/performance_bar/components/detailed_metric_spec.js b/spec/javascripts/performance_bar/components/detailed_metric_spec.js index c4611dc7662..a3b93280b4b 100644 --- a/spec/javascripts/performance_bar/components/detailed_metric_spec.js +++ b/spec/javascripts/performance_bar/components/detailed_metric_spec.js @@ -54,11 +54,9 @@ describe('detailedMetric', () => { }); it('adds a modal with a table of the details', () => { - vm.$el - .querySelectorAll('.performance-bar-modal td strong') - .forEach((duration, index) => { - expect(duration.innerText).toContain(requestDetails[index].duration); - }); + vm.$el.querySelectorAll('.performance-bar-modal td strong').forEach((duration, index) => { + expect(duration.innerText).toContain(requestDetails[index].duration); + }); vm.$el .querySelectorAll('.performance-bar-modal td:nth-child(2)') diff --git a/spec/javascripts/performance_bar/components/request_selector_spec.js b/spec/javascripts/performance_bar/components/request_selector_spec.js index 6108a29f8c4..a272e03c0e1 100644 --- a/spec/javascripts/performance_bar/components/request_selector_spec.js +++ b/spec/javascripts/performance_bar/components/request_selector_spec.js @@ -11,8 +11,7 @@ describe('request selector', () => { }, { id: '789', - url: - 'https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/1.json?serializer=widget', + url: 'https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/1.json?serializer=widget', }, ]; diff --git a/spec/javascripts/performance_bar/index_spec.js b/spec/javascripts/performance_bar/index_spec.js index 1784bd64adb..1444d1bb3cb 100644 --- a/spec/javascripts/performance_bar/index_spec.js +++ b/spec/javascripts/performance_bar/index_spec.js @@ -63,10 +63,7 @@ describe('performance bar wrapper', () => { it('adds the request immediately', () => { vm.loadRequestDetails('123', 'https://gitlab.com/'); - expect(vm.store.addRequest).toHaveBeenCalledWith( - '123', - 'https://gitlab.com/', - ); + expect(vm.store.addRequest).toHaveBeenCalledWith('123', 'https://gitlab.com/'); }); it('makes an HTTP request for the request details', () => { diff --git a/spec/javascripts/performance_bar/services/performance_bar_service_spec.js b/spec/javascripts/performance_bar/services/performance_bar_service_spec.js index bc6947dbe81..cfec4b779e4 100644 --- a/spec/javascripts/performance_bar/services/performance_bar_service_spec.js +++ b/spec/javascripts/performance_bar/services/performance_bar_service_spec.js @@ -8,28 +8,34 @@ describe('PerformanceBarService', () => { } it('returns false when the request URL is the peek URL', () => { - expect(fireCallback({ headers: { 'x-request-id': '123' }, url: '/peek' }, '/peek')) - .toBeFalsy(); + expect( + fireCallback({ headers: { 'x-request-id': '123' }, url: '/peek' }, '/peek'), + ).toBeFalsy(); }); it('returns false when there is no request ID', () => { - expect(fireCallback({ headers: {}, url: '/request' }, '/peek')) - .toBeFalsy(); + expect(fireCallback({ headers: {}, url: '/request' }, '/peek')).toBeFalsy(); }); it('returns false when the request is an API request', () => { - expect(fireCallback({ headers: { 'x-request-id': '123' }, url: '/api/' }, '/peek')) - .toBeFalsy(); + expect( + fireCallback({ headers: { 'x-request-id': '123' }, url: '/api/' }, '/peek'), + ).toBeFalsy(); }); it('returns false when the response is from the cache', () => { - expect(fireCallback({ headers: { 'x-request-id': '123', 'x-gitlab-from-cache': 'true' }, url: '/request' }, '/peek')) - .toBeFalsy(); + expect( + fireCallback( + { headers: { 'x-request-id': '123', 'x-gitlab-from-cache': 'true' }, url: '/request' }, + '/peek', + ), + ).toBeFalsy(); }); it('returns true when all conditions are met', () => { - expect(fireCallback({ headers: { 'x-request-id': '123' }, url: '/request' }, '/peek')) - .toBeTruthy(); + expect( + fireCallback({ headers: { 'x-request-id': '123' }, url: '/request' }, '/peek'), + ).toBeTruthy(); }); }); @@ -39,8 +45,7 @@ describe('PerformanceBarService', () => { } it('gets the request ID from the headers', () => { - expect(requestId({ headers: { 'x-request-id': '123' } }, '/peek')) - .toEqual('123'); + expect(requestId({ headers: { 'x-request-id': '123' } }, '/peek')).toEqual('123'); }); }); @@ -50,13 +55,13 @@ describe('PerformanceBarService', () => { } it('gets the request URL from the response object', () => { - expect(requestUrl({ headers: {}, url: '/request' }, '/peek')) - .toEqual('/request'); + expect(requestUrl({ headers: {}, url: '/request' }, '/peek')).toEqual('/request'); }); it('gets the request URL from response.config if present', () => { - expect(requestUrl({ headers: {}, config: { url: '/config-url' }, url: '/request' }, '/peek')) - .toEqual('/config-url'); + expect( + requestUrl({ headers: {}, config: { url: '/config-url' }, url: '/request' }, '/peek'), + ).toEqual('/config-url'); }); }); }); diff --git a/spec/javascripts/pipelines/blank_state_spec.js b/spec/javascripts/pipelines/blank_state_spec.js index b7a9b60d85c..033bd5ccb73 100644 --- a/spec/javascripts/pipelines/blank_state_spec.js +++ b/spec/javascripts/pipelines/blank_state_spec.js @@ -9,12 +9,10 @@ describe('Pipelines Blank State', () => { beforeEach(() => { Component = Vue.extend(component); - vm = mountComponent(Component, - { - svgPath: 'foo', - message: 'Blank State', - }, - ); + vm = mountComponent(Component, { + svgPath: 'foo', + message: 'Blank State', + }); }); it('should render svg', () => { @@ -22,8 +20,6 @@ describe('Pipelines Blank State', () => { }); it('should render message', () => { - expect( - vm.$el.querySelector('h4').textContent.trim(), - ).toEqual('Blank State'); + expect(vm.$el.querySelector('h4').textContent.trim()).toEqual('Blank State'); }); }); diff --git a/spec/javascripts/pipelines/empty_state_spec.js b/spec/javascripts/pipelines/empty_state_spec.js index 1e41a7bfe7c..e21dca45fa1 100644 --- a/spec/javascripts/pipelines/empty_state_spec.js +++ b/spec/javascripts/pipelines/empty_state_spec.js @@ -28,16 +28,31 @@ describe('Pipelines Empty State', () => { expect(component.$el.querySelector('h4').textContent).toContain('Build with confidence'); expect( - component.$el.querySelector('p').innerHTML.trim().replace(/\n+\s+/m, ' ').replace(/\s\s+/g, ' '), + component.$el + .querySelector('p') + .innerHTML.trim() + .replace(/\n+\s+/m, ' ') + .replace(/\s\s+/g, ' '), ).toContain('Continuous Integration can help catch bugs by running your tests automatically,'); expect( - component.$el.querySelector('p').innerHTML.trim().replace(/\n+\s+/m, ' ').replace(/\s\s+/g, ' '), - ).toContain('while Continuous Deployment can help you deliver code to your product environment'); + component.$el + .querySelector('p') + .innerHTML.trim() + .replace(/\n+\s+/m, ' ') + .replace(/\s\s+/g, ' '), + ).toContain( + 'while Continuous Deployment can help you deliver code to your product environment', + ); }); it('should render a link with provided help path', () => { - expect(component.$el.querySelector('.js-get-started-pipelines').getAttribute('href')).toEqual('foo'); - expect(component.$el.querySelector('.js-get-started-pipelines').textContent).toContain('Get started with Pipelines'); + expect(component.$el.querySelector('.js-get-started-pipelines').getAttribute('href')).toEqual( + 'foo', + ); + + expect(component.$el.querySelector('.js-get-started-pipelines').textContent).toContain( + 'Get started with Pipelines', + ); }); }); diff --git a/spec/javascripts/pipelines/graph/action_component_spec.js b/spec/javascripts/pipelines/graph/action_component_spec.js index 568e679abe9..027066e1d4d 100644 --- a/spec/javascripts/pipelines/graph/action_component_spec.js +++ b/spec/javascripts/pipelines/graph/action_component_spec.js @@ -35,12 +35,13 @@ describe('pipeline graph action component', () => { it('should update bootstrap tooltip when title changes', done => { component.tooltipText = 'changed'; - component.$nextTick() - .then(() => { - expect(component.$el.getAttribute('data-original-title')).toBe('changed'); - }) - .then(done) - .catch(done.fail); + component + .$nextTick() + .then(() => { + expect(component.$el.getAttribute('data-original-title')).toBe('changed'); + }) + .then(done) + .catch(done.fail); }); it('should render an svg', () => { @@ -54,7 +55,8 @@ describe('pipeline graph action component', () => { component.$el.click(); - component.$nextTick() + component + .$nextTick() .then(() => { expect(component.$emit).toHaveBeenCalledWith('pipelineActionRequestComplete'); }) diff --git a/spec/javascripts/pipelines/graph/dropdown_job_component_spec.js b/spec/javascripts/pipelines/graph/job_group_dropdown_spec.js index 2b47ca236b2..24631cc1c89 100644 --- a/spec/javascripts/pipelines/graph/dropdown_job_component_spec.js +++ b/spec/javascripts/pipelines/graph/job_group_dropdown_spec.js @@ -1,12 +1,12 @@ import Vue from 'vue'; -import component from '~/pipelines/components/graph/dropdown_job_component.vue'; +import JobGroupDropdown from '~/pipelines/components/graph/job_group_dropdown.vue'; import mountComponent from 'spec/helpers/vue_mount_component_helper'; -describe('dropdown job component', () => { - const Component = Vue.extend(component); +describe('job group dropdown component', () => { + const Component = Vue.extend(JobGroupDropdown); let vm; - const mock = { + const group = { jobs: [ { id: 4256, @@ -71,15 +71,15 @@ describe('dropdown job component', () => { }); beforeEach(() => { - vm = mountComponent(Component, { job: mock }); + vm = mountComponent(Component, { group }); }); - it('renders button with job name and size', () => { - expect(vm.$el.querySelector('button').textContent).toContain(mock.name); - expect(vm.$el.querySelector('button').textContent).toContain(mock.size); + it('renders button with group name and size', () => { + expect(vm.$el.querySelector('button').textContent).toContain(group.name); + expect(vm.$el.querySelector('button').textContent).toContain(group.size); }); it('renders dropdown with jobs', () => { - expect(vm.$el.querySelectorAll('.scrollable-menu>ul>li').length).toEqual(mock.jobs.length); + expect(vm.$el.querySelectorAll('.scrollable-menu>ul>li').length).toEqual(group.jobs.length); }); }); diff --git a/spec/javascripts/pipelines/graph/job_component_spec.js b/spec/javascripts/pipelines/graph/job_item_spec.js index 0ae448f2ea8..7cbcdc791e7 100644 --- a/spec/javascripts/pipelines/graph/job_component_spec.js +++ b/spec/javascripts/pipelines/graph/job_item_spec.js @@ -1,9 +1,9 @@ import Vue from 'vue'; -import jobComponent from '~/pipelines/components/graph/job_component.vue'; +import JobItem from '~/pipelines/components/graph/job_item.vue'; import mountComponent from 'spec/helpers/vue_mount_component_helper'; -describe('pipeline graph job component', () => { - const JobComponent = Vue.extend(jobComponent); +describe('pipeline graph job item', () => { + const JobComponent = Vue.extend(JobItem); let component; const mockJob = { @@ -31,7 +31,7 @@ describe('pipeline graph job component', () => { }); describe('name with link', () => { - it('should render the job name and status with a link', (done) => { + it('should render the job name and status with a link', done => { component = mountComponent(JobComponent, { job: mockJob }); Vue.nextTick(() => { @@ -39,15 +39,15 @@ describe('pipeline graph job component', () => { expect(link.getAttribute('href')).toEqual(mockJob.status.details_path); - expect( - link.getAttribute('data-original-title'), - ).toEqual(`${mockJob.name} - ${mockJob.status.label}`); + expect(link.getAttribute('data-original-title')).toEqual( + `${mockJob.name} - ${mockJob.status.label}`, + ); expect(component.$el.querySelector('.js-status-icon-success')).toBeDefined(); - expect( - component.$el.querySelector('.ci-status-text').textContent.trim(), - ).toEqual(mockJob.name); + expect(component.$el.querySelector('.ci-status-text').textContent.trim()).toEqual( + mockJob.name, + ); done(); }); @@ -74,9 +74,9 @@ describe('pipeline graph job component', () => { expect(component.$el.querySelector('.js-status-icon-success')).toBeDefined(); expect(component.$el.querySelector('a')).toBeNull(); - expect( - component.$el.querySelector('.ci-status-text').textContent.trim(), - ).toEqual(mockJob.name); + expect(component.$el.querySelector('.ci-status-text').textContent.trim()).toEqual( + mockJob.name, + ); }); }); @@ -95,9 +95,7 @@ describe('pipeline graph job component', () => { cssClassJobName: 'css-class-job-name', }); - expect( - component.$el.querySelector('a').classList.contains('css-class-job-name'), - ).toBe(true); + expect(component.$el.querySelector('a').classList.contains('css-class-job-name')).toBe(true); }); describe('status label', () => { @@ -112,7 +110,11 @@ describe('pipeline graph job component', () => { }, }); - expect(component.$el.querySelector('.js-job-component-tooltip').getAttribute('data-original-title')).toEqual('test'); + expect( + component.$el + .querySelector('.js-job-component-tooltip') + .getAttribute('data-original-title'), + ).toEqual('test'); }); it('should not render status label when it is provided', () => { @@ -128,7 +130,11 @@ describe('pipeline graph job component', () => { }, }); - expect(component.$el.querySelector('.js-job-component-tooltip').getAttribute('data-original-title')).toEqual('test - success'); + expect( + component.$el + .querySelector('.js-job-component-tooltip') + .getAttribute('data-original-title'), + ).toEqual('test - success'); }); }); diff --git a/spec/javascripts/pipelines/graph/stage_column_component_spec.js b/spec/javascripts/pipelines/graph/stage_column_component_spec.js index f6e6bd3132e..d0b8f877d6f 100644 --- a/spec/javascripts/pipelines/graph/stage_column_component_spec.js +++ b/spec/javascripts/pipelines/graph/stage_column_component_spec.js @@ -25,17 +25,16 @@ describe('stage column component', () => { }; beforeEach(() => { - - const mockJobs = []; + const mockGroups = []; for (let i = 0; i < 3; i += 1) { const mockedJob = Object.assign({}, mockJob); mockedJob.id += i; - mockJobs.push(mockedJob); + mockGroups.push(mockedJob); } component = mountComponent(StageColumnComponent, { title: 'foo', - jobs: mockJobs, + groups: mockGroups, }); }); @@ -43,14 +42,14 @@ describe('stage column component', () => { expect(component.$el.querySelector('.stage-name').textContent.trim()).toEqual('foo'); }); - it('should render the provided jobs', () => { + it('should render the provided groups', () => { expect(component.$el.querySelectorAll('.builds-container > ul > li').length).toEqual(3); }); describe('jobId', () => { it('escapes job name', () => { component = mountComponent(StageColumnComponent, { - jobs: [ + groups: [ { id: 4259, name: '<img src=x onerror=alert(document.domain)>', @@ -64,9 +63,9 @@ describe('stage column component', () => { title: 'test', }); - expect( - component.$el.querySelector('.builds-container li').getAttribute('id'), - ).toEqual('ci-badge-<img src=x onerror=alert(document.domain)>'); + expect(component.$el.querySelector('.builds-container li').getAttribute('id')).toEqual( + 'ci-badge-<img src=x onerror=alert(document.domain)>', + ); }); }); }); diff --git a/spec/javascripts/pipelines/header_component_spec.js b/spec/javascripts/pipelines/header_component_spec.js index 034d3b4957d..473a062fc40 100644 --- a/spec/javascripts/pipelines/header_component_spec.js +++ b/spec/javascripts/pipelines/header_component_spec.js @@ -47,13 +47,16 @@ describe('Pipeline details header', () => { it('should render provided pipeline info', () => { expect( - vm.$el.querySelector('.header-main-content').textContent.replace(/\s+/g, ' ').trim(), + vm.$el + .querySelector('.header-main-content') + .textContent.replace(/\s+/g, ' ') + .trim(), ).toEqual('failed Pipeline #123 triggered 3 weeks ago by Foo'); }); describe('action buttons', () => { it('should call postAction when button action is clicked', () => { - eventHub.$on('headerPostAction', (action) => { + eventHub.$on('headerPostAction', action => { expect(action.path).toEqual('path'); }); diff --git a/spec/javascripts/pipelines/nav_controls_spec.js b/spec/javascripts/pipelines/nav_controls_spec.js index d6232f5c567..7806cdf1477 100644 --- a/spec/javascripts/pipelines/nav_controls_spec.js +++ b/spec/javascripts/pipelines/nav_controls_spec.js @@ -24,7 +24,9 @@ describe('Pipelines Nav Controls', () => { component = mountComponent(NavControlsComponent, mockData); expect(component.$el.querySelector('.js-run-pipeline').textContent).toContain('Run Pipeline'); - expect(component.$el.querySelector('.js-run-pipeline').getAttribute('href')).toEqual(mockData.newPipelinePath); + expect(component.$el.querySelector('.js-run-pipeline').getAttribute('href')).toEqual( + mockData.newPipelinePath, + ); }); it('should not render link to create pipeline if no path is provided', () => { @@ -50,7 +52,9 @@ describe('Pipelines Nav Controls', () => { component = mountComponent(NavControlsComponent, mockData); expect(component.$el.querySelector('.js-ci-lint').textContent.trim()).toContain('CI Lint'); - expect(component.$el.querySelector('.js-ci-lint').getAttribute('href')).toEqual(mockData.ciLintPath); + expect(component.$el.querySelector('.js-ci-lint').getAttribute('href')).toEqual( + mockData.ciLintPath, + ); }); describe('Reset Runners Cache', () => { @@ -65,7 +69,9 @@ describe('Pipelines Nav Controls', () => { }); it('should render button for resetting runner caches', () => { - expect(component.$el.querySelector('.js-clear-cache').textContent.trim()).toContain('Clear Runner Caches'); + expect(component.$el.querySelector('.js-clear-cache').textContent.trim()).toContain( + 'Clear Runner Caches', + ); }); it('should emit postAction event when reset runner cache button is clicked', () => { diff --git a/spec/javascripts/pipelines/pipeline_store_spec.js b/spec/javascripts/pipelines/pipeline_store_spec.js index ab2287cc344..1d5754d1f05 100644 --- a/spec/javascripts/pipelines/pipeline_store_spec.js +++ b/spec/javascripts/pipelines/pipeline_store_spec.js @@ -20,6 +20,7 @@ describe('Pipeline Store', () => { it('should store received object', () => { store.storePipeline({ foo: 'bar' }); + expect(store.state.pipeline).toEqual({ foo: 'bar' }); }); }); diff --git a/spec/javascripts/pipelines/pipeline_url_spec.js b/spec/javascripts/pipelines/pipeline_url_spec.js index ddd580ae8b7..c9011b403b7 100644 --- a/spec/javascripts/pipelines/pipeline_url_spec.js +++ b/spec/javascripts/pipelines/pipeline_url_spec.js @@ -38,6 +38,7 @@ describe('Pipeline Url Component', () => { expect(component.$el.querySelector('.js-pipeline-url-link').getAttribute('href')).toEqual( 'foo', ); + expect(component.$el.querySelector('.js-pipeline-url-link span').textContent).toEqual('#1'); }); @@ -66,6 +67,7 @@ describe('Pipeline Url Component', () => { expect(component.$el.querySelector('.js-pipeline-url-user').getAttribute('href')).toEqual( mockData.pipeline.user.web_url, ); + expect(image.getAttribute('data-original-title')).toEqual(mockData.pipeline.user.name); expect(image.getAttribute('src')).toEqual(`${mockData.pipeline.user.avatar_url}?width=20`); }); @@ -105,6 +107,7 @@ describe('Pipeline Url Component', () => { expect(component.$el.querySelector('.js-pipeline-url-yaml').textContent).toContain( 'yaml invalid', ); + expect(component.$el.querySelector('.js-pipeline-url-stuck').textContent).toContain('stuck'); }); diff --git a/spec/javascripts/pipelines/pipelines_actions_spec.js b/spec/javascripts/pipelines/pipelines_actions_spec.js index 0566bc55693..b5c62178642 100644 --- a/spec/javascripts/pipelines/pipelines_actions_spec.js +++ b/spec/javascripts/pipelines/pipelines_actions_spec.js @@ -31,11 +31,13 @@ describe('Pipelines Actions dropdown', () => { it('renders a dropdown with the provided actions', () => { const dropdownItems = vm.$el.querySelectorAll('.dropdown-menu li'); + expect(dropdownItems.length).toEqual(actions.length); }); it("renders a disabled action when it's not playable", () => { const dropdownItem = vm.$el.querySelector('.dropdown-menu li:last-child button'); + expect(dropdownItem).toBeDisabled(); }); }); diff --git a/spec/javascripts/pipelines/pipelines_artifacts_spec.js b/spec/javascripts/pipelines/pipelines_artifacts_spec.js index a8a8e3e2cff..7705d5a19bf 100644 --- a/spec/javascripts/pipelines/pipelines_artifacts_spec.js +++ b/spec/javascripts/pipelines/pipelines_artifacts_spec.js @@ -23,18 +23,16 @@ describe('Pipelines Artifacts dropdown', () => { }); it('should render a dropdown with the provided artifacts', () => { - expect( - component.$el.querySelectorAll('.dropdown-menu li').length, - ).toEqual(artifacts.length); + expect(component.$el.querySelectorAll('.dropdown-menu li').length).toEqual(artifacts.length); }); it('should render a link with the provided path', () => { - expect( - component.$el.querySelector('.dropdown-menu li a').getAttribute('href'), - ).toEqual(artifacts[0].path); + expect(component.$el.querySelector('.dropdown-menu li a').getAttribute('href')).toEqual( + artifacts[0].path, + ); - expect( - component.$el.querySelector('.dropdown-menu li a').textContent, - ).toContain(artifacts[0].name); + expect(component.$el.querySelector('.dropdown-menu li a').textContent).toContain( + artifacts[0].name, + ); }); }); diff --git a/spec/javascripts/pipelines/pipelines_spec.js b/spec/javascripts/pipelines/pipelines_spec.js index 50141bd99b4..37908153e0e 100644 --- a/spec/javascripts/pipelines/pipelines_spec.js +++ b/spec/javascripts/pipelines/pipelines_spec.js @@ -52,7 +52,7 @@ describe('Pipelines', () => { describe('With permission', () => { describe('With pipelines in main tab', () => { - beforeEach((done) => { + beforeEach(done => { mock.onGet('twitter/flight/pipelines.json').reply(200, pipelines); vm = mountComponent(PipelinesComponent, { @@ -72,7 +72,9 @@ describe('Pipelines', () => { }); it('renders Run Pipeline link', () => { - expect(vm.$el.querySelector('.js-run-pipeline').getAttribute('href')).toEqual(paths.newPipelinePath); + expect(vm.$el.querySelector('.js-run-pipeline').getAttribute('href')).toEqual( + paths.newPipelinePath, + ); }); it('renders CI Lint link', () => { @@ -80,18 +82,20 @@ describe('Pipelines', () => { }); it('renders Clear Runner Cache button', () => { - expect(vm.$el.querySelector('.js-clear-cache').textContent.trim()).toEqual('Clear Runner Caches'); + expect(vm.$el.querySelector('.js-clear-cache').textContent.trim()).toEqual( + 'Clear Runner Caches', + ); }); it('renders pipelines table', () => { - expect( - vm.$el.querySelectorAll('.gl-responsive-table-row').length, - ).toEqual(pipelines.pipelines.length + 1); + expect(vm.$el.querySelectorAll('.gl-responsive-table-row').length).toEqual( + pipelines.pipelines.length + 1, + ); }); }); describe('Without pipelines on main tab with CI', () => { - beforeEach((done) => { + beforeEach(done => { mock.onGet('twitter/flight/pipelines.json').reply(200, { pipelines: [], count: { @@ -118,7 +122,9 @@ describe('Pipelines', () => { }); it('renders Run Pipeline link', () => { - expect(vm.$el.querySelector('.js-run-pipeline').getAttribute('href')).toEqual(paths.newPipelinePath); + expect(vm.$el.querySelector('.js-run-pipeline').getAttribute('href')).toEqual( + paths.newPipelinePath, + ); }); it('renders CI Lint link', () => { @@ -126,16 +132,20 @@ describe('Pipelines', () => { }); it('renders Clear Runner Cache button', () => { - expect(vm.$el.querySelector('.js-clear-cache').textContent.trim()).toEqual('Clear Runner Caches'); + expect(vm.$el.querySelector('.js-clear-cache').textContent.trim()).toEqual( + 'Clear Runner Caches', + ); }); it('renders tab empty state', () => { - expect(vm.$el.querySelector('.empty-state h4').textContent.trim()).toEqual('There are currently no pipelines.'); + expect(vm.$el.querySelector('.empty-state h4').textContent.trim()).toEqual( + 'There are currently no pipelines.', + ); }); }); describe('Without pipelines nor CI', () => { - beforeEach((done) => { + beforeEach(done => { mock.onGet('twitter/flight/pipelines.json').reply(200, { pipelines: [], count: { @@ -158,8 +168,13 @@ describe('Pipelines', () => { }); it('renders empty state', () => { - expect(vm.$el.querySelector('.js-empty-state h4').textContent.trim()).toEqual('Build with confidence'); - expect(vm.$el.querySelector('.js-get-started-pipelines').getAttribute('href')).toEqual(paths.helpPagePath); + expect(vm.$el.querySelector('.js-empty-state h4').textContent.trim()).toEqual( + 'Build with confidence', + ); + + expect(vm.$el.querySelector('.js-get-started-pipelines').getAttribute('href')).toEqual( + paths.helpPagePath, + ); }); it('does not render tabs nor buttons', () => { @@ -171,7 +186,7 @@ describe('Pipelines', () => { }); describe('When API returns error', () => { - beforeEach((done) => { + beforeEach(done => { mock.onGet('twitter/flight/pipelines.json').reply(500, {}); vm = mountComponent(PipelinesComponent, { store: new Store(), @@ -190,20 +205,27 @@ describe('Pipelines', () => { }); it('renders buttons', () => { - expect(vm.$el.querySelector('.js-run-pipeline').getAttribute('href')).toEqual(paths.newPipelinePath); + expect(vm.$el.querySelector('.js-run-pipeline').getAttribute('href')).toEqual( + paths.newPipelinePath, + ); + expect(vm.$el.querySelector('.js-ci-lint').getAttribute('href')).toEqual(paths.ciLintPath); - expect(vm.$el.querySelector('.js-clear-cache').textContent.trim()).toEqual('Clear Runner Caches'); + expect(vm.$el.querySelector('.js-clear-cache').textContent.trim()).toEqual( + 'Clear Runner Caches', + ); }); it('renders error state', () => { - expect(vm.$el.querySelector('.empty-state').textContent.trim()).toContain('There was an error fetching the pipelines.'); + expect(vm.$el.querySelector('.empty-state').textContent.trim()).toContain( + 'There was an error fetching the pipelines.', + ); }); }); }); describe('Without permission', () => { describe('With pipelines in main tab', () => { - beforeEach((done) => { + beforeEach(done => { mock.onGet('twitter/flight/pipelines.json').reply(200, pipelines); vm = mountComponent(PipelinesComponent, { @@ -229,14 +251,14 @@ describe('Pipelines', () => { }); it('renders pipelines table', () => { - expect( - vm.$el.querySelectorAll('.gl-responsive-table-row').length, - ).toEqual(pipelines.pipelines.length + 1); + expect(vm.$el.querySelectorAll('.gl-responsive-table-row').length).toEqual( + pipelines.pipelines.length + 1, + ); }); }); describe('Without pipelines on main tab with CI', () => { - beforeEach((done) => { + beforeEach(done => { mock.onGet('twitter/flight/pipelines.json').reply(200, { pipelines: [], count: { @@ -270,12 +292,14 @@ describe('Pipelines', () => { }); it('renders tab empty state', () => { - expect(vm.$el.querySelector('.empty-state h4').textContent.trim()).toEqual('There are currently no pipelines.'); + expect(vm.$el.querySelector('.empty-state h4').textContent.trim()).toEqual( + 'There are currently no pipelines.', + ); }); }); describe('Without pipelines nor CI', () => { - beforeEach((done) => { + beforeEach(done => { mock.onGet('twitter/flight/pipelines.json').reply(200, { pipelines: [], count: { @@ -299,7 +323,10 @@ describe('Pipelines', () => { }); it('renders empty state without button to set CI', () => { - expect(vm.$el.querySelector('.js-empty-state').textContent.trim()).toEqual('This project is not currently set up to run pipelines.'); + expect(vm.$el.querySelector('.js-empty-state').textContent.trim()).toEqual( + 'This project is not currently set up to run pipelines.', + ); + expect(vm.$el.querySelector('.js-get-started-pipelines')).toBeNull(); }); @@ -312,7 +339,7 @@ describe('Pipelines', () => { }); describe('When API returns error', () => { - beforeEach((done) => { + beforeEach(done => { mock.onGet('twitter/flight/pipelines.json').reply(500, {}); vm = mountComponent(PipelinesComponent, { @@ -338,7 +365,9 @@ describe('Pipelines', () => { }); it('renders error state', () => { - expect(vm.$el.querySelector('.empty-state').textContent.trim()).toContain('There was an error fetching the pipelines.'); + expect(vm.$el.querySelector('.empty-state').textContent.trim()).toContain( + 'There was an error fetching the pipelines.', + ); }); }); }); @@ -356,41 +385,44 @@ describe('Pipelines', () => { }); }); - it('should render table', (done) => { + it('should render table', done => { setTimeout(() => { expect(vm.$el.querySelector('.table-holder')).toBeDefined(); - expect( - vm.$el.querySelectorAll('.gl-responsive-table-row').length, - ).toEqual(pipelines.pipelines.length + 1); + expect(vm.$el.querySelectorAll('.gl-responsive-table-row').length).toEqual( + pipelines.pipelines.length + 1, + ); done(); }); }); - it('should render navigation tabs', (done) => { + it('should render navigation tabs', done => { setTimeout(() => { - expect( - vm.$el.querySelector('.js-pipelines-tab-pending').textContent.trim(), - ).toContain('Pending'); - expect( - vm.$el.querySelector('.js-pipelines-tab-all').textContent.trim(), - ).toContain('All'); - expect( - vm.$el.querySelector('.js-pipelines-tab-running').textContent.trim(), - ).toContain('Running'); - expect( - vm.$el.querySelector('.js-pipelines-tab-finished').textContent.trim(), - ).toContain('Finished'); - expect( - vm.$el.querySelector('.js-pipelines-tab-branches').textContent.trim(), - ).toContain('Branches'); - expect( - vm.$el.querySelector('.js-pipelines-tab-tags').textContent.trim(), - ).toContain('Tags'); + expect(vm.$el.querySelector('.js-pipelines-tab-pending').textContent.trim()).toContain( + 'Pending', + ); + + expect(vm.$el.querySelector('.js-pipelines-tab-all').textContent.trim()).toContain('All'); + + expect(vm.$el.querySelector('.js-pipelines-tab-running').textContent.trim()).toContain( + 'Running', + ); + + expect(vm.$el.querySelector('.js-pipelines-tab-finished').textContent.trim()).toContain( + 'Finished', + ); + + expect(vm.$el.querySelector('.js-pipelines-tab-branches').textContent.trim()).toContain( + 'Branches', + ); + + expect(vm.$el.querySelector('.js-pipelines-tab-tags').textContent.trim()).toContain( + 'Tags', + ); done(); }); }); - it('should make an API request when using tabs', (done) => { + it('should make an API request when using tabs', done => { setTimeout(() => { spyOn(vm, 'updateContent'); vm.$el.querySelector('.js-pipelines-tab-finished').click(); @@ -401,7 +433,7 @@ describe('Pipelines', () => { }); describe('with pagination', () => { - it('should make an API request when using pagination', (done) => { + it('should make an API request when using pagination', done => { setTimeout(() => { spyOn(vm, 'updateContent'); // Mock pagination @@ -415,6 +447,7 @@ describe('Pipelines', () => { vm.$nextTick(() => { vm.$el.querySelector('.js-next-button a').click(); + expect(vm.updateContent).toHaveBeenCalledWith({ scope: 'all', page: '2' }); done(); @@ -504,7 +537,7 @@ describe('Pipelines', () => { }); describe('emptyTabMessage', () => { - it('returns message with scope', (done) => { + it('returns message with scope', done => { vm.scope = 'pending'; vm.$nextTick(() => { @@ -523,7 +556,7 @@ describe('Pipelines', () => { expect(vm.stateToRender).toEqual('loading'); }); - it('returns error state when app has error', (done) => { + it('returns error state when app has error', done => { vm.hasError = true; vm.isLoading = false; @@ -533,7 +566,7 @@ describe('Pipelines', () => { }); }); - it('returns table list when app has pipelines', (done) => { + it('returns table list when app has pipelines', done => { vm.isLoading = false; vm.hasError = false; vm.state.pipelines = pipelines.pipelines; @@ -545,7 +578,7 @@ describe('Pipelines', () => { }); }); - it('returns empty tab when app does not have pipelines but project has pipelines', (done) => { + it('returns empty tab when app does not have pipelines but project has pipelines', done => { vm.state.count.all = 10; vm.isLoading = false; @@ -556,7 +589,7 @@ describe('Pipelines', () => { }); }); - it('returns empty tab when project has CI', (done) => { + it('returns empty tab when project has CI', done => { vm.isLoading = false; vm.$nextTick(() => { expect(vm.stateToRender).toEqual('emptyTab'); @@ -565,7 +598,7 @@ describe('Pipelines', () => { }); }); - it('returns empty state when project does not have pipelines nor CI', (done) => { + it('returns empty state when project does not have pipelines nor CI', done => { vm.isLoading = false; vm.hasGitlabCi = false; vm.$nextTick(() => { @@ -577,7 +610,7 @@ describe('Pipelines', () => { }); describe('shouldRenderTabs', () => { - it('returns true when state is loading & has already made the first request', (done) => { + it('returns true when state is loading & has already made the first request', done => { vm.isLoading = true; vm.hasMadeRequest = true; @@ -588,7 +621,7 @@ describe('Pipelines', () => { }); }); - it('returns true when state is tableList & has already made the first request', (done) => { + it('returns true when state is tableList & has already made the first request', done => { vm.isLoading = false; vm.state.pipelines = pipelines.pipelines; vm.hasMadeRequest = true; @@ -600,7 +633,7 @@ describe('Pipelines', () => { }); }); - it('returns true when state is error & has already made the first request', (done) => { + it('returns true when state is error & has already made the first request', done => { vm.isLoading = false; vm.hasError = true; vm.hasMadeRequest = true; @@ -612,7 +645,7 @@ describe('Pipelines', () => { }); }); - it('returns true when state is empty tab & has already made the first request', (done) => { + it('returns true when state is empty tab & has already made the first request', done => { vm.isLoading = false; vm.state.count.all = 10; vm.hasMadeRequest = true; @@ -624,7 +657,7 @@ describe('Pipelines', () => { }); }); - it('returns false when has not made first request', (done) => { + it('returns false when has not made first request', done => { vm.hasMadeRequest = false; vm.$nextTick(() => { @@ -634,7 +667,7 @@ describe('Pipelines', () => { }); }); - it('returns false when state is emtpy state', (done) => { + it('returns false when state is emtpy state', done => { vm.isLoading = false; vm.hasMadeRequest = true; vm.hasGitlabCi = false; @@ -648,7 +681,7 @@ describe('Pipelines', () => { }); describe('shouldRenderButtons', () => { - it('returns true when it has paths & has made the first request', (done) => { + it('returns true when it has paths & has made the first request', done => { vm.hasMadeRequest = true; vm.$nextTick(() => { @@ -658,7 +691,7 @@ describe('Pipelines', () => { }); }); - it('returns false when it has not made the first request', (done) => { + it('returns false when it has not made the first request', done => { vm.hasMadeRequest = false; vm.$nextTick(() => { @@ -675,19 +708,24 @@ describe('Pipelines', () => { const copyPipeline = Object.assign({}, pipelineWithStages); copyPipeline.id += 1; mock - .onGet('twitter/flight/pipelines.json').reply(200, { - pipelines: [pipelineWithStages], - count: { - all: 1, - finished: 1, - pending: 0, - running: 0, + .onGet('twitter/flight/pipelines.json') + .reply( + 200, + { + pipelines: [pipelineWithStages], + count: { + all: 1, + finished: 1, + pending: 0, + running: 0, + }, }, - }, { - 'POLL-INTERVAL': 100, - }) + { + 'POLL-INTERVAL': 100, + }, + ) .onGet(pipelineWithStages.details.stages[0].dropdown_path) - .reply(200, stageReply); + .reply(200, stageReply); vm = mountComponent(PipelinesComponent, { store: new Store(), @@ -698,7 +736,7 @@ describe('Pipelines', () => { }); describe('when a request is being made', () => { - it('stops polling, cancels the request, fetches pipelines & restarts polling', (done) => { + it('stops polling, cancels the request, fetches pipelines & restarts polling', done => { spyOn(vm.poll, 'stop'); spyOn(vm.poll, 'restart'); spyOn(vm, 'getPipelines').and.returnValue(Promise.resolve()); @@ -706,7 +744,8 @@ describe('Pipelines', () => { setTimeout(() => { vm.isMakingRequest = true; - return vm.$nextTick() + return vm + .$nextTick() .then(() => { vm.$el.querySelector('.js-builds-dropdown-button').click(); }) @@ -719,13 +758,14 @@ describe('Pipelines', () => { expect(vm.poll.restart).toHaveBeenCalled(); done(); }, 0); - }); + }) + .catch(done.fail); }, 0); }); }); describe('when no request is being made', () => { - it('stops polling, fetches pipelines & restarts polling', (done) => { + it('stops polling, fetches pipelines & restarts polling', done => { spyOn(vm.poll, 'stop'); spyOn(vm.poll, 'restart'); spyOn(vm, 'getPipelines').and.returnValue(Promise.resolve()); diff --git a/spec/javascripts/pipelines/pipelines_store_spec.js b/spec/javascripts/pipelines/pipelines_store_spec.js index 10ff0c6bb84..ce21f788ed5 100644 --- a/spec/javascripts/pipelines/pipelines_store_spec.js +++ b/spec/javascripts/pipelines/pipelines_store_spec.js @@ -16,12 +16,14 @@ describe('Pipelines Store', () => { describe('storePipelines', () => { it('should use the default parameter if none is provided', () => { store.storePipelines(); + expect(store.state.pipelines).toEqual([]); }); it('should store the provided array', () => { const array = [{ id: 1, status: 'running' }, { id: 2, status: 'success' }]; store.storePipelines(array); + expect(store.state.pipelines).toEqual(array); }); }); @@ -29,6 +31,7 @@ describe('Pipelines Store', () => { describe('storeCount', () => { it('should use the default parameter if none is provided', () => { store.storeCount(); + expect(store.state.count).toEqual({}); }); @@ -43,6 +46,7 @@ describe('Pipelines Store', () => { describe('storePagination', () => { it('should use the default parameter if none is provided', () => { store.storePagination(); + expect(store.state.pageInfo).toEqual({}); }); @@ -66,6 +70,7 @@ describe('Pipelines Store', () => { }; store.storePagination(pagination); + expect(store.state.pageInfo).toEqual(expectedResult); }); }); diff --git a/spec/javascripts/pipelines/pipelines_table_row_spec.js b/spec/javascripts/pipelines/pipelines_table_row_spec.js index 42795f5c134..506d01f5ec1 100644 --- a/spec/javascripts/pipelines/pipelines_table_row_spec.js +++ b/spec/javascripts/pipelines/pipelines_table_row_spec.js @@ -37,6 +37,7 @@ describe('Pipelines Table Row', () => { it('should render a table row', () => { component = buildComponent(pipeline); + expect(component.$el.getAttribute('class')).toContain('gl-responsive-table-row'); }); @@ -97,6 +98,7 @@ describe('Pipelines Table Row', () => { component = buildComponent(pipeline); const commitLink = component.$el.querySelector('.branch-commit .commit-sha'); + expect(commitLink.getAttribute('href')).toEqual(pipeline.commit.commit_path); }); @@ -177,6 +179,7 @@ describe('Pipelines Table Row', () => { expect(component.$el.querySelector('.js-pipelines-retry-button')).not.toBeNull(); expect(component.$el.querySelector('.js-pipelines-cancel-button')).not.toBeNull(); const dropdownMenu = component.$el.querySelectorAll('.dropdown-menu'); + expect(dropdownMenu).toContainText(scheduledJobAction.name); }); @@ -186,6 +189,7 @@ describe('Pipelines Table Row', () => { }); component.$el.querySelector('.js-pipelines-retry-button').click(); + expect(component.isRetrying).toEqual(true); }); @@ -200,7 +204,8 @@ describe('Pipelines Table Row', () => { it('renders a loading icon when `cancelingPipeline` matches pipeline id', done => { component.cancelingPipeline = pipeline.id; - component.$nextTick() + component + .$nextTick() .then(() => { expect(component.isCancelling).toEqual(true); }) diff --git a/spec/javascripts/pipelines/pipelines_table_spec.js b/spec/javascripts/pipelines/pipelines_table_spec.js index d21ba35e96d..5c3387190ab 100644 --- a/spec/javascripts/pipelines/pipelines_table_spec.js +++ b/spec/javascripts/pipelines/pipelines_table_spec.js @@ -38,10 +38,21 @@ describe('Pipelines Table', () => { }); it('should render table head with correct columns', () => { - expect(component.$el.querySelector('.table-section.js-pipeline-status').textContent.trim()).toEqual('Status'); - expect(component.$el.querySelector('.table-section.js-pipeline-info').textContent.trim()).toEqual('Pipeline'); - expect(component.$el.querySelector('.table-section.js-pipeline-commit').textContent.trim()).toEqual('Commit'); - expect(component.$el.querySelector('.table-section.js-pipeline-stages').textContent.trim()).toEqual('Stages'); + expect( + component.$el.querySelector('.table-section.js-pipeline-status').textContent.trim(), + ).toEqual('Status'); + + expect( + component.$el.querySelector('.table-section.js-pipeline-info').textContent.trim(), + ).toEqual('Pipeline'); + + expect( + component.$el.querySelector('.table-section.js-pipeline-commit').textContent.trim(), + ).toEqual('Commit'); + + expect( + component.$el.querySelector('.table-section.js-pipeline-stages').textContent.trim(), + ).toEqual('Stages'); }); }); @@ -54,6 +65,7 @@ describe('Pipelines Table', () => { viewType: 'root', }, }).$mount(); + expect(component.$el.querySelectorAll('.commit.gl-responsive-table-row').length).toEqual(0); }); }); diff --git a/spec/javascripts/pipelines/stage_spec.js b/spec/javascripts/pipelines/stage_spec.js index 3f6789759ae..a3caaeb44dc 100644 --- a/spec/javascripts/pipelines/stage_spec.js +++ b/spec/javascripts/pipelines/stage_spec.js @@ -53,6 +53,7 @@ describe('Pipelines stage component', () => { expect( component.$el.querySelector('.js-builds-dropdown-container ul').textContent.trim(), ).toContain(stageReply.latest_statuses[0].name); + expect(eventHub.$emit).toHaveBeenCalledWith('clickedDropdown'); done(); }, 0); @@ -119,12 +120,13 @@ describe('Pipelines stage component', () => { setTimeout(() => { component.$el.querySelector('.js-ci-action').click(); - component.$nextTick() - .then(() => { - expect(eventHub.$emit).toHaveBeenCalledWith('refreshPipelinesTable'); - }) - .then(done) - .catch(done.fail); + component + .$nextTick() + .then(() => { + expect(eventHub.$emit).toHaveBeenCalledWith('refreshPipelinesTable'); + }) + .then(done) + .catch(done.fail); }, 0); }); }); diff --git a/spec/javascripts/pipelines_spec.js b/spec/javascripts/pipelines_spec.js index c08a73851be..6b86f9ea437 100644 --- a/spec/javascripts/pipelines_spec.js +++ b/spec/javascripts/pipelines_spec.js @@ -12,6 +12,8 @@ describe('Pipelines', () => { }); it('should create a `Pipelines` instance without options', () => { - expect(() => { new Pipelines(); }).not.toThrow(); //eslint-disable-line + expect(() => { + new Pipelines(); // eslint-disable-line no-new + }).not.toThrow(); }); }); diff --git a/spec/javascripts/polyfills/element_spec.js b/spec/javascripts/polyfills/element_spec.js index ecaaf1907ea..d35df595c72 100644 --- a/spec/javascripts/polyfills/element_spec.js +++ b/spec/javascripts/polyfills/element_spec.js @@ -1,6 +1,6 @@ import '~/commons/polyfills/element'; -describe('Element polyfills', function () { +describe('Element polyfills', function() { beforeEach(() => { this.element = document.createElement('ul'); }); diff --git a/spec/javascripts/pretty_time_spec.js b/spec/javascripts/pretty_time_spec.js index 084ffe08917..158cd76dd13 100644 --- a/spec/javascripts/pretty_time_spec.js +++ b/spec/javascripts/pretty_time_spec.js @@ -122,11 +122,13 @@ describe('prettyTime methods', () => { describe('abbreviateTime', () => { it('should abbreviate stringified times for weeks', () => { const fullTimeString = '1w 3d 4h 5m'; + expect(abbreviateTime(fullTimeString)).toBe('1w'); }); it('should abbreviate stringified times for non-weeks', () => { const fullTimeString = '0w 3d 4h 5m'; + expect(abbreviateTime(fullTimeString)).toBe('3d'); }); }); diff --git a/spec/javascripts/profile/account/components/delete_account_modal_spec.js b/spec/javascripts/profile/account/components/delete_account_modal_spec.js index a0939ff5c20..d5f5cabc63e 100644 --- a/spec/javascripts/profile/account/components/delete_account_modal_spec.js +++ b/spec/javascripts/profile/account/components/delete_account_modal_spec.js @@ -28,7 +28,7 @@ describe('DeleteAccountModal component', () => { }; describe('with password confirmation', () => { - beforeEach((done) => { + beforeEach(done => { vm = mountComponent(Component, { actionUrl, confirmWithPassword: true, @@ -42,7 +42,7 @@ describe('DeleteAccountModal component', () => { .catch(done.fail); }); - it('does not accept empty password', (done) => { + it('does not accept empty password', done => { const { form, input, submitButton } = findElements(); spyOn(form, 'submit'); input.value = ''; @@ -53,13 +53,14 @@ describe('DeleteAccountModal component', () => { expect(vm.enteredPassword).toBe(input.value); expect(submitButton).toHaveAttr('disabled', 'disabled'); submitButton.click(); + expect(form.submit).not.toHaveBeenCalled(); }) .then(done) .catch(done.fail); }); - it('submits form with password', (done) => { + it('submits form with password', done => { const { form, input, submitButton } = findElements(); spyOn(form, 'submit'); input.value = 'anything'; @@ -70,6 +71,7 @@ describe('DeleteAccountModal component', () => { expect(vm.enteredPassword).toBe(input.value); expect(submitButton).not.toHaveAttr('disabled', 'disabled'); submitButton.click(); + expect(form.submit).toHaveBeenCalled(); }) .then(done) @@ -78,7 +80,7 @@ describe('DeleteAccountModal component', () => { }); describe('with username confirmation', () => { - beforeEach((done) => { + beforeEach(done => { vm = mountComponent(Component, { actionUrl, confirmWithPassword: false, @@ -92,7 +94,7 @@ describe('DeleteAccountModal component', () => { .catch(done.fail); }); - it('does not accept wrong username', (done) => { + it('does not accept wrong username', done => { const { form, input, submitButton } = findElements(); spyOn(form, 'submit'); input.value = 'this is wrong'; @@ -103,13 +105,14 @@ describe('DeleteAccountModal component', () => { expect(vm.enteredUsername).toBe(input.value); expect(submitButton).toHaveAttr('disabled', 'disabled'); submitButton.click(); + expect(form.submit).not.toHaveBeenCalled(); }) .then(done) .catch(done.fail); }); - it('submits form with correct username', (done) => { + it('submits form with correct username', done => { const { form, input, submitButton } = findElements(); spyOn(form, 'submit'); input.value = username; @@ -120,6 +123,7 @@ describe('DeleteAccountModal component', () => { expect(vm.enteredUsername).toBe(input.value); expect(submitButton).not.toHaveAttr('disabled', 'disabled'); submitButton.click(); + expect(form.submit).toHaveBeenCalled(); }) .then(done) diff --git a/spec/javascripts/profile/account/components/update_username_spec.js b/spec/javascripts/profile/account/components/update_username_spec.js index 5311499fb73..cc07a5f6e43 100644 --- a/spec/javascripts/profile/account/components/update_username_spec.js +++ b/spec/javascripts/profile/account/components/update_username_spec.js @@ -113,6 +113,7 @@ describe('UpdateUsername component', () => { Vue.nextTick() .then(() => { confirmModalBtn.click(); + expect(axios.put).toHaveBeenCalledWith(actionUrl, { user: { username: newUsername } }); }) .then(done) @@ -131,8 +132,7 @@ describe('UpdateUsername component', () => { vm.newUsername = newUsername; - vm - .onConfirm() + vm.onConfirm() .then(() => { expect(vm.username).toBe(newUsername); expect(vm.newUsername).toBe(newUsername); @@ -157,8 +157,7 @@ describe('UpdateUsername component', () => { const invalidUsername = 'anything.git'; vm.newUsername = invalidUsername; - vm - .onConfirm() + vm.onConfirm() .then(() => done.fail('Expected onConfirm to throw!')) .catch(() => { expect(vm.username).toBe(username); diff --git a/spec/javascripts/project_select_combo_button_spec.js b/spec/javascripts/project_select_combo_button_spec.js index 1b65f767f96..109a5000f5d 100644 --- a/spec/javascripts/project_select_combo_button_spec.js +++ b/spec/javascripts/project_select_combo_button_spec.js @@ -3,10 +3,10 @@ import ProjectSelectComboButton from '~/project_select_combo_button'; const fixturePath = 'static/project_select_combo_button.html.raw'; -describe('Project Select Combo Button', function () { +describe('Project Select Combo Button', function() { preloadFixtures(fixturePath); - beforeEach(function () { + beforeEach(function() { this.defaults = { label: 'Select project to create issue', groupId: 12345, @@ -28,43 +28,44 @@ describe('Project Select Combo Button', function () { this.projectSelectInput = document.querySelector('.project-item-select'); }); - describe('on page load when localStorage is empty', function () { - beforeEach(function () { + describe('on page load when localStorage is empty', function() { + beforeEach(function() { this.comboButton = new ProjectSelectComboButton(this.projectSelectInput); }); - it('newItemBtn href is null', function () { + it('newItemBtn href is null', function() { expect(this.newItemBtn.getAttribute('href')).toBe(''); }); - it('newItemBtn text is the plain default label', function () { + it('newItemBtn text is the plain default label', function() { expect(this.newItemBtn.textContent).toBe(this.defaults.label); }); }); - describe('on page load when localStorage is filled', function () { - beforeEach(function () { - window.localStorage - .setItem(this.defaults.localStorageKey, JSON.stringify(this.defaults.projectMeta)); + describe('on page load when localStorage is filled', function() { + beforeEach(function() { + window.localStorage.setItem( + this.defaults.localStorageKey, + JSON.stringify(this.defaults.projectMeta), + ); this.comboButton = new ProjectSelectComboButton(this.projectSelectInput); }); - it('newItemBtn href is correctly set', function () { + it('newItemBtn href is correctly set', function() { expect(this.newItemBtn.getAttribute('href')).toBe(this.defaults.projectMeta.url); }); - it('newItemBtn text is the cached label', function () { - expect(this.newItemBtn.textContent) - .toBe(`New issue in ${this.defaults.projectMeta.name}`); + it('newItemBtn text is the cached label', function() { + expect(this.newItemBtn.textContent).toBe(`New issue in ${this.defaults.projectMeta.name}`); }); - afterEach(function () { + afterEach(function() { window.localStorage.clear(); }); }); - describe('after selecting a new project', function () { - beforeEach(function () { + describe('after selecting a new project', function() { + beforeEach(function() { this.comboButton = new ProjectSelectComboButton(this.projectSelectInput); // mock the effect of selecting an item from the projects dropdown (select2) @@ -73,23 +74,21 @@ describe('Project Select Combo Button', function () { .trigger('change'); }); - it('newItemBtn href is correctly set', function () { - expect(this.newItemBtn.getAttribute('href')) - .toBe('http://myothercoolproject.com/issues/new'); + it('newItemBtn href is correctly set', function() { + expect(this.newItemBtn.getAttribute('href')).toBe('http://myothercoolproject.com/issues/new'); }); - it('newItemBtn text is the selected project label', function () { - expect(this.newItemBtn.textContent) - .toBe(`New issue in ${this.defaults.newProjectMeta.name}`); + it('newItemBtn text is the selected project label', function() { + expect(this.newItemBtn.textContent).toBe(`New issue in ${this.defaults.newProjectMeta.name}`); }); - afterEach(function () { + afterEach(function() { window.localStorage.clear(); }); }); - describe('deriveTextVariants', function () { - beforeEach(function () { + describe('deriveTextVariants', function() { + beforeEach(function() { this.mockExecutionContext = { resourceType: '', resourceLabel: '', @@ -100,7 +99,7 @@ describe('Project Select Combo Button', function () { this.method = this.comboButton.deriveTextVariants.bind(this.mockExecutionContext); }); - it('correctly derives test variants for merge requests', function () { + it('correctly derives test variants for merge requests', function() { this.mockExecutionContext.resourceType = 'merge_requests'; this.mockExecutionContext.resourceLabel = 'New merge request'; @@ -111,7 +110,7 @@ describe('Project Select Combo Button', function () { expect(returnedVariants.presetTextSuffix).toBe('merge request'); }); - it('correctly derives text variants for issues', function () { + it('correctly derives text variants for issues', function() { this.mockExecutionContext.resourceType = 'issues'; this.mockExecutionContext.resourceLabel = 'New issue'; @@ -123,4 +122,3 @@ describe('Project Select Combo Button', function () { }); }); }); - diff --git a/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown_spec.js b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown_spec.js index 21805ef0b28..fdecb823cd2 100644 --- a/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown_spec.js +++ b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown_spec.js @@ -90,14 +90,20 @@ describe('GkeMachineTypeDropdown', () => { expect(vm.$el.querySelector('input').value).toBe(''); vm.$store.commit(SET_MACHINE_TYPES, gapiMachineTypesResponseMock.items); - return vm.$nextTick().then(() => { - vm.$el.querySelector('.dropdown-content button').click(); - - return vm.$nextTick().then(() => { - expect(vm.$el.querySelector('input').value).toBe(selectedMachineTypeMock); - done(); - }); - }); + return vm + .$nextTick() + .then(() => { + vm.$el.querySelector('.dropdown-content button').click(); + + return vm + .$nextTick() + .then(() => { + expect(vm.$el.querySelector('input').value).toBe(selectedMachineTypeMock); + done(); + }) + .catch(done.fail); + }) + .catch(done.fail); }); }); }); diff --git a/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown_spec.js b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown_spec.js index d4fcb2dc8ff..030662b4d90 100644 --- a/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown_spec.js +++ b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown_spec.js @@ -48,45 +48,61 @@ describe('GkeProjectIdDropdown', () => { it('returns project billing validation text', () => { vm.setIsValidatingProjectBilling(true); + expect(vm.toggleText).toBe(LABELS.VALIDATING_PROJECT_BILLING); }); it('returns default toggle text', done => - vm.$nextTick().then(() => { - vm.setItem(emptyProjectMock); + vm + .$nextTick() + .then(() => { + vm.setItem(emptyProjectMock); - expect(vm.toggleText).toBe(LABELS.DEFAULT); - done(); - })); + expect(vm.toggleText).toBe(LABELS.DEFAULT); + done(); + }) + .catch(done.fail)); it('returns project name if project selected', done => - vm.$nextTick().then(() => { - expect(vm.toggleText).toBe(selectedProjectMock.name); - done(); - })); + vm + .$nextTick() + .then(() => { + expect(vm.toggleText).toBe(selectedProjectMock.name); + done(); + }) + .catch(done.fail)); it('returns empty toggle text', done => - vm.$nextTick().then(() => { - vm.$store.commit(SET_PROJECTS, null); - vm.setItem(emptyProjectMock); + vm + .$nextTick() + .then(() => { + vm.$store.commit(SET_PROJECTS, null); + vm.setItem(emptyProjectMock); - expect(vm.toggleText).toBe(LABELS.EMPTY); - done(); - })); + expect(vm.toggleText).toBe(LABELS.EMPTY); + done(); + }) + .catch(done.fail)); }); describe('selectItem', () => { it('reflects new value when dropdown item is clicked', done => { expect(vm.$el.querySelector('input').value).toBe(''); - return vm.$nextTick().then(() => { - vm.$el.querySelector('.dropdown-content button').click(); - - return vm.$nextTick().then(() => { - expect(vm.$el.querySelector('input').value).toBe(selectedProjectMock.projectId); - done(); - }); - }); + return vm + .$nextTick() + .then(() => { + vm.$el.querySelector('.dropdown-content button').click(); + + return vm + .$nextTick() + .then(() => { + expect(vm.$el.querySelector('input').value).toBe(selectedProjectMock.projectId); + done(); + }) + .catch(done.fail); + }) + .catch(done.fail); }); }); }); diff --git a/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown_spec.js b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown_spec.js index 89a4a7ea2ce..95186e19ca1 100644 --- a/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown_spec.js +++ b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown_spec.js @@ -75,14 +75,20 @@ describe('GkeZoneDropdown', () => { expect(vm.$el.querySelector('input').value).toBe(''); vm.$store.commit(SET_ZONES, gapiZonesResponseMock.items); - return vm.$nextTick().then(() => { - vm.$el.querySelector('.dropdown-content button').click(); - - return vm.$nextTick().then(() => { - expect(vm.$el.querySelector('input').value).toBe(selectedZoneMock); - done(); - }); - }); + return vm + .$nextTick() + .then(() => { + vm.$el.querySelector('.dropdown-content button').click(); + + return vm + .$nextTick() + .then(() => { + expect(vm.$el.querySelector('input').value).toBe(selectedZoneMock); + done(); + }) + .catch(done.fail); + }) + .catch(done.fail); }); }); }); diff --git a/spec/javascripts/projects/project_new_spec.js b/spec/javascripts/projects/project_new_spec.js index dace834a3c8..b61e0ac872f 100644 --- a/spec/javascripts/projects/project_new_spec.js +++ b/spec/javascripts/projects/project_new_spec.js @@ -27,7 +27,10 @@ describe('New Project', () => { beforeEach(() => { projectNew.bindEvents(); - $projectPath.val('').keyup().val(dummyImportUrl); + $projectPath + .val('') + .keyup() + .val(dummyImportUrl); }); it('does not change project path for disabled $projectImportUrl', () => { diff --git a/spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js b/spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js index 955ec6a531c..94e2f959d46 100644 --- a/spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js +++ b/spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js @@ -85,9 +85,17 @@ describe('PrometheusMetrics', () => { expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBeTruthy(); expect(prometheusMetrics.$monitoredMetricsList.hasClass('hidden')).toBeFalsy(); - expect(prometheusMetrics.$monitoredMetricsCount.text()).toEqual('3 exporters with 12 metrics were found'); + expect(prometheusMetrics.$monitoredMetricsCount.text()).toEqual( + '3 exporters with 12 metrics were found', + ); + expect($metricsListLi.length).toEqual(metrics.length); - expect($metricsListLi.first().find('.badge').text()).toEqual(`${metrics[0].active_metrics}`); + expect( + $metricsListLi + .first() + .find('.badge') + .text(), + ).toEqual(`${metrics[0].active_metrics}`); }); it('should show missing environment variables list', () => { @@ -129,7 +137,7 @@ describe('PrometheusMetrics', () => { mock.restore(); }); - it('should show loader animation while response is being loaded and hide it when request is complete', (done) => { + it('should show loader animation while response is being loaded and hide it when request is complete', done => { mockSuccess(); prometheusMetrics.loadActiveMetrics(); @@ -143,7 +151,7 @@ describe('PrometheusMetrics', () => { }); }); - it('should show empty state if response failed to load', (done) => { + it('should show empty state if response failed to load', done => { mockError(); prometheusMetrics.loadActiveMetrics(); @@ -155,7 +163,7 @@ describe('PrometheusMetrics', () => { }); }); - it('should populate metrics list once response is loaded', (done) => { + it('should populate metrics list once response is loaded', done => { spyOn(prometheusMetrics, 'populateActiveMetrics'); mockSuccess(); diff --git a/spec/javascripts/raven/raven_config_spec.js b/spec/javascripts/raven/raven_config_spec.js index c82658b9262..5cc59cc28d3 100644 --- a/spec/javascripts/raven/raven_config_spec.js +++ b/spec/javascripts/raven/raven_config_spec.js @@ -133,7 +133,7 @@ describe('RavenConfig', () => { RavenConfig.setUser.call(ravenConfig); }); - it('should call .setUserContext', function () { + it('should call .setUserContext', function() { expect(Raven.setUserContext).toHaveBeenCalledWith({ id: ravenConfig.options.currentUserId, }); diff --git a/spec/javascripts/registry/components/app_spec.js b/spec/javascripts/registry/components/app_spec.js index cf1d0625397..92ff960277a 100644 --- a/spec/javascripts/registry/components/app_spec.js +++ b/spec/javascripts/registry/components/app_spec.js @@ -18,9 +18,11 @@ describe('Registry List', () => { describe('with data', () => { const interceptor = (request, next) => { - next(request.respondWith(JSON.stringify(reposServerResponse), { - status: 200, - })); + next( + request.respondWith(JSON.stringify(reposServerResponse), { + status: 200, + }), + ); }; beforeEach(() => { @@ -32,21 +34,21 @@ describe('Registry List', () => { Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor); }); - it('should render a list of repos', (done) => { + it('should render a list of repos', done => { setTimeout(() => { expect(vm.$store.state.repos.length).toEqual(reposServerResponse.length); Vue.nextTick(() => { - expect( - vm.$el.querySelectorAll('.container-image').length, - ).toEqual(reposServerResponse.length); + expect(vm.$el.querySelectorAll('.container-image').length).toEqual( + reposServerResponse.length, + ); done(); }); }, 0); }); describe('delete repository', () => { - it('should be possible to delete a repo', (done) => { + it('should be possible to delete a repo', done => { setTimeout(() => { Vue.nextTick(() => { expect(vm.$el.querySelector('.container-image-head .js-remove-repo')).toBeDefined(); @@ -57,12 +59,14 @@ describe('Registry List', () => { }); describe('toggle repository', () => { - it('should open the container', (done) => { + it('should open the container', done => { setTimeout(() => { Vue.nextTick(() => { vm.$el.querySelector('.js-toggle-repo').click(); Vue.nextTick(() => { - expect(vm.$el.querySelector('.js-toggle-repo i').className).toEqual('fa fa-chevron-up'); + expect(vm.$el.querySelector('.js-toggle-repo i').className).toEqual( + 'fa fa-chevron-up', + ); done(); }); }); @@ -73,9 +77,11 @@ describe('Registry List', () => { describe('without data', () => { const interceptor = (request, next) => { - next(request.respondWith(JSON.stringify([]), { - status: 200, - })); + next( + request.respondWith(JSON.stringify([]), { + status: 200, + }), + ); }; beforeEach(() => { @@ -87,11 +93,16 @@ describe('Registry List', () => { Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor); }); - it('should render empty message', (done) => { + it('should render empty message', done => { setTimeout(() => { expect( - vm.$el.querySelector('p').textContent.trim().replace(/[\r\n]+/g, ' '), - ).toEqual('No container images stored for this project. Add one by following the instructions above.'); + vm.$el + .querySelector('p') + .textContent.trim() + .replace(/[\r\n]+/g, ' '), + ).toEqual( + 'No container images stored for this project. Add one by following the instructions above.', + ); done(); }, 0); }); @@ -99,9 +110,11 @@ describe('Registry List', () => { describe('while loading data', () => { const interceptor = (request, next) => { - next(request.respondWith(JSON.stringify(reposServerResponse), { - status: 200, - })); + next( + request.respondWith(JSON.stringify(reposServerResponse), { + status: 200, + }), + ); }; beforeEach(() => { @@ -113,7 +126,7 @@ describe('Registry List', () => { Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor); }); - it('should render a loading spinner', (done) => { + it('should render a loading spinner', done => { Vue.nextTick(() => { expect(vm.$el.querySelector('.fa-spinner')).not.toBe(null); done(); diff --git a/spec/javascripts/registry/components/collapsible_container_spec.js b/spec/javascripts/registry/components/collapsible_container_spec.js index 5891921318a..256a242f784 100644 --- a/spec/javascripts/registry/components/collapsible_container_spec.js +++ b/spec/javascripts/registry/components/collapsible_container_spec.js @@ -24,26 +24,32 @@ describe('collapsible registry container', () => { describe('toggle', () => { it('should be closed by default', () => { expect(vm.$el.querySelector('.container-image-tags')).toBe(null); - expect(vm.$el.querySelector('.container-image-head i').className).toEqual('fa fa-chevron-right'); + expect(vm.$el.querySelector('.container-image-head i').className).toEqual( + 'fa fa-chevron-right', + ); }); - it('should be open when user clicks on closed repo', (done) => { + it('should be open when user clicks on closed repo', done => { vm.$el.querySelector('.js-toggle-repo').click(); Vue.nextTick(() => { expect(vm.$el.querySelector('.container-image-tags')).toBeDefined(); - expect(vm.$el.querySelector('.container-image-head i').className).toEqual('fa fa-chevron-up'); + expect(vm.$el.querySelector('.container-image-head i').className).toEqual( + 'fa fa-chevron-up', + ); done(); }); }); - it('should be closed when the user clicks on an opened repo', (done) => { + it('should be closed when the user clicks on an opened repo', done => { vm.$el.querySelector('.js-toggle-repo').click(); Vue.nextTick(() => { vm.$el.querySelector('.js-toggle-repo').click(); Vue.nextTick(() => { expect(vm.$el.querySelector('.container-image-tags')).toBe(null); - expect(vm.$el.querySelector('.container-image-head i').className).toEqual('fa fa-chevron-right'); + expect(vm.$el.querySelector('.container-image-head i').className).toEqual( + 'fa fa-chevron-right', + ); done(); }); }); diff --git a/spec/javascripts/registry/components/table_registry_spec.js b/spec/javascripts/registry/components/table_registry_spec.js index 6aa61afc445..7f5252a7d6c 100644 --- a/spec/javascripts/registry/components/table_registry_spec.js +++ b/spec/javascripts/registry/components/table_registry_spec.js @@ -22,13 +22,15 @@ describe('table registry', () => { }); it('should render a table with the registry list', () => { - expect( - vm.$el.querySelectorAll('table tbody tr').length, - ).toEqual(repoPropsData.list.length); + expect(vm.$el.querySelectorAll('table tbody tr').length).toEqual(repoPropsData.list.length); }); it('should render registry tag', () => { - const textRendered = vm.$el.querySelector('.table tbody tr').textContent.trim().replace(/\s\s+/g, ' '); + const textRendered = vm.$el + .querySelector('.table tbody tr') + .textContent.trim() + .replace(/\s\s+/g, ' '); + expect(textRendered).toContain(repoPropsData.list[0].tag); expect(textRendered).toContain(repoPropsData.list[0].shortRevision); expect(textRendered).toContain(repoPropsData.list[0].layers); @@ -36,9 +38,7 @@ describe('table registry', () => { }); it('should be possible to delete a registry', () => { - expect( - vm.$el.querySelector('.table tbody tr .js-delete-registry'), - ).toBeDefined(); + expect(vm.$el.querySelector('.table tbody tr .js-delete-registry')).toBeDefined(); }); describe('pagination', () => { diff --git a/spec/javascripts/registry/getters_spec.js b/spec/javascripts/registry/getters_spec.js index 3d989541881..839aa718997 100644 --- a/spec/javascripts/registry/getters_spec.js +++ b/spec/javascripts/registry/getters_spec.js @@ -7,25 +7,28 @@ describe('Getters Registry Store', () => { state = { isLoading: false, endpoint: '/root/empty-project/container_registry.json', - repos: [{ - canDelete: true, - destroyPath: 'bar', - id: '134', - isLoading: false, - list: [], - location: 'foo', - name: 'gitlab-org/omnibus-gitlab/foo', - tagsPath: 'foo', - }, { - canDelete: true, - destroyPath: 'bar', - id: '123', - isLoading: false, - list: [], - location: 'foo', - name: 'gitlab-org/omnibus-gitlab', - tagsPath: 'foo', - }], + repos: [ + { + canDelete: true, + destroyPath: 'bar', + id: '134', + isLoading: false, + list: [], + location: 'foo', + name: 'gitlab-org/omnibus-gitlab/foo', + tagsPath: 'foo', + }, + { + canDelete: true, + destroyPath: 'bar', + id: '123', + isLoading: false, + list: [], + location: 'foo', + name: 'gitlab-org/omnibus-gitlab', + tagsPath: 'foo', + }, + ], }; }); diff --git a/spec/javascripts/registry/mock_data.js b/spec/javascripts/registry/mock_data.js index 6bffb47be55..22db203e77f 100644 --- a/spec/javascripts/registry/mock_data.js +++ b/spec/javascripts/registry/mock_data.js @@ -40,7 +40,8 @@ export const registryServerResponse = [ layers: 19, location: 'location', created_at: 1505828744434, - }]; + }, +]; export const parsedReposServerResponse = [ { diff --git a/spec/javascripts/registry/stores/mutations_spec.js b/spec/javascripts/registry/stores/mutations_spec.js index 2e4c0659daa..e19fe7a27cf 100644 --- a/spec/javascripts/registry/stores/mutations_spec.js +++ b/spec/javascripts/registry/stores/mutations_spec.js @@ -18,6 +18,7 @@ describe('Mutations Registry Store', () => { it('should set the main endpoint', () => { const expectedState = Object.assign({}, mockState, { endpoint: 'foo' }); mutations[types.SET_MAIN_ENDPOINT](mockState, 'foo'); + expect(mockState).toEqual(expectedState); }); }); @@ -25,6 +26,7 @@ describe('Mutations Registry Store', () => { describe('SET_REPOS_LIST', () => { it('should set a parsed repository list', () => { mutations[types.SET_REPOS_LIST](mockState, reposServerResponse); + expect(mockState.repos).toEqual(parsedReposServerResponse); }); }); @@ -32,6 +34,7 @@ describe('Mutations Registry Store', () => { describe('TOGGLE_MAIN_LOADING', () => { it('should set a parsed repository list', () => { mutations[types.TOGGLE_MAIN_LOADING](mockState); + expect(mockState.isLoading).toEqual(true); }); }); @@ -75,6 +78,7 @@ describe('Mutations Registry Store', () => { }); mutations[types.TOGGLE_REGISTRY_LIST_LOADING](mockState, mockState.repos[0]); + expect(mockState.repos[0].isLoading).toEqual(true); }); }); diff --git a/spec/javascripts/reports/components/grouped_test_reports_app_spec.js b/spec/javascripts/reports/components/grouped_test_reports_app_spec.js index 333cefe5f8a..f58515daa4f 100644 --- a/spec/javascripts/reports/components/grouped_test_reports_app_spec.js +++ b/spec/javascripts/reports/components/grouped_test_reports_app_spec.js @@ -42,6 +42,7 @@ describe('Grouped Test Reports App', () => { expect(vm.$el.textContent).toContain( 'rspec:pg found no changed test results out of 8 total tests', ); + expect(vm.$el.textContent).toContain( 'java ant found no changed test results out of 3 total tests', ); @@ -88,6 +89,7 @@ describe('Grouped Test Reports App', () => { expect(vm.$el.textContent).toContain( 'rspec:pg found 2 failed test results out of 8 total tests', ); + expect(vm.$el.textContent).toContain('New'); expect(vm.$el.textContent).toContain( 'java ant found no changed test results out of 3 total tests', @@ -115,6 +117,7 @@ describe('Grouped Test Reports App', () => { expect(vm.$el.textContent).toContain( 'rspec:pg found 1 failed test result and 2 fixed test results out of 8 total tests', ); + expect(vm.$el.textContent).toContain('New'); expect(vm.$el.textContent).toContain( ' java ant found 1 failed test result out of 3 total tests', @@ -151,6 +154,7 @@ describe('Grouped Test Reports App', () => { expect(vm.$el.querySelector('.js-mr-code-resolved-issues').textContent).toContain( resolvedFailures.suites[0].resolved_failures[0].name, ); + expect(vm.$el.querySelector('.js-mr-code-resolved-issues').textContent).toContain( resolvedFailures.suites[0].resolved_failures[1].name, ); diff --git a/spec/javascripts/reports/components/modal_spec.js b/spec/javascripts/reports/components/modal_spec.js index 3a567c40eca..6b8471381de 100644 --- a/spec/javascripts/reports/components/modal_spec.js +++ b/spec/javascripts/reports/components/modal_spec.js @@ -27,12 +27,19 @@ describe('Grouped Test Reports Modal', () => { }); it('renders code block', () => { - expect(vm.$el.querySelector('code').textContent).toEqual(modalDataStructure.system_output.value); + expect(vm.$el.querySelector('code').textContent).toEqual( + modalDataStructure.system_output.value, + ); }); it('renders link', () => { - expect(vm.$el.querySelector('.js-modal-link').getAttribute('href')).toEqual(modalDataStructure.class.value); - expect(trimText(vm.$el.querySelector('.js-modal-link').textContent)).toEqual(modalDataStructure.class.value); + expect(vm.$el.querySelector('.js-modal-link').getAttribute('href')).toEqual( + modalDataStructure.class.value, + ); + + expect(trimText(vm.$el.querySelector('.js-modal-link').textContent)).toEqual( + modalDataStructure.class.value, + ); }); it('renders miliseconds', () => { @@ -40,6 +47,8 @@ describe('Grouped Test Reports Modal', () => { }); it('render title', () => { - expect(trimText(vm.$el.querySelector('.modal-title').textContent)).toEqual('Test#sum when a is 1 and b is 2 returns summary'); + expect(trimText(vm.$el.querySelector('.modal-title').textContent)).toEqual( + 'Test#sum when a is 1 and b is 2 returns summary', + ); }); }); diff --git a/spec/javascripts/reports/components/report_link_spec.js b/spec/javascripts/reports/components/report_link_spec.js index cd6911e2f59..f879899e9c5 100644 --- a/spec/javascripts/reports/components/report_link_spec.js +++ b/spec/javascripts/reports/components/report_link_spec.js @@ -45,8 +45,7 @@ describe('report link', () => { vm = mountComponent(Component, { issue: { path: 'Gemfile.lock', - urlPath: - 'https://groups.google.com/forum/#!topic/rubyonrails-security/335P1DcLG00', + urlPath: 'https://groups.google.com/forum/#!topic/rubyonrails-security/335P1DcLG00', line: 22, }, }); @@ -60,8 +59,7 @@ describe('report link', () => { vm = mountComponent(Component, { issue: { path: 'Gemfile.lock', - urlPath: - 'https://groups.google.com/forum/#!topic/rubyonrails-security/335P1DcLG00', + urlPath: 'https://groups.google.com/forum/#!topic/rubyonrails-security/335P1DcLG00', }, }); diff --git a/spec/javascripts/reports/components/report_section_spec.js b/spec/javascripts/reports/components/report_section_spec.js index bf11dbea386..eb7307605d7 100644 --- a/spec/javascripts/reports/components/report_section_spec.js +++ b/spec/javascripts/reports/components/report_section_spec.js @@ -97,6 +97,7 @@ describe('Report section', () => { successText: 'Code quality improved on 1 point and degraded on 1 point', hasIssues: false, }); + expect(vm.$el.textContent.trim()).toEqual('Loading codeclimate report'); }); }); @@ -169,6 +170,7 @@ describe('Report section', () => { successText: 'Code quality improved on 1 point and degraded on 1 point', hasIssues: false, }); + expect(vm.$el.textContent.trim()).toEqual('Failed to load codeclimate report'); }); }); diff --git a/spec/javascripts/reports/components/test_issue_body_spec.js b/spec/javascripts/reports/components/test_issue_body_spec.js index 0ea81f714e7..32baf904ad7 100644 --- a/spec/javascripts/reports/components/test_issue_body_spec.js +++ b/spec/javascripts/reports/components/test_issue_body_spec.js @@ -29,6 +29,7 @@ describe('Test Issue body', () => { spyOn(vm, 'openModal'); vm.$el.querySelector('button').click(); + expect(vm.openModal).toHaveBeenCalledWith({ issue: commonProps.issue, }); diff --git a/spec/javascripts/reports/store/mutations_spec.js b/spec/javascripts/reports/store/mutations_spec.js index 7d19b16efb9..9446cd454ab 100644 --- a/spec/javascripts/reports/store/mutations_spec.js +++ b/spec/javascripts/reports/store/mutations_spec.js @@ -13,6 +13,7 @@ describe('Reports Store Mutations', () => { describe('SET_ENDPOINT', () => { it('should set endpoint', () => { mutations[types.SET_ENDPOINT](stateCopy, 'endpoint.json'); + expect(stateCopy.endpoint).toEqual('endpoint.json'); }); }); @@ -20,6 +21,7 @@ describe('Reports Store Mutations', () => { describe('REQUEST_REPORTS', () => { it('should set isLoading to true', () => { mutations[types.REQUEST_REPORTS](stateCopy); + expect(stateCopy.isLoading).toEqual(true); }); }); diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js index 43e4db4c712..992e17978c1 100644 --- a/spec/javascripts/right_sidebar_spec.js +++ b/spec/javascripts/right_sidebar_spec.js @@ -1,98 +1,87 @@ -/* eslint-disable no-var, one-var, no-return-assign, vars-on-top */ - import $ from 'jquery'; import MockAdapter from 'axios-mock-adapter'; import '~/commons/bootstrap'; import axios from '~/lib/utils/axios_utils'; import Sidebar from '~/right_sidebar'; -(function() { - var $aside, $icon, $labelsIcon, $page, $toggle, assertSidebarState; - - $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() { - describe('fixture tests', () => { - var fixtureName = 'issues/open-issue.html.raw'; - preloadFixtures(fixtureName); - loadJSONFixtures('todos/todos.json'); - let mock; - - beforeEach(function() { - loadFixtures(fixtureName); - mock = new MockAdapter(axios); - new Sidebar(); // eslint-disable-line no-new - $aside = $('.right-sidebar'); - $page = $('.layout-page'); - $icon = $aside.find('i'); - $toggle = $aside.find('.js-sidebar-toggle'); - return $labelsIcon = $aside.find('.sidebar-collapsed-icon'); - }); +let $aside = null; +let $toggle = null; +let $icon = null; +let $page = null; +let $labelsIcon = null; + +const assertSidebarState = function(state) { + const shouldBeExpanded = state === 'expanded'; + const 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); + expect($icon.hasClass('fa-angle-double-left')).toBe(shouldBeCollapsed); +}; + +describe('RightSidebar', function() { + describe('fixture tests', () => { + const fixtureName = 'issues/open-issue.html.raw'; + preloadFixtures(fixtureName); + loadJSONFixtures('todos/todos.json'); + let mock; + + beforeEach(function() { + loadFixtures(fixtureName); + mock = new MockAdapter(axios); + new Sidebar(); // eslint-disable-line no-new + $aside = $('.right-sidebar'); + $page = $('.layout-page'); + $icon = $aside.find('i'); + $toggle = $aside.find('.js-sidebar-toggle'); + $labelsIcon = $aside.find('.sidebar-collapsed-icon'); + }); - afterEach(() => { - mock.restore(); - }); + afterEach(() => { + mock.restore(); + }); - it('should expand/collapse the sidebar when arrow is clicked', function() { - assertSidebarState('expanded'); - $toggle.click(); - assertSidebarState('collapsed'); - $toggle.click(); - assertSidebarState('expanded'); - }); + it('should expand/collapse the sidebar when arrow is clicked', function() { + assertSidebarState('expanded'); + $toggle.click(); + assertSidebarState('collapsed'); + $toggle.click(); + assertSidebarState('expanded'); + }); - it('should float over the page and when sidebar icons clicked', function() { - $labelsIcon.click(); - return assertSidebarState('expanded'); - }); + it('should float over the page and when sidebar icons clicked', function() { + $labelsIcon.click(); + assertSidebarState('expanded'); + }); - it('should collapse when the icon arrow clicked while it is floating on page', function() { - $labelsIcon.click(); - assertSidebarState('expanded'); - $toggle.click(); - return assertSidebarState('collapsed'); - }); + it('should collapse when the icon arrow clicked while it is floating on page', function() { + $labelsIcon.click(); + assertSidebarState('expanded'); + $toggle.click(); + assertSidebarState('collapsed'); + }); - it('should broadcast todo:toggle event when add todo clicked', function(done) { - var todos = getJSONFixture('todos/todos.json'); - mock.onPost(/(.*)\/todos$/).reply(200, todos); + it('should broadcast todo:toggle event when add todo clicked', function(done) { + const todos = getJSONFixture('todos/todos.json'); + mock.onPost(/(.*)\/todos$/).reply(200, todos); - var todoToggleSpy = spyOnEvent(document, 'todo:toggle'); + const todoToggleSpy = spyOnEvent(document, 'todo:toggle'); - $('.issuable-sidebar-header .js-issuable-todo').click(); + $('.issuable-sidebar-header .js-issuable-todo').click(); - setTimeout(() => { - expect(todoToggleSpy.calls.count()).toEqual(1); + setTimeout(() => { + expect(todoToggleSpy.calls.count()).toEqual(1); - done(); - }); + done(); }); + }); - it('should not hide collapsed icons', () => { - [].forEach.call(document.querySelectorAll('.sidebar-collapsed-icon'), (el) => { - expect(el.querySelector('.fa, svg').classList.contains('hidden')).toBeFalsy(); - }); + it('should not hide collapsed icons', () => { + [].forEach.call(document.querySelectorAll('.sidebar-collapsed-icon'), el => { + expect(el.querySelector('.fa, svg').classList.contains('hidden')).toBeFalsy(); }); }); }); -}).call(window); +}); diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js index bc1bb50dc5c..7530fd2a43b 100644 --- a/spec/javascripts/search_autocomplete_spec.js +++ b/spec/javascripts/search_autocomplete_spec.js @@ -111,6 +111,7 @@ describe('Search autocomplete dropdown', () => { if (issuesPath) { const issuesAssignedToMeLink = `a[href="${issuesPath}/?assignee_id=${userId}"]`; const issuesIHaveCreatedLink = `a[href="${issuesPath}/?author_id=${userId}"]`; + expect(list.find(issuesAssignedToMeLink).length).toBe(1); expect(list.find(issuesAssignedToMeLink).text()).toBe('Issues assigned to me'); expect(list.find(issuesIHaveCreatedLink).length).toBe(1); @@ -118,6 +119,7 @@ describe('Search autocomplete dropdown', () => { } const mrsAssignedToMeLink = `a[href="${mrsPath}/?assignee_id=${userId}"]`; const mrsIHaveCreatedLink = `a[href="${mrsPath}/?author_id=${userId}"]`; + expect(list.find(mrsAssignedToMeLink).length).toBe(1); expect(list.find(mrsAssignedToMeLink).text()).toBe('Merge requests assigned to me'); expect(list.find(mrsIHaveCreatedLink).length).toBe(1); @@ -185,7 +187,8 @@ describe('Search autocomplete dropdown', () => { widget.searchInput.triggerHandler('focus'); list = widget.wrap.find('.dropdown-menu').find('ul'); link = "a[href='" + projectIssuesPath + '/?assignee_id=' + userId + "']"; - return expect(list.find(link).length).toBe(0); + + expect(list.find(link).length).toBe(0); }); it('should not submit the search form when selecting an autocomplete row with the keyboard', function() { diff --git a/spec/javascripts/search_spec.js b/spec/javascripts/search_spec.js index 522851c584b..40bdbac7451 100644 --- a/spec/javascripts/search_spec.js +++ b/spec/javascripts/search_spec.js @@ -5,7 +5,7 @@ import Search from '~/pages/search/show/search'; describe('Search', () => { const fixturePath = 'search/show.html.raw'; const searchTerm = 'some search'; - const fillDropdownInput = (dropdownSelector) => { + const fillDropdownInput = dropdownSelector => { const dropdownElement = document.querySelector(dropdownSelector).parentNode; const inputElement = dropdownElement.querySelector('.dropdown-input-field'); inputElement.value = searchTerm; @@ -19,8 +19,8 @@ describe('Search', () => { new Search(); // eslint-disable-line no-new }); - it('requests groups from backend when filtering', (done) => { - spyOn(Api, 'groups').and.callFake((term) => { + it('requests groups from backend when filtering', done => { + spyOn(Api, 'groups').and.callFake(term => { expect(term).toBe(searchTerm); done(); }); @@ -29,8 +29,8 @@ describe('Search', () => { $(inputElement).trigger('input'); }); - it('requests projects from backend when filtering', (done) => { - spyOn(Api, 'projects').and.callFake((term) => { + it('requests projects from backend when filtering', done => { + spyOn(Api, 'projects').and.callFake(term => { expect(term).toBe(searchTerm); done(); }); diff --git a/spec/javascripts/shared/popover_spec.js b/spec/javascripts/shared/popover_spec.js index 1d574c9424b..85bde075b77 100644 --- a/spec/javascripts/shared/popover_spec.js +++ b/spec/javascripts/shared/popover_spec.js @@ -1,9 +1,5 @@ import $ from 'jquery'; -import { - togglePopover, - mouseleave, - mouseenter, -} from '~/shared/popover'; +import { togglePopover, mouseleave, mouseenter } from '~/shared/popover'; describe('popover', () => { describe('togglePopover', () => { @@ -26,14 +22,14 @@ describe('popover', () => { expect(togglePopover.call(context, true)).toEqual(false); }); - it('shows popover', (done) => { + it('shows popover', done => { const context = { hasClass: () => false, popover: () => {}, toggleClass: () => {}, }; - spyOn(context, 'popover').and.callFake((method) => { + spyOn(context, 'popover').and.callFake(method => { expect(method).toEqual('show'); done(); }); @@ -41,7 +37,7 @@ describe('popover', () => { togglePopover.call(context, true); }); - it('adds disable-animation and js-popover-show class', (done) => { + it('adds disable-animation and js-popover-show class', done => { const context = { hasClass: () => false, popover: () => {}, @@ -77,14 +73,14 @@ describe('popover', () => { expect(togglePopover.call(context, false)).toEqual(false); }); - it('hides popover', (done) => { + it('hides popover', done => { const context = { hasClass: () => true, popover: () => {}, toggleClass: () => {}, }; - spyOn(context, 'popover').and.callFake((method) => { + spyOn(context, 'popover').and.callFake(method => { expect(method).toEqual('hide'); done(); }); @@ -92,7 +88,7 @@ describe('popover', () => { togglePopover.call(context, false); }); - it('removes disable-animation and js-popover-show class', (done) => { + it('removes disable-animation and js-popover-show class', done => { const context = { hasClass: () => true, popover: () => {}, @@ -116,9 +112,12 @@ describe('popover', () => { length: 0, }; - spyOn($.fn, 'init').and.callFake(selector => (selector === '.popover:hover' ? fakeJquery : $.fn)); + spyOn($.fn, 'init').and.callFake( + selector => (selector === '.popover:hover' ? fakeJquery : $.fn), + ); spyOn(togglePopover, 'call'); mouseleave(); + expect(togglePopover.call).toHaveBeenCalledWith(jasmine.any(Object), false); }); @@ -127,9 +126,12 @@ describe('popover', () => { length: 1, }; - spyOn($.fn, 'init').and.callFake(selector => (selector === '.popover:hover' ? fakeJquery : $.fn)); + spyOn($.fn, 'init').and.callFake( + selector => (selector === '.popover:hover' ? fakeJquery : $.fn), + ); spyOn(togglePopover, 'call'); mouseleave(); + expect(togglePopover.call).not.toHaveBeenCalledWith(false); }); }); @@ -140,12 +142,13 @@ describe('popover', () => { it('shows popover', () => { spyOn(togglePopover, 'call').and.returnValue(false); mouseenter.call(context); + expect(togglePopover.call).toHaveBeenCalledWith(jasmine.any(Object), true); }); - it('registers mouseleave event if popover is showed', (done) => { + it('registers mouseleave event if popover is showed', done => { spyOn(togglePopover, 'call').and.returnValue(true); - spyOn($.fn, 'on').and.callFake((eventName) => { + spyOn($.fn, 'on').and.callFake(eventName => { expect(eventName).toEqual('mouseleave'); done(); }); @@ -156,6 +159,7 @@ describe('popover', () => { spyOn(togglePopover, 'call').and.returnValue(false); const spy = spyOn($.fn, 'on').and.callFake(() => {}); mouseenter.call(context); + expect(spy).not.toHaveBeenCalled(); }); }); diff --git a/spec/javascripts/sidebar/assignees_spec.js b/spec/javascripts/sidebar/assignees_spec.js index 843e7002180..e7f8f4f9936 100644 --- a/spec/javascripts/sidebar/assignees_spec.js +++ b/spec/javascripts/sidebar/assignees_spec.js @@ -22,6 +22,7 @@ describe('Assignee component', () => { }).$mount(); const collapsed = component.$el.querySelector('.sidebar-collapsed-icon'); + expect(collapsed.childElementCount).toEqual(1); expect(collapsed.children[0].getAttribute('aria-label')).toEqual('No Assignee'); expect(collapsed.children[0].classList.contains('fa')).toEqual(true); @@ -67,6 +68,7 @@ describe('Assignee component', () => { spyOn(component, '$emit'); component.$el.querySelector('.assign-yourself .btn-link').click(); + expect(component.$emit).toHaveBeenCalledWith('assign-self'); }); }); @@ -85,6 +87,7 @@ describe('Assignee component', () => { const collapsed = component.$el.querySelector('.sidebar-collapsed-icon'); const assignee = collapsed.children[0]; + expect(collapsed.childElementCount).toEqual(1); expect(assignee.querySelector('.avatar').getAttribute('src')).toEqual(UsersMock.user.avatar); expect(assignee.querySelector('.avatar').getAttribute('alt')).toEqual(`${UsersMock.user.name}'s avatar`); @@ -138,14 +141,17 @@ describe('Assignee component', () => { }).$mount(); const collapsed = component.$el.querySelector('.sidebar-collapsed-icon'); + expect(collapsed.childElementCount).toEqual(2); const first = collapsed.children[0]; + expect(first.querySelector('.avatar').getAttribute('src')).toEqual(users[0].avatar); expect(first.querySelector('.avatar').getAttribute('alt')).toEqual(`${users[0].name}'s avatar`); expect(first.querySelector('.author').innerText.trim()).toEqual(users[0].name); const second = collapsed.children[1]; + expect(second.querySelector('.avatar').getAttribute('src')).toEqual(users[1].avatar); expect(second.querySelector('.avatar').getAttribute('alt')).toEqual(`${users[1].name}'s avatar`); expect(second.querySelector('.author').innerText.trim()).toEqual(users[1].name); @@ -162,14 +168,17 @@ describe('Assignee component', () => { }).$mount(); const collapsed = component.$el.querySelector('.sidebar-collapsed-icon'); + expect(collapsed.childElementCount).toEqual(2); const first = collapsed.children[0]; + expect(first.querySelector('.avatar').getAttribute('src')).toEqual(users[0].avatar); expect(first.querySelector('.avatar').getAttribute('alt')).toEqual(`${users[0].name}'s avatar`); expect(first.querySelector('.author').innerText.trim()).toEqual(users[0].name); const second = collapsed.children[1]; + expect(second.querySelector('.avatar-counter').innerText.trim()).toEqual('+2'); }); @@ -200,6 +209,7 @@ describe('Assignee component', () => { expect(component.$el.querySelectorAll('.user-item').length).toEqual(component.defaultRenderCount); expect(component.$el.querySelector('.user-list-more')).not.toBe(null); const usersLabelExpectation = users.length - component.defaultRenderCount; + expect(component.$el.querySelector('.user-list-more .btn-link').innerText.trim()) .not.toBe(`+${usersLabelExpectation} more`); component.toggleShowLess(); diff --git a/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js b/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js index 0e30759c41d..4c3dd713589 100644 --- a/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js +++ b/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js @@ -8,10 +8,12 @@ describe('Issuable Time Tracker', () => { let initialData; let vm; - const initTimeTrackingComponent = ({ timeEstimate, - timeSpent, - timeEstimateHumanReadable, - timeSpentHumanReadable }) => { + const initTimeTrackingComponent = ({ + timeEstimate, + timeSpent, + timeEstimateHumanReadable, + timeSpentHumanReadable, + }) => { setFixtures(` <div> <div id="mock-container"></div> @@ -88,6 +90,7 @@ describe('Issuable Time Tracker', () => { Vue.nextTick(() => { expect(vm.showComparisonState).toBe(true); const $comparisonPane = vm.$el.querySelector('.time-tracking-comparison-pane'); + expect($comparisonPane).toBeVisible(); done(); }); @@ -96,14 +99,18 @@ describe('Issuable Time Tracker', () => { describe('Remaining meter', () => { it('should display the remaining meter with the correct width', done => { Vue.nextTick(() => { - expect(vm.$el.querySelector('.time-tracking-comparison-pane .progress[value="5"]')).not.toBeNull(); + expect( + vm.$el.querySelector('.time-tracking-comparison-pane .progress[value="5"]'), + ).not.toBeNull(); done(); }); }); it('should display the remaining meter with the correct background color when within estimate', done => { Vue.nextTick(() => { - expect(vm.$el.querySelector('.time-tracking-comparison-pane .progress[variant="primary"]')).not.toBeNull(); + expect( + vm.$el.querySelector('.time-tracking-comparison-pane .progress[variant="primary"]'), + ).not.toBeNull(); done(); }); }); @@ -112,7 +119,9 @@ describe('Issuable Time Tracker', () => { vm.timeEstimate = 10000; // 2h 46m vm.timeSpent = 20000000; // 231 days Vue.nextTick(() => { - expect(vm.$el.querySelector('.time-tracking-comparison-pane .progress[variant="danger"]')).not.toBeNull(); + expect( + vm.$el.querySelector('.time-tracking-comparison-pane .progress[variant="danger"]'), + ).not.toBeNull(); done(); }); }); diff --git a/spec/javascripts/sidebar/confidential_edit_buttons_spec.js b/spec/javascripts/sidebar/confidential_edit_buttons_spec.js index 482be466aad..32da9f83112 100644 --- a/spec/javascripts/sidebar/confidential_edit_buttons_spec.js +++ b/spec/javascripts/sidebar/confidential_edit_buttons_spec.js @@ -7,8 +7,8 @@ describe('Edit Form Buttons', () => { beforeEach(() => { const Component = Vue.extend(editFormButtons); - const toggleForm = () => { }; - const updateConfidentialAttribute = () => { }; + const toggleForm = () => {}; + const updateConfidentialAttribute = () => {}; vm1 = new Component({ propsData: { @@ -28,12 +28,8 @@ describe('Edit Form Buttons', () => { }); it('renders on or off text based on confidentiality', () => { - expect( - vm1.$el.innerHTML.includes('Turn Off'), - ).toBe(true); + expect(vm1.$el.innerHTML.includes('Turn Off')).toBe(true); - expect( - vm2.$el.innerHTML.includes('Turn On'), - ).toBe(true); + expect(vm2.$el.innerHTML.includes('Turn On')).toBe(true); }); }); diff --git a/spec/javascripts/sidebar/confidential_edit_form_buttons_spec.js b/spec/javascripts/sidebar/confidential_edit_form_buttons_spec.js index 724f5126945..369088cb258 100644 --- a/spec/javascripts/sidebar/confidential_edit_form_buttons_spec.js +++ b/spec/javascripts/sidebar/confidential_edit_form_buttons_spec.js @@ -7,8 +7,8 @@ describe('Edit Form Dropdown', () => { beforeEach(() => { const Component = Vue.extend(editForm); - const toggleForm = () => { }; - const updateConfidentialAttribute = () => { }; + const toggleForm = () => {}; + const updateConfidentialAttribute = () => {}; vm1 = new Component({ propsData: { @@ -28,12 +28,8 @@ describe('Edit Form Dropdown', () => { }); it('renders on the appropriate warning text', () => { - expect( - vm1.$el.innerHTML.includes('You are going to turn off the confidentiality.'), - ).toBe(true); + expect(vm1.$el.innerHTML.includes('You are going to turn off the confidentiality.')).toBe(true); - expect( - vm2.$el.innerHTML.includes('You are going to turn on the confidentiality.'), - ).toBe(true); + expect(vm2.$el.innerHTML.includes('You are going to turn on the confidentiality.')).toBe(true); }); }); diff --git a/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js b/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js index 6110d5d89ac..486a7241e33 100644 --- a/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js +++ b/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js @@ -29,20 +29,14 @@ describe('Confidential Issue Sidebar Block', () => { }); it('shows if confidential and/or editable', () => { - expect( - vm1.$el.innerHTML.includes('Edit'), - ).toBe(true); + expect(vm1.$el.innerHTML.includes('Edit')).toBe(true); - expect( - vm1.$el.innerHTML.includes('This issue is confidential'), - ).toBe(true); + expect(vm1.$el.innerHTML.includes('This issue is confidential')).toBe(true); - expect( - vm2.$el.innerHTML.includes('Not confidential'), - ).toBe(true); + expect(vm2.$el.innerHTML.includes('Not confidential')).toBe(true); }); - it('displays the edit form when editable', (done) => { + it('displays the edit form when editable', done => { expect(vm1.edit).toBe(false); vm1.$el.querySelector('.confidential-edit').click(); @@ -50,17 +44,15 @@ describe('Confidential Issue Sidebar Block', () => { expect(vm1.edit).toBe(true); setTimeout(() => { - expect( - vm1.$el - .innerHTML - .includes('You are going to turn off the confidentiality.'), - ).toBe(true); + expect(vm1.$el.innerHTML.includes('You are going to turn off the confidentiality.')).toBe( + true, + ); done(); }); }); - it('displays the edit form when opened from collapsed state', (done) => { + it('displays the edit form when opened from collapsed state', done => { expect(vm1.edit).toBe(false); vm1.$el.querySelector('.sidebar-collapsed-icon').click(); @@ -68,11 +60,9 @@ describe('Confidential Issue Sidebar Block', () => { expect(vm1.edit).toBe(true); setTimeout(() => { - expect( - vm1.$el - .innerHTML - .includes('You are going to turn off the confidentiality.'), - ).toBe(true); + expect(vm1.$el.innerHTML.includes('You are going to turn off the confidentiality.')).toBe( + true, + ); done(); }); diff --git a/spec/javascripts/sidebar/lock/edit_form_buttons_spec.js b/spec/javascripts/sidebar/lock/edit_form_buttons_spec.js index deeea669de8..330f59f08b2 100644 --- a/spec/javascripts/sidebar/lock/edit_form_buttons_spec.js +++ b/spec/javascripts/sidebar/lock/edit_form_buttons_spec.js @@ -8,8 +8,8 @@ describe('EditFormButtons', () => { beforeEach(() => { const Component = Vue.extend(editFormButtons); - const toggleForm = () => { }; - const updateLockedAttribute = () => { }; + const toggleForm = () => {}; + const updateLockedAttribute = () => {}; vm1 = mountComponent(Component, { isLocked: true, @@ -25,12 +25,8 @@ describe('EditFormButtons', () => { }); it('renders unlock or lock text based on locked state', () => { - expect( - vm1.$el.innerHTML.includes('Unlock'), - ).toBe(true); + expect(vm1.$el.innerHTML.includes('Unlock')).toBe(true); - expect( - vm2.$el.innerHTML.includes('Lock'), - ).toBe(true); + expect(vm2.$el.innerHTML.includes('Lock')).toBe(true); }); }); diff --git a/spec/javascripts/sidebar/lock/edit_form_spec.js b/spec/javascripts/sidebar/lock/edit_form_spec.js index 7abd6997a18..ec10a999a40 100644 --- a/spec/javascripts/sidebar/lock/edit_form_spec.js +++ b/spec/javascripts/sidebar/lock/edit_form_spec.js @@ -7,8 +7,8 @@ describe('EditForm', () => { beforeEach(() => { const Component = Vue.extend(editForm); - const toggleForm = () => { }; - const updateLockedAttribute = () => { }; + const toggleForm = () => {}; + const updateLockedAttribute = () => {}; vm1 = new Component({ propsData: { @@ -30,12 +30,8 @@ describe('EditForm', () => { }); it('renders on the appropriate warning text', () => { - expect( - vm1.$el.innerHTML.includes('Unlock this issue?'), - ).toBe(true); + expect(vm1.$el.innerHTML.includes('Unlock this issue?')).toBe(true); - expect( - vm2.$el.innerHTML.includes('Lock this merge request?'), - ).toBe(true); + expect(vm2.$el.innerHTML.includes('Lock this merge request?')).toBe(true); }); }); diff --git a/spec/javascripts/sidebar/lock/lock_issue_sidebar_spec.js b/spec/javascripts/sidebar/lock/lock_issue_sidebar_spec.js index 9abc3daf221..ca882032bdf 100644 --- a/spec/javascripts/sidebar/lock/lock_issue_sidebar_spec.js +++ b/spec/javascripts/sidebar/lock/lock_issue_sidebar_spec.js @@ -38,20 +38,14 @@ describe('LockIssueSidebar', () => { }); it('shows if locked and/or editable', () => { - expect( - vm1.$el.innerHTML.includes('Edit'), - ).toBe(true); + expect(vm1.$el.innerHTML.includes('Edit')).toBe(true); - expect( - vm1.$el.innerHTML.includes('Locked'), - ).toBe(true); + expect(vm1.$el.innerHTML.includes('Locked')).toBe(true); - expect( - vm2.$el.innerHTML.includes('Unlocked'), - ).toBe(true); + expect(vm2.$el.innerHTML.includes('Unlocked')).toBe(true); }); - it('displays the edit form when editable', (done) => { + it('displays the edit form when editable', done => { expect(vm1.isLockDialogOpen).toBe(false); vm1.$el.querySelector('.lock-edit').click(); @@ -59,17 +53,13 @@ describe('LockIssueSidebar', () => { expect(vm1.isLockDialogOpen).toBe(true); vm1.$nextTick(() => { - expect( - vm1.$el - .innerHTML - .includes('Unlock this issue?'), - ).toBe(true); + expect(vm1.$el.innerHTML.includes('Unlock this issue?')).toBe(true); done(); }); }); - it('displays the edit form when opened from collapsed state', (done) => { + it('displays the edit form when opened from collapsed state', done => { expect(vm1.isLockDialogOpen).toBe(false); vm1.$el.querySelector('.sidebar-collapsed-icon').click(); @@ -77,11 +67,7 @@ describe('LockIssueSidebar', () => { expect(vm1.isLockDialogOpen).toBe(true); setTimeout(() => { - expect( - vm1.$el - .innerHTML - .includes('Unlock this issue?'), - ).toBe(true); + expect(vm1.$el.innerHTML.includes('Unlock this issue?')).toBe(true); done(); }); diff --git a/spec/javascripts/sidebar/participants_spec.js b/spec/javascripts/sidebar/participants_spec.js index e796ddee62f..eb360fd256a 100644 --- a/spec/javascripts/sidebar/participants_spec.js +++ b/spec/javascripts/sidebar/participants_spec.js @@ -11,13 +11,9 @@ const PARTICIPANT = { avatar_url: 'gravatar.com/avatar/xxx', }; -const PARTICIPANT_LIST = [ - PARTICIPANT, - { ...PARTICIPANT, id: 2 }, - { ...PARTICIPANT, id: 3 }, -]; +const PARTICIPANT_LIST = [PARTICIPANT, { ...PARTICIPANT, id: 2 }, { ...PARTICIPANT, id: 3 }]; -describe('Participants', function () { +describe('Participants', function() { let vm; let Participants; @@ -69,7 +65,7 @@ describe('Participants', function () { expect(vm.$el.querySelector('.js-participants-expanded-loading-icon')).toBeDefined(); }); - it('when only showing visible participants, shows an avatar only for each participant under the limit', (done) => { + it('when only showing visible participants, shows an avatar only for each participant under the limit', done => { const numberOfLessParticipants = 2; vm = mountComponent(Participants, { loading: false, @@ -88,7 +84,7 @@ describe('Participants', function () { .catch(done.fail); }); - it('when only showing all participants, each has an avatar', (done) => { + it('when only showing all participants, each has an avatar', done => { const numberOfLessParticipants = 2; vm = mountComponent(Participants, { loading: false, @@ -120,7 +116,7 @@ describe('Participants', function () { expect(moreParticipantLink).toBeNull(); }); - it('when too many participants, has more participants link to show more', (done) => { + it('when too many participants, has more participants link to show more', done => { vm = mountComponent(Participants, { loading: false, participants: PARTICIPANT_LIST, @@ -138,7 +134,7 @@ describe('Participants', function () { .catch(done.fail); }); - it('when too many participants and already showing them, has more participants link to show less', (done) => { + it('when too many participants and already showing them, has more participants link to show less', done => { vm = mountComponent(Participants, { loading: false, participants: PARTICIPANT_LIST, @@ -182,6 +178,7 @@ describe('Participants', function () { const participantsIconEl = vm.$el.querySelector('.sidebar-collapsed-icon'); participantsIconEl.click(); + expect(vm.$emit).toHaveBeenCalledWith('toggleSidebar'); }); }); diff --git a/spec/javascripts/sidebar/sidebar_assignees_spec.js b/spec/javascripts/sidebar/sidebar_assignees_spec.js index ebaaa6e806b..3f0f67d71ca 100644 --- a/spec/javascripts/sidebar/sidebar_assignees_spec.js +++ b/spec/javascripts/sidebar/sidebar_assignees_spec.js @@ -24,10 +24,14 @@ describe('sidebar assignees', () => { const SidebarAssigneeComponent = Vue.extend(SidebarAssignees); sidebarAssigneesEl = document.querySelector('#js-vue-sidebar-assignees'); - vm = mountComponent(SidebarAssigneeComponent, { - mediator, - field: sidebarAssigneesEl.dataset.field, - }, sidebarAssigneesEl); + vm = mountComponent( + SidebarAssigneeComponent, + { + mediator, + field: sidebarAssigneesEl.dataset.field, + }, + sidebarAssigneesEl, + ); }); afterEach(() => { @@ -39,6 +43,7 @@ describe('sidebar assignees', () => { it('calls the mediator when saves the assignees', () => { vm.saveAssignees(); + expect(mediator.saveAssignees).toHaveBeenCalled(); }); @@ -49,8 +54,9 @@ describe('sidebar assignees', () => { expect(mediator.store.assignees.length).toEqual(1); }); - it('hides assignees until fetched', (done) => { + it('hides assignees until fetched', done => { const currentAssignee = sidebarAssigneesEl.querySelector('.value'); + expect(currentAssignee).toBe(null); vm.store.isFetching.assignees = false; diff --git a/spec/javascripts/sidebar/sidebar_mediator_spec.js b/spec/javascripts/sidebar/sidebar_mediator_spec.js index da950258a94..2d853970fc4 100644 --- a/spec/javascripts/sidebar/sidebar_mediator_spec.js +++ b/spec/javascripts/sidebar/sidebar_mediator_spec.js @@ -25,20 +25,23 @@ describe('Sidebar mediator', function() { expect(this.mediator.store.assignees[0]).toEqual(Mock.mediator.currentUser); }); - it('saves assignees', (done) => { - this.mediator.saveAssignees('issue[assignee_ids]') - .then((resp) => { + it('saves assignees', done => { + this.mediator + .saveAssignees('issue[assignee_ids]') + .then(resp => { expect(resp.status).toEqual(200); done(); }) .catch(done.fail); }); - it('fetches the data', (done) => { - const mockData = Mock.responseMap.GET['/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar']; + it('fetches the data', done => { + const mockData = + Mock.responseMap.GET['/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar']; spyOn(this.mediator, 'processFetchedData').and.callThrough(); - this.mediator.fetch() + this.mediator + .fetch() .then(() => { expect(this.mediator.processFetchedData).toHaveBeenCalledWith(mockData); }) @@ -47,7 +50,8 @@ describe('Sidebar mediator', function() { }); it('processes fetched data', () => { - const mockData = Mock.responseMap.GET['/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar']; + const mockData = + Mock.responseMap.GET['/gitlab-org/gitlab-shell/issues/5.json?serializer=sidebar']; this.mediator.processFetchedData(mockData); expect(this.mediator.store.assignees).toEqual(mockData.assignees); @@ -68,12 +72,13 @@ describe('Sidebar mediator', function() { expect(this.mediator.store.setMoveToProjectId).toHaveBeenCalledWith(projectId); }); - it('fetches autocomplete projects', (done) => { + it('fetches autocomplete projects', done => { const searchTerm = 'foo'; spyOn(this.mediator.service, 'getProjectsAutocomplete').and.callThrough(); spyOn(this.mediator.store, 'setAutocompleteProjects').and.callThrough(); - this.mediator.fetchAutocompleteProjects(searchTerm) + this.mediator + .fetchAutocompleteProjects(searchTerm) .then(() => { expect(this.mediator.service.getProjectsAutocomplete).toHaveBeenCalledWith(searchTerm); expect(this.mediator.store.setAutocompleteProjects).toHaveBeenCalled(); @@ -82,13 +87,14 @@ describe('Sidebar mediator', function() { .catch(done.fail); }); - it('moves issue', (done) => { + it('moves issue', done => { const moveToProjectId = 7; this.mediator.store.setMoveToProjectId(moveToProjectId); spyOn(this.mediator.service, 'moveIssue').and.callThrough(); const visitUrl = spyOnDependency(SidebarMediator, 'visitUrl'); - this.mediator.moveIssue() + this.mediator + .moveIssue() .then(() => { expect(this.mediator.service.moveIssue).toHaveBeenCalledWith(moveToProjectId); expect(visitUrl).toHaveBeenCalledWith('/root/some-project/issues/5'); @@ -97,11 +103,12 @@ describe('Sidebar mediator', function() { .catch(done.fail); }); - it('toggle subscription', (done) => { + it('toggle subscription', done => { this.mediator.store.setSubscribedState(false); spyOn(this.mediator.service, 'toggleSubscription').and.callThrough(); - this.mediator.toggleSubscription() + this.mediator + .toggleSubscription() .then(() => { expect(this.mediator.service.toggleSubscription).toHaveBeenCalled(); expect(this.mediator.store.subscribed).toEqual(true); diff --git a/spec/javascripts/sidebar/sidebar_move_issue_spec.js b/spec/javascripts/sidebar/sidebar_move_issue_spec.js index 8f35b9ca437..230e0a933a9 100644 --- a/spec/javascripts/sidebar/sidebar_move_issue_spec.js +++ b/spec/javascripts/sidebar/sidebar_move_issue_spec.js @@ -7,7 +7,7 @@ import SidebarService from '~/sidebar/services/sidebar_service'; import SidebarMoveIssue from '~/sidebar/lib/sidebar_move_issue'; import Mock from './mock_data'; -describe('SidebarMoveIssue', function () { +describe('SidebarMoveIssue', function() { beforeEach(() => { Vue.http.interceptors.push(Mock.sidebarMockInterceptor); this.mediator = new SidebarMediator(Mock.mediator); @@ -72,11 +72,13 @@ describe('SidebarMoveIssue', function () { expect($.fn.glDropdown).toHaveBeenCalled(); }); - it('escapes html from project name', (done) => { + it('escapes html from project name', done => { this.$toggleButton.dropdown('toggle'); setTimeout(() => { - expect(this.$content.find('.js-move-issue-dropdown-item')[1].innerHTML.trim()).toEqual('<img src=x onerror=alert(document.domain)> foo / bar'); + expect(this.$content.find('.js-move-issue-dropdown-item')[1].innerHTML.trim()).toEqual( + '<img src=x onerror=alert(document.domain)> foo / bar', + ); done(); }); }); @@ -94,7 +96,7 @@ describe('SidebarMoveIssue', function () { expect(this.$confirmButton.hasClass('is-loading')).toBe(true); }); - it('should remove loading state from confirm button on failure', (done) => { + it('should remove loading state from confirm button on failure', done => { spyOn(window, 'Flash'); spyOn(this.mediator, 'moveIssue').and.returnValue(Promise.reject()); this.mediator.setMoveToProjectId(7); @@ -121,7 +123,7 @@ describe('SidebarMoveIssue', function () { }); }); - it('should set moveToProjectId on dropdown item "No project" click', (done) => { + it('should set moveToProjectId on dropdown item "No project" click', done => { spyOn(this.mediator, 'setMoveToProjectId'); // Open the dropdown @@ -129,7 +131,10 @@ describe('SidebarMoveIssue', function () { // Wait for the autocomplete request to finish setTimeout(() => { - this.$content.find('.js-move-issue-dropdown-item').eq(0).trigger('click'); + this.$content + .find('.js-move-issue-dropdown-item') + .eq(0) + .trigger('click'); expect(this.mediator.setMoveToProjectId).toHaveBeenCalledWith(0); expect(this.$confirmButton.prop('disabled')).toBeTruthy(); @@ -137,7 +142,7 @@ describe('SidebarMoveIssue', function () { }, 0); }); - it('should set moveToProjectId on dropdown item click', (done) => { + it('should set moveToProjectId on dropdown item click', done => { spyOn(this.mediator, 'setMoveToProjectId'); // Open the dropdown @@ -145,7 +150,10 @@ describe('SidebarMoveIssue', function () { // Wait for the autocomplete request to finish setTimeout(() => { - this.$content.find('.js-move-issue-dropdown-item').eq(1).trigger('click'); + this.$content + .find('.js-move-issue-dropdown-item') + .eq(1) + .trigger('click'); expect(this.mediator.setMoveToProjectId).toHaveBeenCalledWith(20); expect(this.$confirmButton.attr('disabled')).toBe(undefined); diff --git a/spec/javascripts/sidebar/sidebar_store_spec.js b/spec/javascripts/sidebar/sidebar_store_spec.js index 08b112a54ba..85ff70fffbd 100644 --- a/spec/javascripts/sidebar/sidebar_store_spec.js +++ b/spec/javascripts/sidebar/sidebar_store_spec.js @@ -25,20 +25,17 @@ const PARTICIPANT = { avatar_url: 'gravatar.com/avatar/xxx', }; -const PARTICIPANT_LIST = [ - PARTICIPANT, - { ...PARTICIPANT, id: 2 }, - { ...PARTICIPANT, id: 3 }, -]; +const PARTICIPANT_LIST = [PARTICIPANT, { ...PARTICIPANT, id: 2 }, { ...PARTICIPANT, id: 3 }]; -describe('Sidebar store', function () { +describe('Sidebar store', function() { beforeEach(() => { this.store = new SidebarStore({ currentUser: { id: 1, name: 'Administrator', username: 'root', - avatar_url: 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + avatar_url: + 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', }, editable: true, rootPath: '/', @@ -56,11 +53,13 @@ describe('Sidebar store', function () { it('adds a new assignee', () => { this.store.addAssignee(ASSIGNEE); + expect(this.store.assignees.length).toEqual(1); }); it('removes an assignee', () => { this.store.removeAssignee(ASSIGNEE); + expect(this.store.assignees.length).toEqual(0); }); @@ -69,14 +68,17 @@ describe('Sidebar store', function () { this.store.addAssignee(ASSIGNEE); foundAssignee = this.store.findAssignee(ASSIGNEE); + expect(foundAssignee).toBeDefined(); expect(foundAssignee).toEqual(ASSIGNEE); foundAssignee = this.store.findAssignee(ANOTHER_ASSINEE); + expect(foundAssignee).toBeUndefined(); }); it('removes all assignees', () => { this.store.removeAllAssignees(); + expect(this.store.assignees.length).toEqual(0); }); @@ -108,6 +110,7 @@ describe('Sidebar store', function () { }; this.store.setAssigneeData(users); + expect(this.store.isFetching.assignees).toBe(false); expect(this.store.assignees.length).toEqual(3); }); @@ -128,6 +131,7 @@ describe('Sidebar store', function () { it('set time tracking data', () => { this.store.setTimeTrackingData(Mock.time); + expect(this.store.timeEstimate).toEqual(Mock.time.time_estimate); expect(this.store.totalTimeSpent).toEqual(Mock.time.total_time_spent); expect(this.store.humanTimeEstimate).toEqual(Mock.time.human_time_estimate); diff --git a/spec/javascripts/sidebar/sidebar_subscriptions_spec.js b/spec/javascripts/sidebar/sidebar_subscriptions_spec.js index af2fde0a5be..88f64244237 100644 --- a/spec/javascripts/sidebar/sidebar_subscriptions_spec.js +++ b/spec/javascripts/sidebar/sidebar_subscriptions_spec.js @@ -6,7 +6,7 @@ import SidebarStore from '~/sidebar/stores/sidebar_store'; import mountComponent from 'spec/helpers/vue_mount_component_helper'; import Mock from './mock_data'; -describe('Sidebar Subscriptions', function () { +describe('Sidebar Subscriptions', function() { let vm; let SidebarSubscriptions; diff --git a/spec/javascripts/sidebar/subscriptions_spec.js b/spec/javascripts/sidebar/subscriptions_spec.js index f0a53e573c3..32728e58b06 100644 --- a/spec/javascripts/sidebar/subscriptions_spec.js +++ b/spec/javascripts/sidebar/subscriptions_spec.js @@ -3,7 +3,7 @@ import subscriptions from '~/sidebar/components/subscriptions/subscriptions.vue' import eventHub from '~/sidebar/event_hub'; import mountComponent from 'spec/helpers/vue_mount_component_helper'; -describe('Subscriptions', function () { +describe('Subscriptions', function() { let vm; let Subscriptions; @@ -22,7 +22,9 @@ describe('Subscriptions', function () { }); expect(vm.$refs.toggleButton.isLoading).toBe(true); - expect(vm.$refs.toggleButton.$el.querySelector('.project-feature-toggle')).toHaveClass('is-loading'); + expect(vm.$refs.toggleButton.$el.querySelector('.project-feature-toggle')).toHaveClass( + 'is-loading', + ); }); it('is toggled "off" when currently not subscribed', () => { @@ -30,7 +32,9 @@ describe('Subscriptions', function () { subscribed: false, }); - expect(vm.$refs.toggleButton.$el.querySelector('.project-feature-toggle')).not.toHaveClass('is-checked'); + expect(vm.$refs.toggleButton.$el.querySelector('.project-feature-toggle')).not.toHaveClass( + 'is-checked', + ); }); it('is toggled "on" when currently subscribed', () => { @@ -38,7 +42,9 @@ describe('Subscriptions', function () { subscribed: true, }); - expect(vm.$refs.toggleButton.$el.querySelector('.project-feature-toggle')).toHaveClass('is-checked'); + expect(vm.$refs.toggleButton.$el.querySelector('.project-feature-toggle')).toHaveClass( + 'is-checked', + ); }); it('toggleSubscription method emits `toggleSubscription` event on eventHub and Component', () => { @@ -47,6 +53,7 @@ describe('Subscriptions', function () { spyOn(vm, '$emit'); vm.toggleSubscription(); + expect(eventHub.$emit).toHaveBeenCalledWith('toggleSubscription', jasmine.any(Object)); expect(vm.$emit).toHaveBeenCalledWith('toggleSubscription', jasmine.any(Object)); }); @@ -56,6 +63,7 @@ describe('Subscriptions', function () { spyOn(vm, '$emit'); vm.onClickCollapsedIcon(); + expect(vm.$emit).toHaveBeenCalledWith('toggleSidebar'); }); }); diff --git a/spec/javascripts/sidebar/todo_spec.js b/spec/javascripts/sidebar/todo_spec.js index a929b804a29..657e88ecb96 100644 --- a/spec/javascripts/sidebar/todo_spec.js +++ b/spec/javascripts/sidebar/todo_spec.js @@ -42,7 +42,9 @@ describe('SidebarTodo', () => { vm.collapsed = true; Vue.nextTick() .then(() => { - expect(vm.buttonClasses).toBe('btn-blank btn-todo sidebar-collapsed-icon dont-change-state'); + expect(vm.buttonClasses).toBe( + 'btn-blank btn-todo sidebar-collapsed-icon dont-change-state', + ); }) .then(done) .catch(done.fail); @@ -103,6 +105,7 @@ describe('SidebarTodo', () => { it('emits `toggleTodo` event on component', () => { spyOn(vm, '$emit'); vm.handleButtonClick(); + expect(vm.$emit).toHaveBeenCalledWith('toggleTodo'); }); }); @@ -118,16 +121,18 @@ describe('SidebarTodo', () => { container: 'body', boundary: 'viewport', }; + expect(vm.$el.nodeName).toBe('BUTTON'); const elDataAttrs = vm.$el.dataset; - Object.keys(elDataAttrs).forEach((attr) => { + Object.keys(elDataAttrs).forEach(attr => { expect(elDataAttrs[attr]).toBe(dataAttributes[attr]); }); }); it('renders button label element when `collapsed` prop is `false`', () => { const buttonLabelEl = vm.$el.querySelector('span.issuable-todo-inner'); + expect(buttonLabelEl).not.toBeNull(); expect(buttonLabelEl.innerText.trim()).toBe('Mark todo as done'); }); @@ -137,8 +142,11 @@ describe('SidebarTodo', () => { Vue.nextTick() .then(() => { const buttonIconEl = vm.$el.querySelector('svg'); + expect(buttonIconEl).not.toBeNull(); - expect(buttonIconEl.querySelector('use').getAttribute('xlink:href')).toContain('todo-done'); + expect(buttonIconEl.querySelector('use').getAttribute('xlink:href')).toContain( + 'todo-done', + ); }) .then(done) .catch(done.fail); @@ -149,6 +157,7 @@ describe('SidebarTodo', () => { Vue.nextTick() .then(() => { const loadingEl = vm.$el.querySelector('span.loading-container'); + expect(loadingEl).not.toBeNull(); }) .then(done) diff --git a/spec/javascripts/signin_tabs_memoizer_spec.js b/spec/javascripts/signin_tabs_memoizer_spec.js index 9d3905fa1d8..b688a299052 100644 --- a/spec/javascripts/signin_tabs_memoizer_spec.js +++ b/spec/javascripts/signin_tabs_memoizer_spec.js @@ -1,164 +1,170 @@ import AccessorUtilities from '~/lib/utils/accessor'; import SigninTabsMemoizer from '~/pages/sessions/new/signin_tabs_memoizer'; -(() => { - describe('SigninTabsMemoizer', () => { - const fixtureTemplate = 'static/signin_tabs.html.raw'; - const tabSelector = 'ul.new-session-tabs'; - const currentTabKey = 'current_signin_tab'; - let memo; - - function createMemoizer() { - memo = new SigninTabsMemoizer({ - currentTabKey, - tabSelector, - }); - return memo; - } +describe('SigninTabsMemoizer', () => { + const fixtureTemplate = 'static/signin_tabs.html.raw'; + const tabSelector = 'ul.new-session-tabs'; + const currentTabKey = 'current_signin_tab'; + let memo; + + function createMemoizer() { + memo = new SigninTabsMemoizer({ + currentTabKey, + tabSelector, + }); + return memo; + } - preloadFixtures(fixtureTemplate); + preloadFixtures(fixtureTemplate); - beforeEach(() => { - loadFixtures(fixtureTemplate); + beforeEach(() => { + loadFixtures(fixtureTemplate); - spyOn(AccessorUtilities, 'isLocalStorageAccessSafe').and.returnValue(true); - }); + spyOn(AccessorUtilities, 'isLocalStorageAccessSafe').and.returnValue(true); + }); - it('does nothing if no tab was previously selected', () => { - createMemoizer(); + it('does nothing if no tab was previously selected', () => { + createMemoizer(); - expect(document.querySelector(`${tabSelector} > li.active a`).getAttribute('href')).toEqual('#ldap'); - }); + expect(document.querySelector(`${tabSelector} > li.active a`).getAttribute('href')).toEqual( + '#ldap', + ); + }); - it('shows last selected tab on boot', () => { - createMemoizer().saveData('#ldap'); - const fakeTab = { - click: () => {}, - }; - spyOn(document, 'querySelector').and.returnValue(fakeTab); - spyOn(fakeTab, 'click'); + it('shows last selected tab on boot', () => { + createMemoizer().saveData('#ldap'); + const fakeTab = { + click: () => {}, + }; + spyOn(document, 'querySelector').and.returnValue(fakeTab); + spyOn(fakeTab, 'click'); - memo.bootstrap(); + memo.bootstrap(); - // verify that triggers click on the last selected tab - expect(document.querySelector).toHaveBeenCalledWith(`${tabSelector} a[href="#ldap"]`); - expect(fakeTab.click).toHaveBeenCalled(); - }); + // verify that triggers click on the last selected tab + expect(document.querySelector).toHaveBeenCalledWith(`${tabSelector} a[href="#ldap"]`); + expect(fakeTab.click).toHaveBeenCalled(); + }); - it('clicks the first tab if value in local storage is bad', () => { - createMemoizer().saveData('#bogus'); - const fakeTab = { - click: () => {}, - }; - spyOn(document, 'querySelector').and.callFake(selector => (selector === `${tabSelector} a[href="#bogus"]` ? null : fakeTab)); - spyOn(fakeTab, 'click'); + it('clicks the first tab if value in local storage is bad', () => { + createMemoizer().saveData('#bogus'); + const fakeTab = { + click: () => {}, + }; + spyOn(document, 'querySelector').and.callFake( + selector => (selector === `${tabSelector} a[href="#bogus"]` ? null : fakeTab), + ); + spyOn(fakeTab, 'click'); + + memo.bootstrap(); + + // verify that triggers click on stored selector and fallback + expect(document.querySelector.calls.allArgs()).toEqual([ + ['ul.new-session-tabs a[href="#bogus"]'], + ['ul.new-session-tabs a'], + ]); + + expect(fakeTab.click).toHaveBeenCalled(); + }); - memo.bootstrap(); + it('saves last selected tab on change', () => { + createMemoizer(); - // verify that triggers click on stored selector and fallback - expect(document.querySelector.calls.allArgs()).toEqual([['ul.new-session-tabs a[href="#bogus"]'], ['ul.new-session-tabs a']]); - expect(fakeTab.click).toHaveBeenCalled(); - }); + document.querySelector('a[href="#login-pane"]').click(); - it('saves last selected tab on change', () => { - createMemoizer(); + expect(memo.readData()).toEqual('#login-pane'); + }); - document.querySelector('a[href="#login-pane"]').click(); + it('overrides last selected tab with hash tag when given', () => { + window.location.hash = '#ldap'; + createMemoizer(); - expect(memo.readData()).toEqual('#login-pane'); - }); + expect(memo.readData()).toEqual('#ldap'); + }); - it('overrides last selected tab with hash tag when given', () => { - window.location.hash = '#ldap'; - createMemoizer(); + describe('class constructor', () => { + beforeEach(() => { + memo = createMemoizer(); + }); - expect(memo.readData()).toEqual('#ldap'); + it('should set .isLocalStorageAvailable', () => { + expect(AccessorUtilities.isLocalStorageAccessSafe).toHaveBeenCalled(); + expect(memo.isLocalStorageAvailable).toBe(true); }); + }); - describe('class constructor', () => { - beforeEach(() => { - memo = createMemoizer(); - }); + describe('saveData', () => { + beforeEach(() => { + memo = { + currentTabKey, + }; - it('should set .isLocalStorageAvailable', () => { - expect(AccessorUtilities.isLocalStorageAccessSafe).toHaveBeenCalled(); - expect(memo.isLocalStorageAvailable).toBe(true); - }); + spyOn(localStorage, 'setItem'); }); - describe('saveData', () => { - beforeEach(() => { - memo = { - currentTabKey, - }; + describe('if .isLocalStorageAvailable is `false`', () => { + beforeEach(function() { + memo.isLocalStorageAvailable = false; - spyOn(localStorage, 'setItem'); + SigninTabsMemoizer.prototype.saveData.call(memo); }); - describe('if .isLocalStorageAvailable is `false`', () => { - beforeEach(function () { - memo.isLocalStorageAvailable = false; - - SigninTabsMemoizer.prototype.saveData.call(memo); - }); - - it('should not call .setItem', () => { - expect(localStorage.setItem).not.toHaveBeenCalled(); - }); + it('should not call .setItem', () => { + expect(localStorage.setItem).not.toHaveBeenCalled(); }); + }); - describe('if .isLocalStorageAvailable is `true`', () => { - const value = 'value'; + describe('if .isLocalStorageAvailable is `true`', () => { + const value = 'value'; - beforeEach(function () { - memo.isLocalStorageAvailable = true; + beforeEach(function() { + memo.isLocalStorageAvailable = true; - SigninTabsMemoizer.prototype.saveData.call(memo, value); - }); + SigninTabsMemoizer.prototype.saveData.call(memo, value); + }); - it('should call .setItem', () => { - expect(localStorage.setItem).toHaveBeenCalledWith(currentTabKey, value); - }); + it('should call .setItem', () => { + expect(localStorage.setItem).toHaveBeenCalledWith(currentTabKey, value); }); }); + }); - describe('readData', () => { - const itemValue = 'itemValue'; - let readData; + describe('readData', () => { + const itemValue = 'itemValue'; + let readData; - beforeEach(() => { - memo = { - currentTabKey, - }; + beforeEach(() => { + memo = { + currentTabKey, + }; - spyOn(localStorage, 'getItem').and.returnValue(itemValue); - }); + spyOn(localStorage, 'getItem').and.returnValue(itemValue); + }); - describe('if .isLocalStorageAvailable is `false`', () => { - beforeEach(function () { - memo.isLocalStorageAvailable = false; + describe('if .isLocalStorageAvailable is `false`', () => { + beforeEach(function() { + memo.isLocalStorageAvailable = false; - readData = SigninTabsMemoizer.prototype.readData.call(memo); - }); + readData = SigninTabsMemoizer.prototype.readData.call(memo); + }); - it('should not call .getItem and should return `null`', () => { - expect(localStorage.getItem).not.toHaveBeenCalled(); - expect(readData).toBe(null); - }); + it('should not call .getItem and should return `null`', () => { + expect(localStorage.getItem).not.toHaveBeenCalled(); + expect(readData).toBe(null); }); + }); - describe('if .isLocalStorageAvailable is `true`', () => { - beforeEach(function () { - memo.isLocalStorageAvailable = true; + describe('if .isLocalStorageAvailable is `true`', () => { + beforeEach(function() { + memo.isLocalStorageAvailable = true; - readData = SigninTabsMemoizer.prototype.readData.call(memo); - }); + readData = SigninTabsMemoizer.prototype.readData.call(memo); + }); - it('should call .getItem and return the localStorage value', () => { - expect(window.localStorage.getItem).toHaveBeenCalledWith(currentTabKey); - expect(readData).toBe(itemValue); - }); + it('should call .getItem and return the localStorage value', () => { + expect(window.localStorage.getItem).toHaveBeenCalledWith(currentTabKey); + expect(readData).toBe(itemValue); }); }); }); -})(); +}); diff --git a/spec/javascripts/smart_interval_spec.js b/spec/javascripts/smart_interval_spec.js index d9b6dd1d487..c2c2a965e1d 100644 --- a/spec/javascripts/smart_interval_spec.js +++ b/spec/javascripts/smart_interval_spec.js @@ -3,7 +3,7 @@ import _ from 'underscore'; import SmartInterval from '~/smart_interval'; import waitForPromises from 'spec/helpers/wait_for_promises'; -describe('SmartInterval', function () { +describe('SmartInterval', function() { const DEFAULT_MAX_INTERVAL = 100; const DEFAULT_STARTING_INTERVAL = 5; const DEFAULT_SHORT_TIMEOUT = 75; @@ -35,8 +35,8 @@ describe('SmartInterval', function () { jasmine.clock().uninstall(); }); - describe('Increment Interval', function () { - it('should increment the interval delay', (done) => { + describe('Increment Interval', function() { + it('should increment the interval delay', done => { const smartInterval = createDefaultSmartInterval(); jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT); @@ -45,8 +45,8 @@ describe('SmartInterval', function () { .then(() => { const intervalConfig = smartInterval.cfg; const iterationCount = 4; - const maxIntervalAfterIterations = intervalConfig.startingInterval * - (intervalConfig.incrementByFactorOf ** iterationCount); + const maxIntervalAfterIterations = + intervalConfig.startingInterval * intervalConfig.incrementByFactorOf ** iterationCount; const currentInterval = smartInterval.getCurrentInterval(); // Provide some flexibility for performance of testing environment @@ -57,7 +57,7 @@ describe('SmartInterval', function () { .catch(done.fail); }); - it('should not increment past maxInterval', (done) => { + it('should not increment past maxInterval', done => { const smartInterval = createDefaultSmartInterval({ maxInterval: DEFAULT_STARTING_INTERVAL }); jasmine.clock().tick(DEFAULT_STARTING_INTERVAL); @@ -66,6 +66,7 @@ describe('SmartInterval', function () { waitForPromises() .then(() => { const currentInterval = smartInterval.getCurrentInterval(); + expect(currentInterval).toBe(smartInterval.cfg.maxInterval); }) .then(done) @@ -82,6 +83,7 @@ describe('SmartInterval', function () { waitForPromises() .then(() => { const oneInterval = smartInterval.cfg.startingInterval * DEFAULT_INCREMENT_FACTOR; + expect(smartInterval.getCurrentInterval()).toEqual(oneInterval); }) .then(done) @@ -89,12 +91,12 @@ describe('SmartInterval', function () { }); }); - describe('Public methods', function () { - beforeEach(function () { + describe('Public methods', function() { + beforeEach(function() { this.smartInterval = createDefaultSmartInterval(); }); - it('should cancel an interval', function (done) { + it('should cancel an interval', function(done) { const interval = this.smartInterval; jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT); @@ -114,7 +116,7 @@ describe('SmartInterval', function () { .catch(done.fail); }); - it('should resume an interval', function (done) { + it('should resume an interval', function(done) { const interval = this.smartInterval; jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT); @@ -126,6 +128,7 @@ describe('SmartInterval', function () { waitForPromises() .then(() => { const { intervalId } = interval.state; + expect(intervalId).toBeTruthy(); }) .then(done) @@ -133,15 +136,15 @@ describe('SmartInterval', function () { }); }); - describe('DOM Events', function () { - beforeEach(function () { + describe('DOM Events', function() { + beforeEach(function() { // This ensures DOM and DOM events are initialized for these specs. setFixtures('<div></div>'); this.smartInterval = createDefaultSmartInterval(); }); - it('should pause when page is not visible', function (done) { + it('should pause when page is not visible', function(done) { const interval = this.smartInterval; jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT); @@ -168,8 +171,10 @@ describe('SmartInterval', function () { waitForPromises() .then(() => { expect(interval.state.intervalId).toBeTruthy(); - expect(interval.getCurrentInterval() >= DEFAULT_STARTING_INTERVAL && - interval.getCurrentInterval() <= DEFAULT_MAX_INTERVAL).toBeTruthy(); + expect( + interval.getCurrentInterval() >= DEFAULT_STARTING_INTERVAL && + interval.getCurrentInterval() <= DEFAULT_MAX_INTERVAL, + ).toBeTruthy(); // simulates triggering of visibilitychange event interval.handleVisibilityChange({ target: { visibilityState: 'hidden' } }); @@ -181,7 +186,7 @@ describe('SmartInterval', function () { .catch(done.fail); }); - it('should resume when page is becomes visible at the previous interval', function (done) { + it('should resume when page is becomes visible at the previous interval', function(done) { const interval = this.smartInterval; jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT); @@ -204,7 +209,7 @@ describe('SmartInterval', function () { .catch(done.fail); }); - it('should cancel on page unload', function (done) { + it('should cancel on page unload', function(done) { const interval = this.smartInterval; jasmine.clock().tick(DEFAULT_SHORT_TIMEOUT); @@ -212,6 +217,7 @@ describe('SmartInterval', function () { waitForPromises() .then(() => { $(document).triggerHandler('beforeunload'); + expect(interval.state.intervalId).toBeUndefined(); expect(interval.getCurrentInterval()).toBe(interval.cfg.startingInterval); }) @@ -219,8 +225,9 @@ describe('SmartInterval', function () { .catch(done.fail); }); - it('should execute callback before first interval', function () { + it('should execute callback before first interval', function() { const interval = createDefaultSmartInterval({ immediateExecution: true }); + expect(interval.cfg.immediateExecution).toBeFalsy(); }); }); diff --git a/spec/javascripts/syntax_highlight_spec.js b/spec/javascripts/syntax_highlight_spec.js index 512be88c24c..5438368ccbe 100644 --- a/spec/javascripts/syntax_highlight_spec.js +++ b/spec/javascripts/syntax_highlight_spec.js @@ -9,36 +9,44 @@ describe('Syntax Highlighter', function() { if (window.gon == null) { window.gon = {}; } - return window.gon.user_color_scheme = value; + return (window.gon.user_color_scheme = value); }; describe('on a js-syntax-highlight element', function() { beforeEach(function() { return setFixtures('<div class="js-syntax-highlight"></div>'); }); - return it('applies syntax highlighting', function() { + + it('applies syntax highlighting', function() { stubUserColorScheme('monokai'); syntaxHighlight($('.js-syntax-highlight')); - return expect($('.js-syntax-highlight')).toHaveClass('monokai'); + + expect($('.js-syntax-highlight')).toHaveClass('monokai'); }); }); - return describe('on a parent element', function() { + + describe('on a parent element', function() { beforeEach(function() { - return setFixtures("<div class=\"parent\">\n <div class=\"js-syntax-highlight\"></div>\n <div class=\"foo\"></div>\n <div class=\"js-syntax-highlight\"></div>\n</div>"); + return setFixtures( + '<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'); syntaxHighlight($('.parent')); + expect($('.parent, .foo')).not.toHaveClass('monokai'); - return expect($('.monokai').length).toBe(2); + expect($('.monokai').length).toBe(2); }); - return it('prevents an infinite loop when no matches exist', function() { + + it('prevents an infinite loop when no matches exist', function() { var highlight; setFixtures('<div></div>'); highlight = function() { return syntaxHighlight($('div')); }; - return expect(highlight).not.toThrow(); + + expect(highlight).not.toThrow(); }); }); }); diff --git a/spec/javascripts/todos_spec.js b/spec/javascripts/todos_spec.js index e74f4bdef7e..69e43274250 100644 --- a/spec/javascripts/todos_spec.js +++ b/spec/javascripts/todos_spec.js @@ -14,10 +14,10 @@ describe('Todos', () => { }); describe('goToTodoUrl', () => { - it('opens the todo url', (done) => { + it('opens the todo url', done => { const todoLink = todoItem.dataset.url; - spyOnDependency(Todos, 'visitUrl').and.callFake((url) => { + spyOnDependency(Todos, 'visitUrl').and.callFake(url => { expect(url).toEqual(todoLink); done(); }); diff --git a/spec/javascripts/toggle_buttons_spec.js b/spec/javascripts/toggle_buttons_spec.js index 17d0b94ebe0..09756ff76ec 100644 --- a/spec/javascripts/toggle_buttons_spec.js +++ b/spec/javascripts/toggle_buttons_spec.js @@ -24,11 +24,14 @@ describe('ToggleButtons', () => { it('should initialize as checked', () => { const wrapper = setupFixture(true); - expect(wrapper.querySelector('.js-project-feature-toggle').classList.contains('is-checked')).toEqual(true); + expect( + wrapper.querySelector('.js-project-feature-toggle').classList.contains('is-checked'), + ).toEqual(true); + expect(wrapper.querySelector('.js-project-feature-toggle-input').value).toEqual('true'); }); - it('should toggle to unchecked when clicked', (done) => { + it('should toggle to unchecked when clicked', done => { const wrapper = setupFixture(true); const toggleButton = wrapper.querySelector('.js-project-feature-toggle'); @@ -48,11 +51,14 @@ describe('ToggleButtons', () => { it('should initialize as unchecked', () => { const wrapper = setupFixture(false); - expect(wrapper.querySelector('.js-project-feature-toggle').classList.contains('is-checked')).toEqual(false); + expect( + wrapper.querySelector('.js-project-feature-toggle').classList.contains('is-checked'), + ).toEqual(false); + expect(wrapper.querySelector('.js-project-feature-toggle-input').value).toEqual('false'); }); - it('should toggle to checked when clicked', (done) => { + it('should toggle to checked when clicked', done => { const wrapper = setupFixture(false); const toggleButton = wrapper.querySelector('.js-project-feature-toggle'); @@ -68,7 +74,7 @@ describe('ToggleButtons', () => { }); }); - it('should emit `trigger-change` event', (done) => { + it('should emit `trigger-change` event', done => { const changeSpy = jasmine.createSpy('changeEventHandler'); const wrapper = setupFixture(false); const toggleButton = wrapper.querySelector('.js-project-feature-toggle'); @@ -87,7 +93,7 @@ describe('ToggleButtons', () => { }); describe('clickCallback', () => { - it('should show loading indicator while waiting', (done) => { + it('should show loading indicator while waiting', done => { const isChecked = true; const clickCallback = (newValue, toggleButton) => { const input = toggleButton.querySelector('.js-project-feature-toggle-input'); diff --git a/spec/javascripts/u2f/authenticate_spec.js b/spec/javascripts/u2f/authenticate_spec.js index 57e0caa692c..ddb09811dda 100644 --- a/spec/javascripts/u2f/authenticate_spec.js +++ b/spec/javascripts/u2f/authenticate_spec.js @@ -3,7 +3,7 @@ import U2FAuthenticate from '~/u2f/authenticate'; import 'vendor/u2f'; import MockU2FDevice from './mock_u2f_device'; -describe('U2FAuthenticate', function () { +describe('U2FAuthenticate', function() { preloadFixtures('u2f/authenticate.html.raw'); beforeEach(() => { @@ -32,30 +32,40 @@ describe('U2FAuthenticate', function () { window.u2f = this.oldu2f; }); - it('falls back to normal 2fa', (done) => { - this.component.start().then(() => { - expect(this.component.switchToFallbackUI).toHaveBeenCalled(); - done(); - }).catch(done.fail); + it('falls back to normal 2fa', done => { + this.component + .start() + .then(() => { + expect(this.component.switchToFallbackUI).toHaveBeenCalled(); + done(); + }) + .catch(done.fail); }); }); describe('with u2f available', () => { - beforeEach((done) => { + beforeEach(done => { // bypass automatic form submission within renderAuthenticated spyOn(this.component, 'renderAuthenticated').and.returnValue(true); this.u2fDevice = new MockU2FDevice(); - this.component.start().then(done).catch(done.fail); + this.component + .start() + .then(done) + .catch(done.fail); }); it('allows authenticating via a U2F device', () => { const 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', }); - expect(this.component.renderAuthenticated).toHaveBeenCalledWith('{"deviceData":"this is data from the device"}'); + + expect(this.component.renderAuthenticated).toHaveBeenCalledWith( + '{"deviceData":"this is data from the device"}', + ); }); describe('errors', () => { @@ -66,7 +76,8 @@ describe('U2FAuthenticate', function () { errorCode: 'error!', }); const errorMessage = this.container.find('p'); - return expect(errorMessage.text()).toContain('There was a problem communicating with your device'); + + expect(errorMessage.text()).toContain('There was a problem communicating with your device'); }); return it('allows retrying authentication after an error', () => { let setupButton = this.container.find('#js-login-u2f-device'); @@ -81,7 +92,10 @@ describe('U2FAuthenticate', function () { this.u2fDevice.respondToAuthenticateRequest({ deviceData: 'this is data from the device', }); - expect(this.component.renderAuthenticated).toHaveBeenCalledWith('{"deviceData":"this is data from the device"}'); + + expect(this.component.renderAuthenticated).toHaveBeenCalledWith( + '{"deviceData":"this is data from the device"}', + ); }); }); }); diff --git a/spec/javascripts/u2f/mock_u2f_device.js b/spec/javascripts/u2f/mock_u2f_device.js index a8692be3546..26ddd8ade61 100644 --- a/spec/javascripts/u2f/mock_u2f_device.js +++ b/spec/javascripts/u2f/mock_u2f_device.js @@ -5,14 +5,14 @@ export default class MockU2FDevice { this.respondToAuthenticateRequest = this.respondToAuthenticateRequest.bind(this); this.respondToRegisterRequest = this.respondToRegisterRequest.bind(this); window.u2f || (window.u2f = {}); - window.u2f.register = (function (_this) { - return function (appId, registerRequests, signRequests, callback) { - return _this.registerCallback = callback; + 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; + window.u2f.sign = (function(_this) { + return function(appId, challenges, signRequests, callback) { + return (_this.authenticateCallback = callback); }; })(this); } diff --git a/spec/javascripts/u2f/register_spec.js b/spec/javascripts/u2f/register_spec.js index b774627651f..261db3d66d7 100644 --- a/spec/javascripts/u2f/register_spec.js +++ b/spec/javascripts/u2f/register_spec.js @@ -3,41 +3,48 @@ import U2FRegister from '~/u2f/register'; import 'vendor/u2f'; import MockU2FDevice from './mock_u2f_device'; -describe('U2FRegister', function () { +describe('U2FRegister', function() { preloadFixtures('u2f/register.html.raw'); - beforeEach((done) => { + beforeEach(done => { loadFixtures('u2f/register.html.raw'); this.u2fDevice = new MockU2FDevice(); this.container = $('#js-register-u2f'); this.component = new U2FRegister(this.container, $('#js-register-u2f-templates'), {}, 'token'); - this.component.start().then(done).catch(done.fail); + this.component + .start() + .then(done) + .catch(done.fail); }); it('allows registering a U2F device', () => { const setupButton = this.container.find('#js-setup-u2f-device'); + expect(setupButton.text()).toBe('Set up new U2F device'); setupButton.trigger('click'); const 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', }); const registeredMessage = this.container.find('p'); const 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"}'); + expect(deviceResponse.val()).toBe('{"deviceData":"this is data from the device"}'); }); - return describe('errors', () => { - it('doesn\'t allow the same device to be registered twice (for the same user', () => { + describe('errors', () => { + it("doesn't allow the same device to be registered twice (for the same user", () => { const setupButton = this.container.find('#js-setup-u2f-device'); setupButton.trigger('click'); this.u2fDevice.respondToRegisterRequest({ errorCode: 4, }); const errorMessage = this.container.find('p'); - return expect(errorMessage.text()).toContain('already been registered with us'); + + expect(errorMessage.text()).toContain('already been registered with us'); }); it('displays an error message for other errors', () => { @@ -47,10 +54,11 @@ describe('U2FRegister', function () { errorCode: 'error!', }); const errorMessage = this.container.find('p'); - return expect(errorMessage.text()).toContain('There was a problem communicating with your device'); + + expect(errorMessage.text()).toContain('There was a problem communicating with your device'); }); - return it('allows retrying registration after an error', () => { + it('allows retrying registration after an error', () => { let setupButton = this.container.find('#js-setup-u2f-device'); setupButton.trigger('click'); this.u2fDevice.respondToRegisterRequest({ @@ -64,7 +72,8 @@ describe('U2FRegister', function () { deviceData: 'this is data from the device', }); const registeredMessage = this.container.find('p'); - return expect(registeredMessage.text()).toContain('Your device was successfully set up!'); + + expect(registeredMessage.text()).toContain('Your device was successfully set up!'); }); }); }); diff --git a/spec/javascripts/u2f/util_spec.js b/spec/javascripts/u2f/util_spec.js index 4187183236f..32cd6891384 100644 --- a/spec/javascripts/u2f/util_spec.js +++ b/spec/javascripts/u2f/util_spec.js @@ -3,42 +3,58 @@ import { canInjectU2fApi } from '~/u2f/util'; describe('U2F Utils', () => { describe('canInjectU2fApi', () => { it('returns false for Chrome < 41', () => { - const userAgent = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.28 Safari/537.36'; + const userAgent = + 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.28 Safari/537.36'; + expect(canInjectU2fApi(userAgent)).toBe(false); }); it('returns true for Chrome >= 41', () => { - const userAgent = 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'; + const userAgent = + 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'; + expect(canInjectU2fApi(userAgent)).toBe(true); }); it('returns false for Opera < 40', () => { - const userAgent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36 OPR/32.0.1948.25'; + const userAgent = + 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36 OPR/32.0.1948.25'; + expect(canInjectU2fApi(userAgent)).toBe(false); }); it('returns true for Opera >= 40', () => { - const userAgent = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 OPR/43.0.2442.991'; + const userAgent = + 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 OPR/43.0.2442.991'; + expect(canInjectU2fApi(userAgent)).toBe(true); }); it('returns false for Safari', () => { - const userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/10.1.1 Safari/603.2.4'; + const userAgent = + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/10.1.1 Safari/603.2.4'; + expect(canInjectU2fApi(userAgent)).toBe(false); }); it('returns false for Chrome on Android', () => { - const userAgent = 'Mozilla/5.0 (Linux; Android 7.0; VS988 Build/NRD90U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3145.0 Mobile Safari/537.36'; + const userAgent = + 'Mozilla/5.0 (Linux; Android 7.0; VS988 Build/NRD90U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3145.0 Mobile Safari/537.36'; + expect(canInjectU2fApi(userAgent)).toBe(false); }); it('returns false for Chrome on iOS', () => { - const userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1'; + const userAgent = + 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1'; + expect(canInjectU2fApi(userAgent)).toBe(false); }); it('returns false for Safari on iOS', () => { - const userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A356 Safari/604.1'; + const userAgent = + 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A356 Safari/604.1'; + expect(canInjectU2fApi(userAgent)).toBe(false); }); }); diff --git a/spec/javascripts/version_check_image_spec.js b/spec/javascripts/version_check_image_spec.js index 5f963e8c11e..0e69fcc4c5f 100644 --- a/spec/javascripts/version_check_image_spec.js +++ b/spec/javascripts/version_check_image_spec.js @@ -2,17 +2,19 @@ import $ from 'jquery'; import VersionCheckImage from '~/version_check_image'; import ClassSpecHelper from './helpers/class_spec_helper'; -describe('VersionCheckImage', function () { - describe('bindErrorEvent', function () { +describe('VersionCheckImage', function() { + describe('bindErrorEvent', function() { ClassSpecHelper.itShouldBeAStaticMethod(VersionCheckImage, 'bindErrorEvent'); - beforeEach(function () { + beforeEach(function() { this.imageElement = $('<div></div>'); }); - it('registers an error event', function () { + it('registers an error event', function() { spyOn($.prototype, 'on'); - spyOn($.prototype, 'off').and.callFake(function () { return this; }); + spyOn($.prototype, 'off').and.callFake(function() { + return this; + }); VersionCheckImage.bindErrorEvent(this.imageElement); @@ -20,7 +22,7 @@ describe('VersionCheckImage', function () { expect($.prototype.on).toHaveBeenCalledWith('error', jasmine.any(Function)); }); - it('hides the imageElement on error', function () { + it('hides the imageElement on error', function() { spyOn($.prototype, 'hide'); VersionCheckImage.bindErrorEvent(this.imageElement); diff --git a/spec/javascripts/vue_mr_widget/components/deployment_spec.js b/spec/javascripts/vue_mr_widget/components/deployment_spec.js index 50c2b0e2bd0..ce850bc621e 100644 --- a/spec/javascripts/vue_mr_widget/components/deployment_spec.js +++ b/spec/javascripts/vue_mr_widget/components/deployment_spec.js @@ -14,6 +14,20 @@ const deploymentMockData = { external_url_formatted: 'diplo.', deployed_at: '2017-03-22T22:44:42.258Z', deployed_at_formatted: 'Mar 22, 2017 10:44pm', + changes: [ + { + path: 'index.html', + external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/index.html', + }, + { + path: 'imgs/gallery.html', + external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/imgs/gallery.html', + }, + { + path: 'about/', + external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/about/', + }, + ], }; const createComponent = () => { const Component = Vue.extend(deploymentComponent); @@ -39,6 +53,7 @@ describe('Deployment component', () => { describe('deployTimeago', () => { it('return formatted date', () => { const readable = getTimeago().format(deploymentMockData.deployed_at); + expect(vm.deployTimeago).toEqual(readable); }); }); @@ -101,19 +116,20 @@ describe('Deployment component', () => { describe('methods', () => { describe('stopEnvironment', () => { const url = '/foo/bar'; - const returnPromise = () => new Promise((resolve) => { - resolve({ - data: { - redirect_url: url, - }, + const returnPromise = () => + new Promise(resolve => { + resolve({ + data: { + redirect_url: url, + }, + }); }); - }); const mockStopEnvironment = () => { vm.stopEnvironment(deploymentMockData); return vm; }; - it('should show a confirm dialog and call service.stopEnvironment when confirmed', (done) => { + it('should show a confirm dialog and call service.stopEnvironment when confirmed', done => { spyOn(window, 'confirm').and.returnValue(true); spyOn(MRWidgetService, 'stopEnvironment').and.returnValue(returnPromise(true)); const visitUrl = spyOnDependency(deploymentComponent, 'visitUrl').and.returnValue(true); @@ -147,12 +163,18 @@ describe('Deployment component', () => { }); it('renders deployment name', () => { - expect(el.querySelector('.js-deploy-meta').getAttribute('href')).toEqual(deploymentMockData.url); + expect(el.querySelector('.js-deploy-meta').getAttribute('href')).toEqual( + deploymentMockData.url, + ); + expect(el.querySelector('.js-deploy-meta').innerText).toContain(deploymentMockData.name); }); it('renders external URL', () => { - expect(el.querySelector('.js-deploy-url').getAttribute('href')).toEqual(deploymentMockData.external_url); + expect(el.querySelector('.js-deploy-url').getAttribute('href')).toEqual( + deploymentMockData.external_url, + ); + expect(el.querySelector('.js-deploy-url').innerText).toContain('View app'); }); @@ -168,4 +190,42 @@ describe('Deployment component', () => { expect(el.querySelector('.js-mr-memory-usage')).not.toBeNull(); }); }); + + describe('with `features.ciEnvironmentsStatusChanges` enabled', () => { + beforeEach(() => { + window.gon = window.gon || {}; + window.gon.features = window.gon.features || {}; + window.gon.features.ciEnvironmentsStatusChanges = true; + + vm = createComponent(deploymentMockData); + }); + + afterEach(() => { + window.gon.features = {}; + }); + + it('renders dropdown with changes', () => { + expect(vm.$el.querySelector('.js-mr-wigdet-deployment-dropdown')).not.toBeNull(); + expect(vm.$el.querySelector('.js-deploy-url-feature-flag')).toBeNull(); + }); + }); + + describe('with `features.ciEnvironmentsStatusChanges` disabled', () => { + beforeEach(() => { + window.gon = window.gon || {}; + window.gon.features = window.gon.features || {}; + window.gon.features.ciEnvironmentsStatusChanges = false; + + vm = createComponent(deploymentMockData); + }); + + afterEach(() => { + delete window.gon.features.ciEnvironmentsStatusChanges; + }); + + it('renders the old link to the review app', () => { + expect(vm.$el.querySelector('.js-mr-wigdet-deployment-dropdown')).toBeNull(); + expect(vm.$el.querySelector('.js-deploy-url-feature-flag')).not.toBeNull(); + }); + }); }); diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_author_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_author_spec.js index 00f4f2d7c39..b69082082ba 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_author_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_author_spec.js @@ -13,9 +13,9 @@ describe('MrWidgetAuthor', () => { name: 'Administrator', username: 'root', webUrl: 'http://localhost:3000/root', - avatarUrl: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + avatarUrl: + 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', }, - }); }); @@ -28,9 +28,9 @@ describe('MrWidgetAuthor', () => { }); it('renders image with avatar url', () => { - expect( - vm.$el.querySelector('img').getAttribute('src'), - ).toEqual('http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon'); + expect(vm.$el.querySelector('img').getAttribute('src')).toEqual( + 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + ); }); it('renders author name', () => { diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js index 10143402acf..787f44e478d 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js @@ -14,7 +14,8 @@ describe('MrWidgetAuthorTime', () => { name: 'Administrator', username: 'root', webUrl: 'http://localhost:3000/root', - avatarUrl: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + avatarUrl: + 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', }, dateTitle: '2017-03-23T23:02:00.807Z', dateReadable: '12 hours ago', @@ -34,7 +35,10 @@ describe('MrWidgetAuthorTime', () => { }); it('renders provided time', () => { - expect(vm.$el.querySelector('time').getAttribute('data-original-title')).toEqual('2017-03-23T23:02:00.807Z'); + expect(vm.$el.querySelector('time').getAttribute('data-original-title')).toEqual( + '2017-03-23T23:02:00.807Z', + ); + expect(vm.$el.querySelector('time').textContent.trim()).toEqual('12 hours ago'); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js index 237e2fa79f2..02c476f2871 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js @@ -41,6 +41,7 @@ describe('MRWidgetHeader', () => { statusPath: 'abc', }, }); + expect(vm.shouldShowCommitsBehindText).toEqual(false); }); }); @@ -58,7 +59,9 @@ describe('MRWidgetHeader', () => { }, }); - expect(vm.commitsBehindText).toEqual('The source branch is <a href="/foo/bar/master">1 commit behind</a> the target branch'); + expect(vm.commitsBehindText).toEqual( + 'The source branch is <a href="/foo/bar/master">1 commit behind</a> the target branch', + ); }); it('returns plural when there is more than one commit', () => { @@ -73,7 +76,9 @@ describe('MRWidgetHeader', () => { }, }); - expect(vm.commitsBehindText).toEqual('The source branch is <a href="/foo/bar/master">2 commits behind</a> the target branch'); + expect(vm.commitsBehindText).toEqual( + 'The source branch is <a href="/foo/bar/master">2 commits behind</a> the target branch', + ); }); }); }); @@ -165,6 +170,7 @@ describe('MRWidgetHeader', () => { vm = mountComponent(Component, { mr }); const link = vm.$el.querySelector('.js-web-ide'); + expect(link.classList.contains('disabled')).toBe(true); expect(link.getAttribute('href')).toBeNull(); }); @@ -295,9 +301,18 @@ describe('MRWidgetHeader', () => { }); it('renders diverged commits info', () => { - expect(vm.$el.querySelector('.diverged-commits-count').textContent).toEqual('The source branch is 12 commits behind the target branch'); - expect(vm.$el.querySelector('.diverged-commits-count a').textContent).toEqual('12 commits behind'); - expect(vm.$el.querySelector('.diverged-commits-count a')).toHaveAttr('href', vm.mr.targetBranchPath); + expect(vm.$el.querySelector('.diverged-commits-count').textContent).toEqual( + 'The source branch is 12 commits behind the target branch', + ); + + expect(vm.$el.querySelector('.diverged-commits-count a').textContent).toEqual( + '12 commits behind', + ); + + expect(vm.$el.querySelector('.diverged-commits-count a')).toHaveAttr( + 'href', + vm.mr.targetBranchPath, + ); }); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js index 305cee94f57..4baaea9745a 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_memory_usage_spec.js @@ -128,13 +128,7 @@ describe('MemoryUsage', () => { describe('computeGraphData', () => { it('should populate sparkline graph', () => { vm.computeGraphData(metrics, deployment_time); - const { - hasMetrics, - memoryMetrics, - deploymentTime, - memoryFrom, - memoryTo, - } = vm; + const { hasMetrics, memoryMetrics, deploymentTime, memoryFrom, memoryTo } = vm; expect(hasMetrics).toBeTruthy(); expect(memoryMetrics.length).toBeGreaterThan(0); @@ -153,18 +147,13 @@ describe('MemoryUsage', () => { }); it('should load metrics data using MRWidgetService', done => { - spyOn(MRWidgetService, 'fetchMetrics').and.returnValue( - returnServicePromise(true), - ); + spyOn(MRWidgetService, 'fetchMetrics').and.returnValue(returnServicePromise(true)); spyOn(vm, 'computeGraphData'); vm.loadMetrics(); setTimeout(() => { expect(MRWidgetService.fetchMetrics).toHaveBeenCalledWith(url); - expect(vm.computeGraphData).toHaveBeenCalledWith( - metrics, - deployment_time, - ); + expect(vm.computeGraphData).toHaveBeenCalledWith(metrics, deployment_time); done(); }, 333); }); @@ -183,15 +172,11 @@ describe('MemoryUsage', () => { vm.loadFailed = false; Vue.nextTick(() => { - expect( - el.querySelector('.js-usage-info.usage-info-loading'), - ).toBeDefined(); - expect( - el.querySelector('.js-usage-info .usage-info-load-spinner'), - ).toBeDefined(); - expect(el.querySelector('.js-usage-info').innerText).toContain( - messages.loadingMetrics, - ); + expect(el.querySelector('.js-usage-info.usage-info-loading')).toBeDefined(); + + expect(el.querySelector('.js-usage-info .usage-info-load-spinner')).toBeDefined(); + + expect(el.querySelector('.js-usage-info').innerText).toContain(messages.loadingMetrics); done(); }); }); @@ -203,9 +188,7 @@ describe('MemoryUsage', () => { Vue.nextTick(() => { expect(el.querySelector('.memory-graph-container')).toBeDefined(); - expect(el.querySelector('.js-usage-info').innerText).toContain( - messages.hasMetrics, - ); + expect(el.querySelector('.js-usage-info').innerText).toContain(messages.hasMetrics); done(); }); }); @@ -216,12 +199,9 @@ describe('MemoryUsage', () => { vm.loadFailed = true; Vue.nextTick(() => { - expect( - el.querySelector('.js-usage-info.usage-info-failed'), - ).toBeDefined(); - expect(el.querySelector('.js-usage-info').innerText).toContain( - messages.loadFailed, - ); + expect(el.querySelector('.js-usage-info.usage-info-failed')).toBeDefined(); + + expect(el.querySelector('.js-usage-info').innerText).toContain(messages.loadFailed); done(); }); }); @@ -232,12 +212,9 @@ describe('MemoryUsage', () => { vm.loadFailed = false; Vue.nextTick(() => { - expect( - el.querySelector('.js-usage-info.usage-info-unavailable'), - ).toBeDefined(); - expect(el.querySelector('.js-usage-info').innerText).toContain( - messages.metricsUnavailable, - ); + expect(el.querySelector('.js-usage-info.usage-info-unavailable')).toBeDefined(); + + expect(el.querySelector('.js-usage-info').innerText).toContain(messages.metricsUnavailable); done(); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_merge_help_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_merge_help_spec.js index 367c499daaf..2c554f3f3ab 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_merge_help_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_merge_help_spec.js @@ -23,15 +23,23 @@ describe('MRWidgetMergeHelp', () => { it('renders missing branch information', () => { expect( - vm.$el.textContent.trim().replace(/[\r\n]+/g, ' ').replace(/\s\s+/g, ' '), + vm.$el.textContent + .trim() + .replace(/[\r\n]+/g, ' ') + .replace(/\s\s+/g, ' '), ).toEqual( 'If the this-is-not-the-branch-you-are-looking-for branch exists in your local repository, you can merge this merge request manually using the command line', ); }); it('renders button to open help modal', () => { - expect(vm.$el.querySelector('.js-open-modal-help').getAttribute('data-target')).toEqual('#modal_merge_info'); - expect(vm.$el.querySelector('.js-open-modal-help').getAttribute('data-toggle')).toEqual('modal'); + expect(vm.$el.querySelector('.js-open-modal-help').getAttribute('data-target')).toEqual( + '#modal_merge_info', + ); + + expect(vm.$el.querySelector('.js-open-modal-help').getAttribute('data-toggle')).toEqual( + 'modal', + ); }); }); @@ -42,15 +50,21 @@ describe('MRWidgetMergeHelp', () => { it('renders information about how to merge manually', () => { expect( - vm.$el.textContent.trim().replace(/[\r\n]+/g, ' ').replace(/\s\s+/g, ' '), - ).toEqual( - 'You can merge this merge request manually using the command line', - ); + vm.$el.textContent + .trim() + .replace(/[\r\n]+/g, ' ') + .replace(/\s\s+/g, ' '), + ).toEqual('You can merge this merge request manually using the command line'); }); it('renders element to open a modal', () => { - expect(vm.$el.querySelector('.js-open-modal-help').getAttribute('data-target')).toEqual('#modal_merge_info'); - expect(vm.$el.querySelector('.js-open-modal-help').getAttribute('data-toggle')).toEqual('modal'); + expect(vm.$el.querySelector('.js-open-modal-help').getAttribute('data-target')).toEqual( + '#modal_merge_info', + ); + + expect(vm.$el.querySelector('.js-open-modal-help').getAttribute('data-toggle')).toEqual( + 'modal', + ); }); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js index b453d180a40..14d6e8d7556 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js @@ -39,13 +39,16 @@ describe('Merge request widget rebase component', () => { }); it('it should render rebase button and warning message', () => { - const text = vm.$el.querySelector('.rebase-state-find-class-convention span').textContent.trim(); + const text = vm.$el + .querySelector('.rebase-state-find-class-convention span') + .textContent.trim(); + expect(text).toContain('Fast-forward merge is not possible.'); expect(text).toContain('Rebase the source branch onto the target branch or merge target'); expect(text).toContain('branch into source branch to allow this merge request to be merged.'); }); - it('it should render error message when it fails', (done) => { + it('it should render error message when it fails', done => { vm.rebasingError = 'Something went wrong!'; Vue.nextTick(() => { @@ -68,7 +71,9 @@ describe('Merge request widget rebase component', () => { service: {}, }); - const text = vm.$el.querySelector('.rebase-state-find-class-convention span').textContent.trim(); + const text = vm.$el + .querySelector('.rebase-state-find-class-convention span') + .textContent.trim(); expect(text).toContain('Fast-forward merge is not possible.'); expect(text).toContain('Rebase the source branch onto'); @@ -78,7 +83,7 @@ describe('Merge request widget rebase component', () => { }); describe('methods', () => { - it('checkRebaseStatus', (done) => { + it('checkRebaseStatus', done => { spyOn(eventHub, '$emit'); vm = mountComponent(Component, { mr: {}, diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js index 5de6ac4079d..7a5d0efdea5 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js @@ -5,7 +5,7 @@ import mountComponent from 'spec/helpers/vue_mount_component_helper'; describe('MRWidgetRelatedLinks', () => { let vm; - const createComponent = (data) => { + const createComponent = data => { const Component = Vue.extend(relatedLinksComponent); return mountComponent(Component, data); @@ -19,16 +19,19 @@ describe('MRWidgetRelatedLinks', () => { describe('closesText', () => { it('returns Closes text for open merge request', () => { vm = createComponent({ state: 'open', relatedLinks: {} }); + expect(vm.closesText).toEqual('Closes'); }); it('returns correct text for closed merge request', () => { vm = createComponent({ state: 'closed', relatedLinks: {} }); + expect(vm.closesText).toEqual('Did not close'); }); it('returns correct tense for merged request', () => { vm = createComponent({ state: 'merged', relatedLinks: {} }); + expect(vm.closesText).toEqual('Closed'); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js index 0b25500caf4..a0a336ae604 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js @@ -17,6 +17,7 @@ describe('MR widget status icon component', () => { describe('while loading', () => { it('renders loading icon', () => { vm = mountComponent(Component, { status: 'loading' }); + expect(vm.$el.querySelector('.mr-widget-icon i').classList).toContain('fa-spinner'); }); }); @@ -24,6 +25,7 @@ describe('MR widget status icon component', () => { describe('with status icon', () => { it('renders ci status icon', () => { vm = mountComponent(Component, { status: 'failed' }); + expect(vm.$el.querySelector('.js-ci-status-icon-failed')).not.toBeNull(); }); }); @@ -31,6 +33,7 @@ describe('MR widget status icon component', () => { describe('with disabled button', () => { it('renders a disabled button', () => { vm = mountComponent(Component, { status: 'failed', showDisabledButton: true }); + expect(vm.$el.querySelector('.js-disabled-merge-button').textContent.trim()).toEqual('Merge'); }); }); @@ -38,6 +41,7 @@ describe('MR widget status icon component', () => { describe('without disabled button', () => { it('does not render a disabled button', () => { vm = mountComponent(Component, { status: 'failed' }); + expect(vm.$el.querySelector('.js-disabled-merge-button')).toBeNull(); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_archived_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_archived_spec.js index e818f87b4c8..b90f5881a4d 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_archived_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_archived_spec.js @@ -24,8 +24,8 @@ describe('MRWidgetArchived', () => { }); it('renders information', () => { - expect( - vm.$el.querySelector('.bold').textContent.trim(), - ).toEqual('This project is archived, write access has been disabled'); + expect(vm.$el.querySelector('.bold').textContent.trim()).toEqual( + 'This project is archived, write access has been disabled', + ); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js index d069dc3fcc6..eb4fa0df727 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js @@ -30,7 +30,7 @@ describe('MRWidgetAutoMergeFailed', () => { expect(vm.$el.querySelector('button').textContent.trim()).toEqual('Refresh'); }); - it('emits event and shows loading icon when button is clicked', (done) => { + it('emits event and shows loading icon when button is clicked', done => { spyOn(eventHub, '$emit'); vm.$el.querySelector('button').click(); @@ -38,9 +38,7 @@ describe('MRWidgetAutoMergeFailed', () => { Vue.nextTick(() => { expect(vm.$el.querySelector('button').getAttribute('disabled')).toEqual('disabled'); - expect( - vm.$el.querySelector('button i').classList, - ).toContain('fa-spinner'); + expect(vm.$el.querySelector('button i').classList).toContain('fa-spinner'); done(); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js index 658612aad3c..7da27bb8890 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js @@ -24,6 +24,8 @@ describe('MRWidgetChecking', () => { }); it('renders information about merging', () => { - expect(vm.$el.querySelector('.media-body').textContent.trim()).toEqual('Checking ability to merge automatically'); + expect(vm.$el.querySelector('.media-body').textContent.trim()).toEqual( + 'Checking ability to merge automatically', + ); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js index 0e3c134d3ac..9523e7d5474 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js @@ -7,23 +7,26 @@ describe('MRWidgetClosed', () => { beforeEach(() => { const Component = Vue.extend(closedComponent); - vm = mountComponent(Component, { mr: { - metrics: { - mergedBy: {}, - closedBy: { - name: 'Administrator', - username: 'root', - webUrl: 'http://localhost:3000/root', - avatarUrl: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + vm = mountComponent(Component, { + mr: { + metrics: { + mergedBy: {}, + closedBy: { + name: 'Administrator', + username: 'root', + webUrl: 'http://localhost:3000/root', + avatarUrl: + 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + }, + mergedAt: 'Jan 24, 2018 1:02pm GMT+0000', + closedAt: 'Jan 24, 2018 1:02pm GMT+0000', + readableMergedAt: '', + readableClosedAt: 'less than a minute ago', }, - mergedAt: 'Jan 24, 2018 1:02pm GMT+0000', - closedAt: 'Jan 24, 2018 1:02pm GMT+0000', - readableMergedAt: '', - readableClosedAt: 'less than a minute ago', + targetBranchPath: '/twitter/flight/commits/so_long_jquery', + targetBranch: 'so_long_jquery', }, - targetBranchPath: '/twitter/flight/commits/so_long_jquery', - targetBranch: 'so_long_jquery', - } }); + }); }); afterEach(() => { @@ -36,23 +39,31 @@ describe('MRWidgetClosed', () => { it('renders closed by information with author and time', () => { expect( - vm.$el.querySelector('.js-mr-widget-author').textContent.trim().replace(/\s\s+/g, ' '), - ).toContain( - 'Closed by Administrator less than a minute ago', - ); + vm.$el + .querySelector('.js-mr-widget-author') + .textContent.trim() + .replace(/\s\s+/g, ' '), + ).toContain('Closed by Administrator less than a minute ago'); }); it('links to the user that closed the MR', () => { - expect(vm.$el.querySelector('.author-link').getAttribute('href')).toEqual('http://localhost:3000/root'); + expect(vm.$el.querySelector('.author-link').getAttribute('href')).toEqual( + 'http://localhost:3000/root', + ); }); it('renders information about the changes not being merged', () => { expect( - vm.$el.querySelector('.mr-info-list').textContent.trim().replace(/\s\s+/g, ' '), + vm.$el + .querySelector('.mr-info-list') + .textContent.trim() + .replace(/\s\s+/g, ' '), ).toContain('The changes were not merged into so_long_jquery'); }); it('renders link for target branch', () => { - expect(vm.$el.querySelector('.label-branch').getAttribute('href')).toEqual('/twitter/flight/commits/so_long_jquery'); + expect(vm.$el.querySelector('.label-branch').getAttribute('href')).toEqual( + '/twitter/flight/commits/so_long_jquery', + ); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js index 3d05dbfa305..f9cd5c8bd3c 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js @@ -59,7 +59,9 @@ describe('MRWidgetConflicts', () => { }); it('should show proper message', () => { - expect(vm.$el.textContent.trim().replace(/\s\s+/g, ' ')).toContain('ask someone with write access'); + expect(vm.$el.textContent.trim().replace(/\s\s+/g, ' ')).toContain( + 'ask someone with write access', + ); }); it('should not have action buttons', () => { @@ -79,9 +81,9 @@ describe('MRWidgetConflicts', () => { }); it('should tell you to rebase locally', () => { - expect( - removeBreakLine(vm.$el.textContent).trim(), - ).toContain('Fast-forward merge is not possible. To merge this request, first rebase locally.'); + expect(removeBreakLine(vm.$el.textContent).trim()).toContain( + 'Fast-forward merge is not possible. To merge this request, first rebase locally.', + ); }); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js index 8de99fd3c96..3229ddd5e27 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js @@ -43,6 +43,7 @@ describe('MRWidgetFailedToMerge', () => { expect(vm.timerText).toEqual('Refreshing in 10 seconds to show the updated status...'); vm.timer = 1; + expect(vm.timerText).toEqual('Refreshing in a second to show the updated status...'); }); }); @@ -73,6 +74,7 @@ describe('MRWidgetFailedToMerge', () => { expect(vm.isRefreshing).toEqual(false); vm.refresh(); + expect(vm.isRefreshing).toEqual(true); expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested'); expect(eventHub.$emit).toHaveBeenCalledWith('EnablePolling'); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js index d47815a5b5a..d46ad0acc9b 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js @@ -42,22 +42,27 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => { it('should return false when user id is not the same with who set the MWPS', () => { vm.mr.mergeUserId = 2; + expect(vm.canRemoveSourceBranch).toBeFalsy(); vm.mr.currentUserId = 2; + expect(vm.canRemoveSourceBranch).toBeTruthy(); vm.mr.currentUserId = 3; + expect(vm.canRemoveSourceBranch).toBeFalsy(); }); it('should return false when shouldRemoveSourceBranch set to false', () => { vm.mr.shouldRemoveSourceBranch = true; + expect(vm.canRemoveSourceBranch).toBeFalsy(); }); it('should return false if user is not able to remove the source branch', () => { vm.mr.canRemoveSourceBranch = false; + expect(vm.canRemoveSourceBranch).toBeFalsy(); }); }); @@ -65,15 +70,17 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => { describe('methods', () => { describe('cancelAutomaticMerge', () => { - it('should set flag and call service then tell main component to update the widget with data', (done) => { + it('should set flag and call service then tell main component to update the widget with data', done => { const mrObj = { is_new_mr_data: true, }; - spyOn(vm.service, 'cancelAutomaticMerge').and.returnValue(new Promise((resolve) => { - resolve({ - data: mrObj, - }); - })); + spyOn(vm.service, 'cancelAutomaticMerge').and.returnValue( + new Promise(resolve => { + resolve({ + data: mrObj, + }); + }), + ); vm.cancelAutomaticMerge(); setTimeout(() => { @@ -85,12 +92,14 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => { }); describe('removeSourceBranch', () => { - it('should set flag and call service then request main component to update the widget', (done) => { - spyOn(vm.service, 'merge').and.returnValue(Promise.resolve({ - data: { - status: 'merge_when_pipeline_succeeds', - }, - })); + it('should set flag and call service then request main component to update the widget', done => { + spyOn(vm.service, 'merge').and.returnValue( + Promise.resolve({ + data: { + status: 'merge_when_pipeline_succeeds', + }, + }), + ); vm.removeSourceBranch(); setTimeout(() => { @@ -113,13 +122,19 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => { expect(vm.$el.innerText).toContain('The changes will be merged into'); expect(vm.$el.innerText).toContain(targetBranch); expect(vm.$el.innerText).toContain('The source branch will not be removed'); - expect(vm.$el.querySelector('.js-cancel-auto-merge').innerText).toContain('Cancel automatic merge'); + expect(vm.$el.querySelector('.js-cancel-auto-merge').innerText).toContain( + 'Cancel automatic merge', + ); + expect(vm.$el.querySelector('.js-cancel-auto-merge').getAttribute('disabled')).toBeFalsy(); - expect(vm.$el.querySelector('.js-remove-source-branch').innerText).toContain('Remove source branch'); + expect(vm.$el.querySelector('.js-remove-source-branch').innerText).toContain( + 'Remove source branch', + ); + expect(vm.$el.querySelector('.js-remove-source-branch').getAttribute('disabled')).toBeFalsy(); }); - it('should disable cancel auto merge button when the action is in progress', (done) => { + it('should disable cancel auto merge button when the action is in progress', done => { vm.isCancellingAutoMerge = true; Vue.nextTick(() => { @@ -128,18 +143,19 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => { }); }); - it('should show source branch will be removed text when it source branch set to remove', (done) => { + it('should show source branch will be removed text when it source branch set to remove', done => { vm.mr.shouldRemoveSourceBranch = true; Vue.nextTick(() => { const normalizedText = vm.$el.innerText.replace(/\s+/g, ' '); + expect(normalizedText).toContain('The source branch will be removed'); expect(normalizedText).not.toContain('The source branch will not be removed'); done(); }); }); - it('should not show remove source branch button when user not able to remove source branch', (done) => { + it('should not show remove source branch button when user not able to remove source branch', done => { vm.mr.currentUserId = 4; Vue.nextTick(() => { @@ -148,11 +164,13 @@ describe('MRWidgetMergeWhenPipelineSucceeds', () => { }); }); - it('should disable remove source branch button when the action is in progress', (done) => { + it('should disable remove source branch button when the action is in progress', done => { vm.isRemovingSourceBranch = true; Vue.nextTick(() => { - expect(vm.$el.querySelector('.js-remove-source-branch').getAttribute('disabled')).toBeTruthy(); + expect( + vm.$el.querySelector('.js-remove-source-branch').getAttribute('disabled'), + ).toBeTruthy(); done(); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js index 033cb694249..d68342635ef 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js @@ -30,7 +30,8 @@ describe('MRWidgetMerged', () => { name: 'Administrator', username: 'root', webUrl: 'http://localhost:3000/root', - avatarUrl: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', + avatarUrl: + 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', }, mergedAt: 'Jan 24, 2018 1:02pm GMT+0000', readableMergedAt: '', @@ -41,7 +42,8 @@ describe('MRWidgetMerged', () => { updatedAt: 'mergedUpdatedAt', shortMergeCommitSha: '958c0475', mergeCommitSha: '958c047516e182dfc52317f721f696e8a1ee85ed', - mergeCommitPath: 'http://localhost:3000/root/nautilus/commit/f7ce827c314c9340b075657fd61c789fb01cf74d', + mergeCommitPath: + 'http://localhost:3000/root/nautilus/commit/f7ce827c314c9340b075657fd61c789fb01cf74d', sourceBranch: 'bar', targetBranch, }; @@ -63,23 +65,27 @@ describe('MRWidgetMerged', () => { describe('shouldShowRemoveSourceBranch', () => { it('returns true when sourceBranchRemoved is false', () => { vm.mr.sourceBranchRemoved = false; + expect(vm.shouldShowRemoveSourceBranch).toEqual(true); }); it('returns false wehn sourceBranchRemoved is true', () => { vm.mr.sourceBranchRemoved = true; + expect(vm.shouldShowRemoveSourceBranch).toEqual(false); }); it('returns false when canRemoveSourceBranch is false', () => { vm.mr.sourceBranchRemoved = false; vm.mr.canRemoveSourceBranch = false; + expect(vm.shouldShowRemoveSourceBranch).toEqual(false); }); it('returns false when is making request', () => { vm.mr.canRemoveSourceBranch = true; vm.isMakingRequest = true; + expect(vm.shouldShowRemoveSourceBranch).toEqual(false); }); @@ -87,6 +93,7 @@ describe('MRWidgetMerged', () => { vm.mr.isRemovingSourceBranch = true; vm.mr.canRemoveSourceBranch = true; vm.isMakingRequest = true; + expect(vm.shouldShowRemoveSourceBranch).toEqual(false); }); }); @@ -94,17 +101,21 @@ describe('MRWidgetMerged', () => { describe('shouldShowSourceBranchRemoving', () => { it('should correct value when fields changed', () => { vm.mr.sourceBranchRemoved = false; + expect(vm.shouldShowSourceBranchRemoving).toEqual(false); vm.mr.sourceBranchRemoved = true; + expect(vm.shouldShowRemoveSourceBranch).toEqual(false); vm.mr.sourceBranchRemoved = false; vm.isMakingRequest = true; + expect(vm.shouldShowSourceBranchRemoving).toEqual(true); vm.isMakingRequest = false; vm.mr.isRemovingSourceBranch = true; + expect(vm.shouldShowSourceBranchRemoving).toEqual(true); }); }); @@ -112,18 +123,21 @@ describe('MRWidgetMerged', () => { describe('methods', () => { describe('removeSourceBranch', () => { - it('should set flag and call service then request main component to update the widget', (done) => { - spyOn(vm.service, 'removeSourceBranch').and.returnValue(new Promise((resolve) => { - resolve({ - data: { - message: 'Branch was removed', - }, - }); - })); + it('should set flag and call service then request main component to update the widget', done => { + spyOn(vm.service, 'removeSourceBranch').and.returnValue( + new Promise(resolve => { + resolve({ + data: { + message: 'Branch was removed', + }, + }); + }), + ); vm.removeSourceBranch(); setTimeout(() => { const args = eventHub.$emit.calls.argsFor(0); + expect(vm.isMakingRequest).toEqual(true); expect(args[0]).toEqual('MRWidgetUpdateRequested'); expect(args[1]).not.toThrow(); @@ -154,10 +168,12 @@ describe('MRWidgetMerged', () => { it('shows button to copy commit SHA to clipboard', () => { expect(selectors.copyMergeShaButton).toExist(); - expect(selectors.copyMergeShaButton.getAttribute('data-clipboard-text')).toBe(vm.mr.mergeCommitSha); + expect(selectors.copyMergeShaButton.getAttribute('data-clipboard-text')).toBe( + vm.mr.mergeCommitSha, + ); }); - it('hides button to copy commit SHA if SHA does not exist', (done) => { + it('hides button to copy commit SHA if SHA does not exist', done => { vm.mr.mergeCommitSha = null; Vue.nextTick(() => { @@ -173,7 +189,7 @@ describe('MRWidgetMerged', () => { expect(selectors.mergeCommitShaLink.href).toBe(vm.mr.mergeCommitPath); }); - it('should not show source branch removed text', (done) => { + it('should not show source branch removed text', done => { vm.mr.sourceBranchRemoved = false; Vue.nextTick(() => { @@ -183,7 +199,7 @@ describe('MRWidgetMerged', () => { }); }); - it('should show source branch removing text', (done) => { + it('should show source branch removing text', done => { vm.mr.isRemovingSourceBranch = true; vm.mr.sourceBranchRemoved = false; @@ -196,8 +212,8 @@ describe('MRWidgetMerged', () => { }); it('should use mergedEvent mergedAt as tooltip title', () => { - expect( - vm.$el.querySelector('time').getAttribute('data-original-title'), - ).toBe('Jan 24, 2018 1:02pm GMT+0000'); + expect(vm.$el.querySelector('time').getAttribute('data-original-title')).toBe( + 'Jan 24, 2018 1:02pm GMT+0000', + ); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merging_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merging_spec.js index d2d219e4bdb..57773d1648a 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merging_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merging_spec.js @@ -7,10 +7,12 @@ describe('MRWidgetMerging', () => { beforeEach(() => { const Component = Vue.extend(mergingComponent); - vm = mountComponent(Component, { mr: { - targetBranchPath: '/branch-path', - targetBranch: 'branch', - } }); + vm = mountComponent(Component, { + mr: { + targetBranchPath: '/branch-path', + targetBranch: 'branch', + }, + }); }); afterEach(() => { @@ -19,16 +21,23 @@ describe('MRWidgetMerging', () => { it('renders information about merge request being merged', () => { expect( - vm.$el.querySelector('.media-body').textContent.trim().replace(/\s\s+/g, ' ').replace(/[\r\n]+/g, ' '), + vm.$el + .querySelector('.media-body') + .textContent.trim() + .replace(/\s\s+/g, ' ') + .replace(/[\r\n]+/g, ' '), ).toContain('This merge request is in the process of being merged'); }); it('renders branch information', () => { expect( - vm.$el.querySelector('.mr-info-list').textContent.trim().replace(/\s\s+/g, ' ').replace(/[\r\n]+/g, ' '), + vm.$el + .querySelector('.mr-info-list') + .textContent.trim() + .replace(/\s\s+/g, ' ') + .replace(/[\r\n]+/g, ' '), ).toEqual('The changes will be merged into branch'); - expect( - vm.$el.querySelector('a').getAttribute('href'), - ).toEqual('/branch-path'); + + expect(vm.$el.querySelector('a').getAttribute('href')).toEqual('/branch-path'); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js index 34f76b39b28..096301837c4 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js @@ -20,6 +20,7 @@ describe('MRWidgetMissingBranch', () => { expect(vm.missingBranchName).toEqual('source'); vm.mr.sourceBranchRemoved = false; + expect(vm.missingBranchName).toEqual('target'); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_not_allowed_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_not_allowed_spec.js index 9f8b96c118b..6b95ca3460b 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_not_allowed_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_not_allowed_spec.js @@ -19,6 +19,8 @@ describe('MRWidgetNotAllowed', () => { it('renders informative text', () => { expect(vm.$el.innerText).toContain('Ready to be merged automatically.'); - expect(vm.$el.innerText).toContain('Ask someone with write access to this repository to merge this request'); + expect(vm.$el.innerText).toContain( + 'Ask someone with write access to this repository to merge this request', + ); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_nothing_to_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_nothing_to_merge_spec.js index 2a762c9336e..babb8cea0ab 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_nothing_to_merge_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_nothing_to_merge_spec.js @@ -15,7 +15,10 @@ describe('NothingToMerge', () => { it('should have correct elements', () => { expect(vm.$el.classList.contains('mr-widget-body')).toBeTruthy(); expect(vm.$el.querySelector('a').href).toContain(newBlobPath); - expect(vm.$el.innerText).toContain('Currently there are no changes in this merge request\'s source branch'); + expect(vm.$el.innerText).toContain( + "Currently there are no changes in this merge request's source branch", + ); + expect(vm.$el.innerText).toContain('Please push new commits or use a different branch.'); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_blocked_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_blocked_spec.js index ab096a56918..477041fa383 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_blocked_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_blocked_spec.js @@ -19,8 +19,8 @@ describe('MRWidgetPipelineBlocked', () => { }); it('renders information text', () => { - expect( - removeBreakLine(vm.$el.textContent).trim(), - ).toContain('Pipeline blocked. The pipeline for this merge request requires a manual action to proceed'); + expect(removeBreakLine(vm.$el.textContent).trim()).toContain( + 'Pipeline blocked. The pipeline for this merge request requires a manual action to proceed', + ); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_failed_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_failed_spec.js index 5573d7c5c93..f7523a01963 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_failed_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_failed_spec.js @@ -11,9 +11,9 @@ describe('PipelineFailed', () => { it('should have correct elements', () => { expect(vm.$el.classList.contains('mr-widget-body')).toBeTruthy(); expect(vm.$el.querySelector('button').getAttribute('disabled')).toBeTruthy(); - expect( - removeBreakLine(vm.$el.innerText).trim(), - ).toContain('The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure'); + expect(removeBreakLine(vm.$el.innerText).trim()).toContain( + 'The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure', + ); }); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js index 81c16593eb4..2119a3b927a 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js @@ -76,11 +76,13 @@ describe('ReadyToMerge', () => { describe('shouldShowMergeWhenPipelineSucceedsText', () => { it('should return true with active pipeline', () => { vm.mr.isPipelineActive = true; + expect(vm.shouldShowMergeWhenPipelineSucceedsText).toBeTruthy(); }); it('should return false with inactive pipeline', () => { vm.mr.isPipelineActive = false; + expect(vm.shouldShowMergeWhenPipelineSucceedsText).toBeFalsy(); }); }); @@ -95,6 +97,7 @@ describe('ReadyToMerge', () => { it('should return message without description', () => { vm.useCommitMessageWithDescription = true; + expect(vm.commitMessageLinkTitle).toEqual(withoutDesc); }); }); @@ -102,11 +105,13 @@ describe('ReadyToMerge', () => { describe('status', () => { it('defaults to success', () => { vm.mr.pipeline = true; + expect(vm.status).toEqual('success'); }); it('returns failed when MR has CI but also has an unknown status', () => { vm.mr.hasCI = true; + expect(vm.status).toEqual('failed'); }); @@ -117,12 +122,14 @@ describe('ReadyToMerge', () => { it('returns pending when pipeline is active', () => { vm.mr.pipeline = {}; vm.mr.isPipelineActive = true; + expect(vm.status).toEqual('pending'); }); it('returns failed when pipeline is failed', () => { vm.mr.pipeline = {}; vm.mr.isPipelineFailed = true; + expect(vm.status).toEqual('failed'); }); }); @@ -138,17 +145,20 @@ describe('ReadyToMerge', () => { it('returns success class for success status', () => { vm.mr.pipeline = true; + expect(vm.mergeButtonClass).toEqual(defaultClass); }); it('returns info class for pending status', () => { vm.mr.pipeline = {}; vm.mr.isPipelineActive = true; + expect(vm.mergeButtonClass).toEqual(inActionClass); }); it('returns failed class for failed status', () => { vm.mr.hasCI = true; + expect(vm.mergeButtonClass).toEqual(failedClass); }); }); @@ -160,22 +170,26 @@ describe('ReadyToMerge', () => { it('shows tick for success status', () => { vm.mr.pipeline = true; + expect(vm.iconClass).toEqual('success'); }); it('shows tick for pending status', () => { vm.mr.pipeline = {}; vm.mr.isPipelineActive = true; + expect(vm.iconClass).toEqual('success'); }); it('shows warning icon for failed status', () => { vm.mr.hasCI = true; + expect(vm.iconClass).toEqual('warning'); }); it('shows warning icon for merge not allowed', () => { vm.mr.hasCI = true; + expect(vm.iconClass).toEqual('warning'); }); }); @@ -187,12 +201,14 @@ describe('ReadyToMerge', () => { it('should return Merge in progress', () => { vm.isMergingImmediately = true; + expect(vm.mergeButtonText).toEqual('Merge in progress'); }); it('should return Merge when pipeline succeeds', () => { vm.isMergingImmediately = false; vm.mr.isPipelineActive = true; + expect(vm.mergeButtonText).toEqual('Merge when pipeline succeeds'); }); }); @@ -204,12 +220,14 @@ describe('ReadyToMerge', () => { it('should return true when pipeline active', () => { vm.mr.isPipelineActive = true; + expect(vm.shouldShowMergeOptionsDropdown).toBeTruthy(); }); it('should return false when pipeline active but only merge when pipeline succeeds set in project options', () => { vm.mr.isPipelineActive = true; vm.mr.onlyAllowMergeIfPipelineSucceeds = true; + expect(vm.shouldShowMergeOptionsDropdown).toBeFalsy(); }); }); @@ -217,24 +235,28 @@ describe('ReadyToMerge', () => { describe('isMergeButtonDisabled', () => { it('should return false with initial data', () => { vm.mr.isMergeAllowed = true; + expect(vm.isMergeButtonDisabled).toBeFalsy(); }); it('should return true when there is no commit message', () => { vm.mr.isMergeAllowed = true; vm.commitMessage = ''; + expect(vm.isMergeButtonDisabled).toBeTruthy(); }); it('should return true if merge is not allowed', () => { vm.mr.isMergeAllowed = false; vm.mr.onlyAllowMergeIfPipelineSucceeds = true; + expect(vm.isMergeButtonDisabled).toBeTruthy(); }); it('should return true when the vm instance is making request', () => { vm.mr.isMergeAllowed = true; vm.isMakingRequest = true; + expect(vm.isMergeButtonDisabled).toBeTruthy(); }); }); @@ -245,24 +267,28 @@ describe('ReadyToMerge', () => { it('should return false when an external pipeline is running and required to succeed', () => { vm.mr.isMergeAllowed = false; vm.mr.isPipelineActive = false; + expect(vm.shouldShowMergeControls()).toBeFalsy(); }); it('should return true when the build succeeded or build not required to succeed', () => { vm.mr.isMergeAllowed = true; vm.mr.isPipelineActive = false; + expect(vm.shouldShowMergeControls()).toBeTruthy(); }); it('should return true when showing the MWPS button and a pipeline is running that needs to be successful', () => { vm.mr.isMergeAllowed = false; vm.mr.isPipelineActive = true; + expect(vm.shouldShowMergeControls()).toBeTruthy(); }); it('should return true when showing the MWPS button but not required for the pipeline to succeed', () => { vm.mr.isMergeAllowed = true; vm.mr.isPipelineActive = true; + expect(vm.shouldShowMergeControls()).toBeTruthy(); }); }); @@ -272,9 +298,11 @@ describe('ReadyToMerge', () => { expect(vm.useCommitMessageWithDescription).toBeFalsy(); expect(vm.commitMessage).toEqual(commitMessage); vm.updateCommitMessage(); + expect(vm.useCommitMessageWithDescription).toBeTruthy(); expect(vm.commitMessage).toEqual(commitMessageWithDescription); vm.updateCommitMessage(); + expect(vm.useCommitMessageWithDescription).toBeFalsy(); expect(vm.commitMessage).toEqual(commitMessage); }); @@ -284,20 +312,22 @@ describe('ReadyToMerge', () => { it('should toggle showCommitMessageEditor flag', () => { expect(vm.showCommitMessageEditor).toBeFalsy(); vm.toggleCommitMessageEditor(); + expect(vm.showCommitMessageEditor).toBeTruthy(); }); }); describe('handleMergeButtonClick', () => { - const returnPromise = status => new Promise((resolve) => { - resolve({ - data: { - status, - }, + const returnPromise = status => + new Promise(resolve => { + resolve({ + data: { + status, + }, + }); }); - }); - it('should handle merge when pipeline succeeds', (done) => { + it('should handle merge when pipeline succeeds', done => { spyOn(eventHub, '$emit'); spyOn(vm.service, 'merge').and.returnValue(returnPromise('merge_when_pipeline_succeeds')); vm.removeSourceBranch = false; @@ -309,6 +339,7 @@ describe('ReadyToMerge', () => { expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested'); const params = vm.service.merge.calls.argsFor(0)[0]; + expect(params.sha).toEqual(vm.mr.sha); expect(params.commit_message).toEqual(vm.mr.commitMessage); expect(params.should_remove_source_branch).toBeFalsy(); @@ -317,7 +348,7 @@ describe('ReadyToMerge', () => { }, 333); }); - it('should handle merge failed', (done) => { + it('should handle merge failed', done => { spyOn(eventHub, '$emit'); spyOn(vm.service, 'merge').and.returnValue(returnPromise('failed')); vm.handleMergeButtonClick(false, true); @@ -328,13 +359,14 @@ describe('ReadyToMerge', () => { expect(eventHub.$emit).toHaveBeenCalledWith('FailedToMerge', undefined); const params = vm.service.merge.calls.argsFor(0)[0]; + expect(params.should_remove_source_branch).toBeTruthy(); expect(params.merge_when_pipeline_succeeds).toBeFalsy(); done(); }, 333); }); - it('should handle merge action accepted case', (done) => { + it('should handle merge action accepted case', done => { spyOn(vm.service, 'merge').and.returnValue(returnPromise('success')); spyOn(vm, 'initiateMergePolling'); vm.handleMergeButtonClick(); @@ -345,6 +377,7 @@ describe('ReadyToMerge', () => { expect(vm.initiateMergePolling).toHaveBeenCalled(); const params = vm.service.merge.calls.argsFor(0)[0]; + expect(params.should_remove_source_branch).toBeTruthy(); expect(params.merge_when_pipeline_succeeds).toBeFalsy(); done(); @@ -356,25 +389,27 @@ describe('ReadyToMerge', () => { it('should call simplePoll', () => { const simplePoll = spyOnDependency(ReadyToMerge, 'simplePoll'); vm.initiateMergePolling(); + expect(simplePoll).toHaveBeenCalled(); }); }); describe('handleMergePolling', () => { - const returnPromise = state => new Promise((resolve) => { - resolve({ - data: { - state, - source_branch_exists: true, - }, + const returnPromise = state => + new Promise(resolve => { + resolve({ + data: { + state, + source_branch_exists: true, + }, + }); }); - }); beforeEach(() => { loadFixtures('merge_requests/merge_request_of_current_user.html.raw'); }); - it('should call start and stop polling when MR merged', (done) => { + it('should call start and stop polling when MR merged', done => { spyOn(eventHub, '$emit'); spyOn(vm.service, 'poll').and.returnValue(returnPromise('merged')); spyOn(vm, 'initiateRemoveSourceBranchPolling'); @@ -382,7 +417,14 @@ describe('ReadyToMerge', () => { let cpc = false; // continuePollingCalled let spc = false; // stopPollingCalled - vm.handleMergePolling(() => { cpc = true; }, () => { spc = true; }); + vm.handleMergePolling( + () => { + cpc = true; + }, + () => { + spc = true; + }, + ); setTimeout(() => { expect(vm.service.poll).toHaveBeenCalled(); expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested'); @@ -395,7 +437,7 @@ describe('ReadyToMerge', () => { }, 333); }); - it('updates status box', (done) => { + it('updates status box', done => { spyOn(vm.service, 'poll').and.returnValue(returnPromise('merged')); spyOn(vm, 'initiateRemoveSourceBranchPolling'); @@ -403,6 +445,7 @@ describe('ReadyToMerge', () => { setTimeout(() => { const statusBox = document.querySelector('.status-box'); + expect(statusBox.classList.contains('status-box-mr-merged')).toBeTruthy(); expect(statusBox.textContent).toContain('Merged'); @@ -410,7 +453,7 @@ describe('ReadyToMerge', () => { }); }); - it('hides close button', (done) => { + it('hides close button', done => { spyOn(vm.service, 'poll').and.returnValue(returnPromise('merged')); spyOn(vm, 'initiateRemoveSourceBranchPolling'); @@ -423,7 +466,7 @@ describe('ReadyToMerge', () => { }); }); - it('updates merge request count badge', (done) => { + it('updates merge request count badge', done => { spyOn(vm.service, 'poll').and.returnValue(returnPromise('merged')); spyOn(vm, 'initiateRemoveSourceBranchPolling'); @@ -436,14 +479,21 @@ describe('ReadyToMerge', () => { }); }); - it('should continue polling until MR is merged', (done) => { + it('should continue polling until MR is merged', done => { spyOn(vm.service, 'poll').and.returnValue(returnPromise('some_other_state')); spyOn(vm, 'initiateRemoveSourceBranchPolling'); let cpc = false; // continuePollingCalled let spc = false; // stopPollingCalled - vm.handleMergePolling(() => { cpc = true; }, () => { spc = true; }); + vm.handleMergePolling( + () => { + cpc = true; + }, + () => { + spc = true; + }, + ); setTimeout(() => { expect(cpc).toBeTruthy(); expect(spc).toBeFalsy(); @@ -459,35 +509,46 @@ describe('ReadyToMerge', () => { const simplePoll = spyOnDependency(ReadyToMerge, 'simplePoll'); vm.initiateRemoveSourceBranchPolling(); + expect(eventHub.$emit).toHaveBeenCalledWith('SetBranchRemoveFlag', [true]); expect(simplePoll).toHaveBeenCalled(); }); }); describe('handleRemoveBranchPolling', () => { - const returnPromise = state => new Promise((resolve) => { - resolve({ - data: { - source_branch_exists: state, - }, + const returnPromise = state => + new Promise(resolve => { + resolve({ + data: { + source_branch_exists: state, + }, + }); }); - }); - it('should call start and stop polling when MR merged', (done) => { + it('should call start and stop polling when MR merged', done => { spyOn(eventHub, '$emit'); spyOn(vm.service, 'poll').and.returnValue(returnPromise(false)); let cpc = false; // continuePollingCalled let spc = false; // stopPollingCalled - vm.handleRemoveBranchPolling(() => { cpc = true; }, () => { spc = true; }); + vm.handleRemoveBranchPolling( + () => { + cpc = true; + }, + () => { + spc = true; + }, + ); setTimeout(() => { expect(vm.service.poll).toHaveBeenCalled(); const args = eventHub.$emit.calls.argsFor(0); + expect(args[0]).toEqual('MRWidgetUpdateRequested'); expect(args[1]).toBeDefined(); args[1](); + expect(eventHub.$emit).toHaveBeenCalledWith('SetBranchRemoveFlag', [false]); expect(cpc).toBeFalsy(); @@ -497,13 +558,20 @@ describe('ReadyToMerge', () => { }, 333); }); - it('should continue polling until MR is merged', (done) => { + it('should continue polling until MR is merged', done => { spyOn(vm.service, 'poll').and.returnValue(returnPromise(true)); let cpc = false; // continuePollingCalled let spc = false; // stopPollingCalled - vm.handleRemoveBranchPolling(() => { cpc = true; }, () => { spc = true; }); + vm.handleRemoveBranchPolling( + () => { + cpc = true; + }, + () => { + spc = true; + }, + ); setTimeout(() => { expect(cpc).toBeTruthy(); expect(spc).toBeFalsy(); @@ -518,6 +586,7 @@ describe('ReadyToMerge', () => { describe('when user can merge but cannot delete branch', () => { it('should be disabled in the rendered output', () => { const checkboxElement = vm.$el.querySelector('#remove-source-branch-input'); + expect(checkboxElement).toBeNull(); }); }); @@ -537,6 +606,7 @@ describe('ReadyToMerge', () => { it('should be enabled in rendered output', () => { const checkboxElement = customVm.$el.querySelector('#remove-source-branch-input'); + expect(checkboxElement).not.toBeNull(); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_sha_mismatch_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_sha_mismatch_spec.js index abf642c166a..36f8c7a9683 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_sha_mismatch_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_sha_mismatch_spec.js @@ -18,8 +18,8 @@ describe('ShaMismatch', () => { it('should render information message', () => { expect(vm.$el.querySelector('button').disabled).toEqual(true); - expect( - removeBreakLine(vm.$el.textContent).trim(), - ).toContain('The source branch HEAD has recently changed. Please reload the page and review the changes before merging'); + expect(removeBreakLine(vm.$el.textContent).trim()).toContain( + 'The source branch HEAD has recently changed. Please reload the page and review the changes before merging', + ); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_unresolved_discussions_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_unresolved_discussions_spec.js index d797f1266df..bd64d7b2926 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_unresolved_discussions_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_unresolved_discussions_spec.js @@ -12,13 +12,18 @@ describe('UnresolvedDiscussions', () => { describe('with discussions path', () => { beforeEach(() => { - vm = mountComponent(Component, { mr: { - createIssueToResolveDiscussionsPath: gl.TEST_HOST, - } }); + vm = mountComponent(Component, { + mr: { + createIssueToResolveDiscussionsPath: gl.TEST_HOST, + }, + }); }); it('should have correct elements', () => { - expect(vm.$el.innerText).toContain('There are unresolved discussions. Please resolve these discussions'); + expect(vm.$el.innerText).toContain( + 'There are unresolved discussions. Please resolve these discussions', + ); + expect(vm.$el.innerText).toContain('Create an issue to resolve them later'); expect(vm.$el.querySelector('.js-create-issue').getAttribute('href')).toEqual(gl.TEST_HOST); }); @@ -30,7 +35,10 @@ describe('UnresolvedDiscussions', () => { }); it('should not show create issue link if user cannot create issue', () => { - expect(vm.$el.innerText).toContain('There are unresolved discussions. Please resolve these discussions'); + expect(vm.$el.innerText).toContain( + 'There are unresolved discussions. Please resolve these discussions', + ); + expect(vm.$el.querySelector('.js-create-issue')).toEqual(null); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js index cea603368bf..88937df2f7b 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_wip_spec.js @@ -33,6 +33,7 @@ describe('Wip', () => { describe('data', () => { it('should have default data', () => { const vm = createComponent(); + expect(vm.isMakingRequest).toBeFalsy(); }); }); @@ -43,22 +44,27 @@ describe('Wip', () => { }; describe('removeWIP', () => { - it('should make a request to service and handle response', (done) => { + it('should make a request to service and handle response', done => { const vm = createComponent(); spyOn(window, 'Flash').and.returnValue(true); spyOn(eventHub, '$emit'); - spyOn(vm.service, 'removeWIP').and.returnValue(new Promise((resolve) => { - resolve({ - data: mrObj, - }); - })); + spyOn(vm.service, 'removeWIP').and.returnValue( + new Promise(resolve => { + resolve({ + data: mrObj, + }); + }), + ); vm.removeWIP(); setTimeout(() => { expect(vm.isMakingRequest).toBeTruthy(); expect(eventHub.$emit).toHaveBeenCalledWith('UpdateWidgetData', mrObj); - expect(window.Flash).toHaveBeenCalledWith('The merge request can now be merged.', 'notice'); + expect(window.Flash).toHaveBeenCalledWith( + 'The merge request can now be merged.', + 'notice', + ); done(); }, 333); }); @@ -82,7 +88,7 @@ describe('Wip', () => { expect(el.querySelector('.js-remove-wip').innerText).toContain('Resolve WIP status'); }); - it('should not show removeWIP button is user cannot update MR', (done) => { + it('should not show removeWIP button is user cannot update MR', done => { vm.mr.removeWIPPath = ''; Vue.nextTick(() => { diff --git a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js index 6ac7138743b..d1a064b9f4d 100644 --- a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js +++ b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js @@ -7,11 +7,12 @@ import mountComponent from 'spec/helpers/vue_mount_component_helper'; import mockData from './mock_data'; import { faviconDataUrl, overlayDataUrl, faviconWithOverlayDataUrl } from '../lib/utils/mock_data'; -const returnPromise = data => new Promise((resolve) => { - resolve({ - data, +const returnPromise = data => + new Promise(resolve => { + resolve({ + data, + }); }); -}); describe('mrWidgetOptions', () => { let vm; @@ -46,6 +47,7 @@ describe('mrWidgetOptions', () => { it('should return conflicts component', () => { vm.mr.state = 'conflicts'; + expect(vm.componentName).toEqual('mr-widget-conflicts'); }); }); @@ -57,6 +59,7 @@ describe('mrWidgetOptions', () => { it('should return true for a state which requires help widget', () => { vm.mr.state = 'conflicts'; + expect(vm.shouldRenderMergeHelp).toBeTruthy(); }); }); @@ -82,6 +85,7 @@ describe('mrWidgetOptions', () => { it('should return true if there is relatedLinks in MR', () => { Vue.set(vm.mr, 'relatedLinks', {}); + expect(vm.shouldRenderRelatedLinks).toBeTruthy(); }); }); @@ -132,7 +136,7 @@ describe('mrWidgetOptions', () => { describe('methods', () => { describe('checkStatus', () => { - it('should tell service to check status', (done) => { + it('should tell service to check status', done => { spyOn(vm.service, 'checkStatus').and.returnValue(returnPromise(mockData)); spyOn(vm.mr, 'setData'); spyOn(vm, 'handleNotification'); @@ -182,7 +186,7 @@ describe('mrWidgetOptions', () => { }); describe('fetchDeployments', () => { - it('should fetch deployments', (done) => { + it('should fetch deployments', done => { spyOn(vm.service, 'fetchDeployments').and.returnValue(returnPromise([{ id: 1 }])); vm.fetchDeployments(); @@ -197,7 +201,7 @@ describe('mrWidgetOptions', () => { }); describe('fetchActionsContent', () => { - it('should fetch content of Cherry Pick and Revert modals', (done) => { + it('should fetch content of Cherry Pick and Revert modals', done => { spyOn(vm.service, 'fetchMergeActionsContent').and.returnValue(returnPromise('hello world')); vm.fetchActionsContent(); @@ -223,18 +227,23 @@ describe('mrWidgetOptions', () => { vm.bindEventHubListeners(); eventHub.$emit('SetBranchRemoveFlag', ['flag']); + expect(vm.mr.isRemovingSourceBranch).toEqual('flag'); eventHub.$emit('FailedToMerge'); + expect(vm.mr.state).toEqual('failedToMerge'); eventHub.$emit('UpdateWidgetData', mockData); + expect(vm.mr.setData).toHaveBeenCalledWith(mockData); eventHub.$emit('EnablePolling'); + expect(vm.resumePolling).toHaveBeenCalled(); eventHub.$emit('DisablePolling'); + expect(vm.stopPolling).toHaveBeenCalled(); const listenersWithServiceRequest = { @@ -243,7 +252,7 @@ describe('mrWidgetOptions', () => { }; const allArgs = eventHub.$on.calls.allArgs(); - allArgs.forEach((params) => { + allArgs.forEach(params => { const eventName = params[0]; const callback = params[1]; @@ -253,22 +262,12 @@ describe('mrWidgetOptions', () => { }); listenersWithServiceRequest.MRWidgetUpdateRequested(); + expect(vm.checkStatus).toHaveBeenCalled(); listenersWithServiceRequest.FetchActionsContent(); - expect(vm.fetchActionsContent).toHaveBeenCalled(); - }); - }); - - describe('handleMounted', () => { - it('should call required methods to do the initial kick-off', () => { - spyOn(vm, 'initDeploymentsPolling'); - spyOn(vm, 'setFaviconHelper'); - vm.handleMounted(); - - expect(vm.setFaviconHelper).toHaveBeenCalled(); - expect(vm.initDeploymentsPolling).toHaveBeenCalled(); + expect(vm.fetchActionsContent).toHaveBeenCalled(); }); }); @@ -288,13 +287,14 @@ describe('mrWidgetOptions', () => { document.body.removeChild(document.getElementById('favicon')); }); - it('should call setFavicon method', (done) => { + it('should call setFavicon method', done => { vm.mr.ciStatusFaviconPath = overlayDataUrl; - vm.setFaviconHelper().then(() => { - expect(faviconElement.getAttribute('href')).toEqual(faviconWithOverlayDataUrl); - done(); - }) - .catch(done.fail); + vm.setFaviconHelper() + .then(() => { + expect(faviconElement.getAttribute('href')).toEqual(faviconWithOverlayDataUrl); + done(); + }) + .catch(done.fail); }); it('should not call setFavicon when there is no ciStatusFaviconPath', () => { @@ -352,6 +352,7 @@ describe('mrWidgetOptions', () => { spyOn(vm.pollingInterval, 'resume'); vm.resumePolling(); + expect(vm.pollingInterval.resume).toHaveBeenCalled(); }); }); @@ -361,13 +362,14 @@ describe('mrWidgetOptions', () => { spyOn(vm.pollingInterval, 'stopTimer'); vm.stopPolling(); + expect(vm.pollingInterval.stopTimer).toHaveBeenCalled(); }); }); }); describe('rendering relatedLinks', () => { - beforeEach((done) => { + beforeEach(done => { vm.mr.relatedLinks = { assignToMe: null, closing: ` @@ -384,7 +386,7 @@ describe('mrWidgetOptions', () => { expect(vm.$el.querySelector('.close-related-link')).toBeDefined(); }); - it('does not render if state is nothingToMerge', (done) => { + it('does not render if state is nothingToMerge', done => { vm.mr.state = stateKey.nothingToMerge; Vue.nextTick(() => { expect(vm.$el.querySelector('.close-related-link')).toBeNull(); @@ -394,7 +396,7 @@ describe('mrWidgetOptions', () => { }); describe('rendering source branch removal status', () => { - it('renders when user cannot remove branch and branch should be removed', (done) => { + it('renders when user cannot remove branch and branch should be removed', done => { vm.mr.canRemoveSourceBranch = false; vm.mr.shouldRemoveSourceBranch = true; vm.mr.state = 'readyToMerge'; @@ -411,7 +413,7 @@ describe('mrWidgetOptions', () => { }); }); - it('does not render in merged state', (done) => { + it('does not render in merged state', done => { vm.mr.canRemoveSourceBranch = false; vm.mr.shouldRemoveSourceBranch = true; vm.mr.state = 'merged'; @@ -426,6 +428,20 @@ describe('mrWidgetOptions', () => { }); describe('rendering deployments', () => { + const changes = [ + { + path: 'index.html', + external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/index.html', + }, + { + path: 'imgs/gallery.html', + external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/imgs/gallery.html', + }, + { + path: 'about/', + external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/about/', + }, + ]; const deploymentMockData = { id: 15, name: 'review/diplo', @@ -437,15 +453,23 @@ describe('mrWidgetOptions', () => { external_url_formatted: 'diplo.', deployed_at: '2017-03-22T22:44:42.258Z', deployed_at_formatted: 'Mar 22, 2017 10:44pm', + changes, }; - beforeEach((done) => { - vm.mr.deployments.push({ - ...deploymentMockData, - }, { - ...deploymentMockData, - id: deploymentMockData.id + 1, - }); + beforeEach(done => { + window.gon = window.gon || {}; + window.gon.features = window.gon.features || {}; + window.gon.features.ciEnvironmentsStatusChanges = true; + + vm.mr.deployments.push( + { + ...deploymentMockData, + }, + { + ...deploymentMockData, + id: deploymentMockData.id + 1, + }, + ); vm.$nextTick(done); }); @@ -453,5 +477,13 @@ describe('mrWidgetOptions', () => { it('renders multiple deployments', () => { expect(vm.$el.querySelectorAll('.deploy-heading').length).toBe(2); }); + + it('renders dropdpown with multiple file changes', () => { + expect( + vm.$el + .querySelector('.js-mr-wigdet-deployment-dropdown') + .querySelectorAll('.js-filtered-dropdown-result').length, + ).toEqual(changes.length); + }); }); }); diff --git a/spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js b/spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js index 179e42a7cc4..9d34bdd1084 100644 --- a/spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js +++ b/spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js @@ -20,46 +20,60 @@ describe('getStateKey', () => { work_in_progress: false, }; const bound = getStateKey.bind(context, data); + expect(bound()).toEqual(null); context.canBeMerged = true; + expect(bound()).toEqual('readyToMerge'); context.canMerge = false; + expect(bound()).toEqual('notAllowedToMerge'); context.mergeWhenPipelineSucceeds = true; + expect(bound()).toEqual('mergeWhenPipelineSucceeds'); context.hasSHAChanged = true; + expect(bound()).toEqual('shaMismatch'); context.isPipelineBlocked = true; + expect(bound()).toEqual('pipelineBlocked'); context.hasMergeableDiscussionsState = true; + expect(bound()).toEqual('unresolvedDiscussions'); context.onlyAllowMergeIfPipelineSucceeds = true; context.isPipelineFailed = true; + expect(bound()).toEqual('pipelineFailed'); data.work_in_progress = true; + expect(bound()).toEqual('workInProgress'); data.has_conflicts = true; + expect(bound()).toEqual('conflicts'); context.mergeStatus = 'unchecked'; + expect(bound()).toEqual('checking'); data.commits_count = 0; + expect(bound()).toEqual('nothingToMerge'); data.branch_missing = true; + expect(bound()).toEqual('missingBranch'); data.project_archived = true; + expect(bound()).toEqual('archived'); }); }); diff --git a/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js b/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js index 33d052aceb2..f5079147f60 100644 --- a/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js +++ b/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js @@ -12,32 +12,38 @@ describe('MergeRequestStore', () => { it('should set hasSHAChanged when the diff SHA changes', () => { store.setData({ ...mockData, diff_head_sha: 'a-different-string' }); + expect(store.hasSHAChanged).toBe(true); }); it('should not set hasSHAChanged when other data changes', () => { store.setData({ ...mockData, work_in_progress: !mockData.work_in_progress }); + expect(store.hasSHAChanged).toBe(false); }); describe('isPipelinePassing', () => { it('is true when the CI status is `success`', () => { store.setData({ ...mockData, ci_status: 'success' }); + expect(store.isPipelinePassing).toBe(true); }); it('is true when the CI status is `success_with_warnings`', () => { store.setData({ ...mockData, ci_status: 'success_with_warnings' }); + expect(store.isPipelinePassing).toBe(true); }); it('is false when the CI status is `failed`', () => { store.setData({ ...mockData, ci_status: 'failed' }); + expect(store.isPipelinePassing).toBe(false); }); it('is false when the CI status is anything except `success`', () => { store.setData({ ...mockData, ci_status: 'foobarbaz' }); + expect(store.isPipelinePassing).toBe(false); }); }); @@ -45,11 +51,13 @@ describe('MergeRequestStore', () => { describe('isPipelineSkipped', () => { it('should set isPipelineSkipped=true when the CI status is `skipped`', () => { store.setData({ ...mockData, ci_status: 'skipped' }); + expect(store.isPipelineSkipped).toBe(true); }); it('should set isPipelineSkipped=false when the CI status is anything except `skipped`', () => { store.setData({ ...mockData, ci_status: 'foobarbaz' }); + expect(store.isPipelineSkipped).toBe(false); }); }); @@ -57,11 +65,13 @@ describe('MergeRequestStore', () => { describe('isNothingToMergeState', () => { it('returns true when nothingToMerge', () => { store.state = stateKey.nothingToMerge; + expect(store.isNothingToMergeState).toEqual(true); }); it('returns false when not nothingToMerge', () => { store.state = 'state'; + expect(store.isNothingToMergeState).toEqual(false); }); }); diff --git a/spec/javascripts/vue_shared/components/bar_chart_spec.js b/spec/javascripts/vue_shared/components/bar_chart_spec.js index 7e91cd6f63f..8f753876e44 100644 --- a/spec/javascripts/vue_shared/components/bar_chart_spec.js +++ b/spec/javascripts/vue_shared/components/bar_chart_spec.js @@ -71,12 +71,6 @@ describe('Bar chart component', () => { expect(barChart.xAxisLocation).toEqual('translate(100, 250)'); }); - it('Contains a total of 4 ticks across the y axis', () => { - const ticks = barChart.$el.querySelector('.y-axis').querySelectorAll('.tick').length; - - expect(ticks).toEqual(4); - }); - it('rotates the x axis labels a total of 90 degress (CCW)', () => { const xAxisLabel = barChart.$el.querySelector('.x-axis').querySelectorAll('text')[0]; diff --git a/spec/javascripts/vue_shared/components/ci_badge_link_spec.js b/spec/javascripts/vue_shared/components/ci_badge_link_spec.js index 668742ebaee..4b0b7ba66e5 100644 --- a/spec/javascripts/vue_shared/components/ci_badge_link_spec.js +++ b/spec/javascripts/vue_shared/components/ci_badge_link_spec.js @@ -81,11 +81,12 @@ describe('CI Badge Link Component', () => { }); it('should render each status badge', () => { - Object.keys(statuses).map((status) => { + Object.keys(statuses).map(status => { vm = mountComponent(CIBadge, { status: statuses[status] }); + expect(vm.$el.getAttribute('href')).toEqual(statuses[status].details_path); expect(vm.$el.textContent.trim()).toEqual(statuses[status].text); - expect(vm.$el.getAttribute('class')).toEqual(`ci-status ci-${statuses[status].group}`); + expect(vm.$el.getAttribute('class')).toContain(`ci-status ci-${statuses[status].group}`); expect(vm.$el.querySelector('svg')).toBeDefined(); return vm; }); @@ -93,6 +94,7 @@ describe('CI Badge Link Component', () => { it('should not render label', () => { vm = mountComponent(CIBadge, { status: statuses.canceled, showText: false }); + expect(vm.$el.textContent.trim()).toEqual(''); }); }); diff --git a/spec/javascripts/vue_shared/components/clipboard_button_spec.js b/spec/javascripts/vue_shared/components/clipboard_button_spec.js index ea525b1e44f..2f7ea077b54 100644 --- a/spec/javascripts/vue_shared/components/clipboard_button_spec.js +++ b/spec/javascripts/vue_shared/components/clipboard_button_spec.js @@ -44,6 +44,7 @@ describe('clipboard button', () => { title: 'Copy this value into Clipboard!', cssClass: 'btn-danger', }); + expect(vm.$el.getAttribute('data-clipboard-text')).toEqual( '{"text":"copy me","gfm":"`path/to/file`"}', ); diff --git a/spec/javascripts/vue_shared/components/commit_spec.js b/spec/javascripts/vue_shared/components/commit_spec.js index 7189e8cfcfa..97dacec1fce 100644 --- a/spec/javascripts/vue_shared/components/commit_spec.js +++ b/spec/javascripts/vue_shared/components/commit_spec.js @@ -78,6 +78,7 @@ describe('Commit component', () => { expect(component.$el.querySelector('.commit-sha').getAttribute('href')).toEqual( props.commitUrl, ); + expect(component.$el.querySelector('.commit-sha').textContent).toContain(props.shortSha); }); @@ -100,6 +101,7 @@ describe('Commit component', () => { .querySelector('.commit-title .avatar-image-container img') .getAttribute('data-original-title'), ).toContain(props.author.username); + expect( component.$el .querySelector('.commit-title .avatar-image-container img') @@ -112,6 +114,7 @@ describe('Commit component', () => { expect(component.$el.querySelector('a.commit-row-message').getAttribute('href')).toEqual( props.commitUrl, ); + expect(component.$el.querySelector('a.commit-row-message').textContent).toContain( props.title, ); diff --git a/spec/javascripts/vue_shared/components/deprecated_modal_spec.js b/spec/javascripts/vue_shared/components/deprecated_modal_spec.js index 59d4e549a91..be75be92158 100644 --- a/spec/javascripts/vue_shared/components/deprecated_modal_spec.js +++ b/spec/javascripts/vue_shared/components/deprecated_modal_spec.js @@ -47,7 +47,7 @@ describe('DeprecatedModal', () => { }); }); - it('works with data-toggle="modal"', (done) => { + it('works with data-toggle="modal"', done => { setFixtures(` <button id="modal-button" data-toggle="modal" data-target="#my-modal"></button> <div id="modal-container"></div> @@ -55,9 +55,13 @@ describe('DeprecatedModal', () => { const modalContainer = document.getElementById('modal-container'); const modalButton = document.getElementById('modal-button'); - vm = mountComponent(modalComponent, { - id: 'my-modal', - }, modalContainer); + vm = mountComponent( + modalComponent, + { + id: 'my-modal', + }, + modalContainer, + ); const modalElement = vm.$el.querySelector('#my-modal'); $(modalElement).on('shown.bs.modal', () => done()); diff --git a/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js b/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js index 71d9145bf22..fcd231ec693 100644 --- a/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js +++ b/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js @@ -55,6 +55,7 @@ describe('DiffViewer', () => { expect(vm.$el.querySelector('.deleted .file-info').textContent.trim()).toContain( 'testold.abc', ); + expect(vm.$el.querySelector('.deleted .btn.btn-default').textContent.trim()).toContain( 'Download', ); diff --git a/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js b/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js index dde49b4a5d7..380effdb669 100644 --- a/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js +++ b/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js @@ -55,6 +55,7 @@ describe('ImageDiffViewer', () => { expect(vm.$el.querySelector('.added .image_file img').getAttribute('src')).toBe( GREEN_BOX_IMAGE_URL, ); + expect(vm.$el.querySelector('.deleted .image_file img').getAttribute('src')).toBe( RED_BOX_IMAGE_URL, ); @@ -63,6 +64,7 @@ describe('ImageDiffViewer', () => { expect(vm.$el.querySelector('.view-modes-menu li:nth-child(2)').textContent.trim()).toBe( 'Swipe', ); + expect(vm.$el.querySelector('.view-modes-menu li:nth-child(3)').textContent.trim()).toBe( 'Onion skin', ); diff --git a/spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js b/spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js index 2796cd088c6..2fc4943de30 100644 --- a/spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js +++ b/spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js @@ -55,6 +55,7 @@ describe('DropdownButtonComponent', () => { it('renders dropdown toggle text element', () => { const dropdownToggleTextEl = vm.$el.querySelector('.dropdown-toggle-text'); + expect(dropdownToggleTextEl).not.toBeNull(); expect(dropdownToggleTextEl.innerText.trim()).toBe(defaultLabel); }); @@ -67,9 +68,12 @@ describe('DropdownButtonComponent', () => { }); it('renders slot, if default slot exists', () => { - vm = createComponent({}, { - default: ['Lorem Ipsum Dolar'], - }); + vm = createComponent( + {}, + { + default: ['Lorem Ipsum Dolar'], + }, + ); expect(vm.$el).not.toContainElement('.dropdown-toggle-text'); expect(vm.$el).toHaveText('Lorem Ipsum Dolar'); diff --git a/spec/javascripts/vue_shared/components/file_icon_spec.js b/spec/javascripts/vue_shared/components/file_icon_spec.js index f2a09d08829..34c9b35e02a 100644 --- a/spec/javascripts/vue_shared/components/file_icon_spec.js +++ b/spec/javascripts/vue_shared/components/file_icon_spec.js @@ -28,7 +28,9 @@ describe('File Icon component', () => { fileName: 'test.js', }); - expect(vm.$el.firstChild.firstChild.getAttribute('xlink:href')).toBe(`${gon.sprite_file_icons}#javascript`); + expect(vm.$el.firstChild.firstChild.getAttribute('xlink:href')).toBe( + `${gon.sprite_file_icons}#javascript`, + ); }); it('should render a image icon based on file ending', () => { @@ -36,7 +38,9 @@ describe('File Icon component', () => { fileName: 'test.png', }); - expect(vm.$el.firstChild.firstChild.getAttribute('xlink:href')).toBe(`${gon.sprite_file_icons}#image`); + expect(vm.$el.firstChild.firstChild.getAttribute('xlink:href')).toBe( + `${gon.sprite_file_icons}#image`, + ); }); it('should render a webpack icon based on file namer', () => { @@ -44,7 +48,9 @@ describe('File Icon component', () => { fileName: 'webpack.js', }); - expect(vm.$el.firstChild.firstChild.getAttribute('xlink:href')).toBe(`${gon.sprite_file_icons}#webpack`); + expect(vm.$el.firstChild.firstChild.getAttribute('xlink:href')).toBe( + `${gon.sprite_file_icons}#webpack`, + ); }); it('should render a standard folder icon', () => { @@ -53,7 +59,9 @@ describe('File Icon component', () => { folder: true, }); - expect(vm.$el.querySelector('span > svg > use').getAttribute('xlink:href')).toBe(`${gon.sprite_file_icons}#folder`); + expect(vm.$el.querySelector('span > svg > use').getAttribute('xlink:href')).toBe( + `${gon.sprite_file_icons}#folder`, + ); }); it('should render a loading icon', () => { @@ -63,6 +71,7 @@ describe('File Icon component', () => { }); const { classList } = vm.$el.querySelector('i'); + expect(classList.contains('fa')).toEqual(true); expect(classList.contains('fa-spin')).toEqual(true); expect(classList.contains('fa-spinner')).toEqual(true); @@ -79,6 +88,7 @@ describe('File Icon component', () => { const { classList } = vm.$el.firstChild; const containsSizeClass = classList.contains('s120'); const containsCustomClass = classList.contains('extraclasses'); + expect(containsSizeClass).toBe(true); expect(containsCustomClass).toBe(true); }); diff --git a/spec/javascripts/vue_shared/components/filtered_search_dropdown_spec.js b/spec/javascripts/vue_shared/components/filtered_search_dropdown_spec.js new file mode 100644 index 00000000000..b71cb36ecf6 --- /dev/null +++ b/spec/javascripts/vue_shared/components/filtered_search_dropdown_spec.js @@ -0,0 +1,91 @@ +import Vue from 'vue'; +import component from '~/vue_shared/components/filtered_search_dropdown.vue'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; + +describe('Filtered search dropdown', () => { + const Component = Vue.extend(component); + let vm; + + afterEach(() => { + vm.$destroy(); + }); + + describe('with an empty array of items', () => { + beforeEach(() => { + vm = mountComponent(Component, { + items: [], + filterKey: '', + }); + }); + + it('renders empty list', () => { + expect(vm.$el.querySelectorAll('.js-filtered-dropdown-result').length).toEqual(0); + }); + + it('renders filter input', () => { + expect(vm.$el.querySelector('.js-filtered-dropdown-input')).not.toBeNull(); + }); + }); + + describe('when visible numbers is less than the items length', () => { + beforeEach(() => { + vm = mountComponent(Component, { + items: [{ title: 'One' }, { title: 'Two' }, { title: 'Three' }], + visibleItems: 2, + filterKey: 'title', + }); + }); + + it('it renders only the maximum number provided', () => { + expect(vm.$el.querySelectorAll('.js-filtered-dropdown-result').length).toEqual(2); + }); + }); + + describe('when visible number is bigger than the items lenght', () => { + beforeEach(() => { + vm = mountComponent(Component, { + items: [{ title: 'One' }, { title: 'Two' }, { title: 'Three' }], + filterKey: 'title', + }); + }); + + it('it renders the full list of items the maximum number provided', () => { + expect(vm.$el.querySelectorAll('.js-filtered-dropdown-result').length).toEqual(3); + }); + }); + + describe('while filtering', () => { + beforeEach(() => { + vm = mountComponent(Component, { + items: [ + { title: 'One' }, + { title: 'Two/three' }, + { title: 'Three four' }, + { title: 'Five' }, + ], + filterKey: 'title', + }); + }); + + it('updates the results to match the typed value', done => { + vm.$el.querySelector('.js-filtered-dropdown-input').value = 'three'; + vm.$el.querySelector('.js-filtered-dropdown-input').dispatchEvent(new Event('input')); + vm.$nextTick(() => { + expect(vm.$el.querySelectorAll('.js-filtered-dropdown-result').length).toEqual(2); + done(); + }); + }); + + describe('when no value matches the typed one', () => { + it('does not render any result', done => { + vm.$el.querySelector('.js-filtered-dropdown-input').value = 'six'; + vm.$el.querySelector('.js-filtered-dropdown-input').dispatchEvent(new Event('input')); + + vm.$nextTick(() => { + expect(vm.$el.querySelectorAll('.js-filtered-dropdown-result').length).toEqual(0); + done(); + }); + }); + }); + }); +}); diff --git a/spec/javascripts/vue_shared/components/gl_modal_spec.js b/spec/javascripts/vue_shared/components/gl_modal_spec.js index 263824a102a..19af8b5d2f7 100644 --- a/spec/javascripts/vue_shared/components/gl_modal_spec.js +++ b/spec/javascripts/vue_shared/components/gl_modal_spec.js @@ -48,6 +48,7 @@ describe('GlModal', () => { it('sets the modal title', () => { const modalTitle = vm.$el.querySelector('.modal-title'); + expect(modalTitle.innerHTML.trim()).toBe(props.headerTitleText); }); }); @@ -63,6 +64,7 @@ describe('GlModal', () => { it('sets the primary button class', () => { const primaryButton = vm.$el.querySelector('.modal-footer button:last-of-type'); + expect(primaryButton).toHaveClass(`btn-${props.footerPrimaryButtonVariant}`); }); }); @@ -78,6 +80,7 @@ describe('GlModal', () => { it('sets the primary button text', () => { const primaryButton = vm.$el.querySelector('.modal-footer button:last-of-type'); + expect(primaryButton.innerHTML.trim()).toBe(props.footerPrimaryButtonText); }); }); @@ -173,6 +176,7 @@ describe('GlModal', () => { it('sets the modal body', () => { const modalBody = vm.$el.querySelector('.modal-body'); + expect(modalBody.innerHTML).toBe(slotContent); }); }); @@ -184,6 +188,7 @@ describe('GlModal', () => { it('sets the modal header', () => { const modalHeader = vm.$el.querySelector('.modal-header'); + expect(modalHeader.innerHTML).toBe(slotContent); }); }); @@ -195,6 +200,7 @@ describe('GlModal', () => { it('sets the modal title', () => { const modalTitle = vm.$el.querySelector('.modal-title'); + expect(modalTitle.innerHTML).toBe(slotContent); }); }); @@ -206,6 +212,7 @@ describe('GlModal', () => { it('sets the modal footer', () => { const modalFooter = vm.$el.querySelector('.modal-footer'); + expect(modalFooter.innerHTML).toBe(slotContent); }); }); diff --git a/spec/javascripts/vue_shared/components/header_ci_component_spec.js b/spec/javascripts/vue_shared/components/header_ci_component_spec.js index f17818c17c7..3bf497bc00b 100644 --- a/spec/javascripts/vue_shared/components/header_ci_component_spec.js +++ b/spec/javascripts/vue_shared/components/header_ci_component_spec.js @@ -59,9 +59,9 @@ describe('Header CI Component', () => { it('should render status badge', () => { expect(vm.$el.querySelector('.ci-failed')).toBeDefined(); expect(vm.$el.querySelector('.ci-status-icon-failed svg')).toBeDefined(); - expect( - vm.$el.querySelector('.ci-failed').getAttribute('href'), - ).toEqual(props.status.details_path); + expect(vm.$el.querySelector('.ci-failed').getAttribute('href')).toEqual( + props.status.details_path, + ); }); it('should render item name and id', () => { @@ -84,7 +84,7 @@ describe('Header CI Component', () => { expect(vm.$el.querySelector('.link').getAttribute('href')).toEqual(props.actions[0].path); }); - it('should show loading icon', (done) => { + it('should show loading icon', done => { vm.actions[0].isLoading = true; Vue.nextTick(() => { @@ -94,7 +94,7 @@ describe('Header CI Component', () => { }); it('should render sidebar toggle button', () => { - expect(vm.$el.querySelector('.js-sidebar-build-toggle')).toBeDefined(); + expect(vm.$el.querySelector('.js-sidebar-build-toggle')).not.toBeNull(); }); }); diff --git a/spec/javascripts/vue_shared/components/icon_spec.js b/spec/javascripts/vue_shared/components/icon_spec.js index 01f4649339e..45eef2ad737 100644 --- a/spec/javascripts/vue_shared/components/icon_spec.js +++ b/spec/javascripts/vue_shared/components/icon_spec.js @@ -2,11 +2,11 @@ import Vue from 'vue'; import Icon from '~/vue_shared/components/icon.vue'; import mountComponent from 'spec/helpers/vue_mount_component_helper'; -describe('Sprite Icon Component', function () { - describe('Initialization', function () { +describe('Sprite Icon Component', function() { + describe('Initialization', function() { let icon; - beforeEach(function () { + beforeEach(function() { const IconComponent = Vue.extend(Icon); icon = mountComponent(IconComponent, { @@ -21,20 +21,20 @@ describe('Sprite Icon Component', function () { icon.$destroy(); }); - it('should return a defined Vue component', function () { + it('should return a defined Vue component', function() { expect(icon).toBeDefined(); }); - it('should have <svg> as a child element', function () { + it('should have <svg> as a child element', function() { expect(icon.$el.tagName).toBe('svg'); }); - it('should have <use> as a child element with the correct href', function () { + it('should have <use> as a child element with the correct href', function() { expect(icon.$el.firstChild.tagName).toBe('use'); expect(icon.$el.firstChild.getAttribute('xlink:href')).toBe(`${gon.sprite_icons}#commit`); }); - it('should properly compute iconSizeClass', function () { + it('should properly compute iconSizeClass', function() { expect(icon.iconSizeClass).toBe('s32'); }); @@ -44,10 +44,11 @@ describe('Sprite Icon Component', function () { expect(icon.$options.props.size.validator(9001)).toBeFalsy(); }); - it('should properly render img css', function () { + it('should properly render img css', function() { const { classList } = icon.$el; const containsSizeClass = classList.contains('s32'); const containsCustomClass = classList.contains('extraclasses'); + expect(containsSizeClass).toBe(true); expect(containsCustomClass).toBe(true); }); diff --git a/spec/javascripts/vue_shared/components/identicon_spec.js b/spec/javascripts/vue_shared/components/identicon_spec.js index 0719800c682..0b3dbb61c96 100644 --- a/spec/javascripts/vue_shared/components/identicon_spec.js +++ b/spec/javascripts/vue_shared/components/identicon_spec.js @@ -1,7 +1,7 @@ import Vue from 'vue'; import identiconComponent from '~/vue_shared/components/identicon.vue'; -const createComponent = (sizeClass) => { +const createComponent = sizeClass => { const Component = Vue.extend(identiconComponent); return new Component({ diff --git a/spec/javascripts/vue_shared/components/issue/issue_warning_spec.js b/spec/javascripts/vue_shared/components/issue/issue_warning_spec.js index e6ed77dbb52..aa7d6ea2e34 100644 --- a/spec/javascripts/vue_shared/components/issue/issue_warning_spec.js +++ b/spec/javascripts/vue_shared/components/issue/issue_warning_spec.js @@ -6,7 +6,10 @@ const IssueWarning = Vue.extend(issueWarning); function formatWarning(string) { // Replace newlines with a space then replace multiple spaces with one space - return string.trim().replace(/\n/g, ' ').replace(/\s\s+/g, ' '); + return string + .trim() + .replace(/\n/g, ' ') + .replace(/\s\s+/g, ' '); } describe('Issue Warning Component', () => { @@ -17,7 +20,9 @@ describe('Issue Warning Component', () => { }); expect(vm.$el.querySelector('.icon use').href.baseVal).toMatch(/lock$/); - expect(formatWarning(vm.$el.querySelector('span').textContent)).toEqual('This issue is locked. Only project members can comment.'); + expect(formatWarning(vm.$el.querySelector('span').textContent)).toEqual( + 'This issue is locked. Only project members can comment.', + ); }); }); @@ -28,7 +33,9 @@ describe('Issue Warning Component', () => { }); expect(vm.$el.querySelector('.icon use').href.baseVal).toMatch(/eye-slash$/); - expect(formatWarning(vm.$el.querySelector('span').textContent)).toEqual('This is a confidential issue. Your comment will not be visible to the public.'); + expect(formatWarning(vm.$el.querySelector('span').textContent)).toEqual( + 'This is a confidential issue. Your comment will not be visible to the public.', + ); }); }); @@ -40,7 +47,9 @@ describe('Issue Warning Component', () => { }); expect(vm.$el.querySelector('.icon')).toBeFalsy(); - expect(formatWarning(vm.$el.querySelector('span').textContent)).toEqual('This issue is confidential and locked. People without permission will never get a notification and won\'t be able to comment.'); + expect(formatWarning(vm.$el.querySelector('span').textContent)).toEqual( + "This issue is confidential and locked. People without permission will never get a notification and won't be able to comment.", + ); }); }); }); diff --git a/spec/javascripts/vue_shared/components/loading_button_spec.js b/spec/javascripts/vue_shared/components/loading_button_spec.js index 51c19cd4080..db89d4a934c 100644 --- a/spec/javascripts/vue_shared/components/loading_button_spec.js +++ b/spec/javascripts/vue_shared/components/loading_button_spec.js @@ -4,7 +4,7 @@ import mountComponent from 'spec/helpers/vue_mount_component_helper'; const LABEL = 'Hello'; -describe('LoadingButton', function () { +describe('LoadingButton', function() { let vm; let LoadingButton; @@ -69,6 +69,7 @@ describe('LoadingButton', function () { describe('container class', () => { it('should default to btn btn-align-content', () => { vm = mountComponent(LoadingButton, {}); + expect(vm.$el.classList.contains('btn')).toEqual(true); expect(vm.$el.classList.contains('btn-align-content')).toEqual(true); }); @@ -77,6 +78,7 @@ describe('LoadingButton', function () { vm = mountComponent(LoadingButton, { containerClass: 'test-class', }); + expect(vm.$el.classList.contains('btn')).toEqual(false); expect(vm.$el.classList.contains('btn-align-content')).toEqual(false); expect(vm.$el.classList.contains('test-class')).toEqual(true); diff --git a/spec/javascripts/vue_shared/components/markdown/field_spec.js b/spec/javascripts/vue_shared/components/markdown/field_spec.js index 0dea9278cc2..abb17440c0e 100644 --- a/spec/javascripts/vue_shared/components/markdown/field_spec.js +++ b/spec/javascripts/vue_shared/components/markdown/field_spec.js @@ -11,7 +11,7 @@ function assertMarkdownTabs(isWrite, writeLink, previewLink, vm) { describe('Markdown field component', () => { let vm; - beforeEach((done) => { + beforeEach(done => { vm = new Vue({ components: { fieldComponent, @@ -39,9 +39,7 @@ describe('Markdown field component', () => { describe('mounted', () => { it('renders textarea inside backdrop', () => { - expect( - vm.$el.querySelector('.zen-backdrop textarea'), - ).not.toBeNull(); + expect(vm.$el.querySelector('.zen-backdrop textarea')).not.toBeNull(); }); describe('markdown preview', () => { @@ -49,73 +47,70 @@ describe('Markdown field component', () => { let writeLink; beforeEach(() => { - spyOn(Vue.http, 'post').and.callFake(() => new Promise((resolve) => { - setTimeout(() => { - resolve({ - json() { - return { - body: '<p>markdown preview</p>', - }; - }, - }); - }); - })); + spyOn(Vue.http, 'post').and.callFake( + () => + new Promise(resolve => { + setTimeout(() => { + resolve({ + json() { + return { + body: '<p>markdown preview</p>', + }; + }, + }); + }); + }), + ); previewLink = vm.$el.querySelector('.nav-links .js-preview-link'); writeLink = vm.$el.querySelector('.nav-links .js-write-link'); }); - it('sets preview link as active', (done) => { + it('sets preview link as active', done => { previewLink.click(); Vue.nextTick(() => { - expect( - previewLink.parentNode.classList.contains('active'), - ).toBeTruthy(); + expect(previewLink.parentNode.classList.contains('active')).toBeTruthy(); done(); }); }); - it('shows preview loading text', (done) => { + it('shows preview loading text', done => { previewLink.click(); Vue.nextTick(() => { - expect( - vm.$el.querySelector('.md-preview').textContent.trim(), - ).toContain('Loading...'); + expect(vm.$el.querySelector('.md-preview').textContent.trim()).toContain('Loading...'); done(); }); }); - it('renders markdown preview', (done) => { + it('renders markdown preview', done => { previewLink.click(); setTimeout(() => { - expect( - vm.$el.querySelector('.md-preview').innerHTML, - ).toContain('<p>markdown preview</p>'); + expect(vm.$el.querySelector('.md-preview').innerHTML).toContain( + '<p>markdown preview</p>', + ); done(); }); }); - it('renders GFM with jQuery', (done) => { + it('renders GFM with jQuery', done => { spyOn($.fn, 'renderGFM'); previewLink.click(); setTimeout(() => { - expect( - $.fn.renderGFM, - ).toHaveBeenCalled(); + expect($.fn.renderGFM).toHaveBeenCalled(); done(); }, 0); }); - it('clicking already active write or preview link does nothing', (done) => { + it('clicking already active write or preview link does nothing', done => { writeLink.click(); Vue.nextTick() .then(() => assertMarkdownTabs(true, writeLink, previewLink, vm)) @@ -134,46 +129,40 @@ describe('Markdown field component', () => { }); describe('markdown buttons', () => { - it('converts single words', (done) => { + it('converts single words', done => { const textarea = vm.$el.querySelector('textarea'); textarea.setSelectionRange(0, 7); vm.$el.querySelector('.js-md').click(); Vue.nextTick(() => { - expect( - textarea.value, - ).toContain('**testing**'); + expect(textarea.value).toContain('**testing**'); done(); }); }); - it('converts a line', (done) => { + it('converts a line', done => { const textarea = vm.$el.querySelector('textarea'); textarea.setSelectionRange(0, 0); vm.$el.querySelectorAll('.js-md')[5].click(); Vue.nextTick(() => { - expect( - textarea.value, - ).toContain('* testing'); + expect(textarea.value).toContain('* testing'); done(); }); }); - it('converts multiple lines', (done) => { + it('converts multiple lines', done => { const textarea = vm.$el.querySelector('textarea'); textarea.setSelectionRange(0, 50); vm.$el.querySelectorAll('.js-md')[5].click(); Vue.nextTick(() => { - expect( - textarea.value, - ).toContain('* testing\n* 123'); + expect(textarea.value).toContain('* testing\n* 123'); done(); }); diff --git a/spec/javascripts/vue_shared/components/markdown/header_spec.js b/spec/javascripts/vue_shared/components/markdown/header_spec.js index a4681617e66..59613faa49f 100644 --- a/spec/javascripts/vue_shared/components/markdown/header_spec.js +++ b/spec/javascripts/vue_shared/components/markdown/header_spec.js @@ -18,7 +18,18 @@ describe('Markdown field header component', () => { }); it('renders markdown header buttons', () => { - const buttons = ['Add bold text', 'Add italic text', 'Insert a quote', 'Insert code', 'Add a link', 'Add a bullet list', 'Add a numbered list', 'Add a task list', 'Add a table', 'Go full screen']; + const buttons = [ + 'Add bold text', + 'Add italic text', + 'Insert a quote', + 'Insert code', + 'Add a link', + 'Add a bullet list', + 'Add a numbered list', + 'Add a task list', + 'Add a table', + 'Go full screen', + ]; const elements = vm.$el.querySelectorAll('.toolbar-btn'); elements.forEach((buttonEl, index) => { @@ -56,14 +67,16 @@ describe('Markdown field header component', () => { spyOn(vm, '$emit'); $(document).triggerHandler('markdown-preview:show', [ - $('<form><div class="js-vue-markdown-field"><textarea class="markdown-area"></textarea></div></form>'), + $( + '<form><div class="js-vue-markdown-field"><textarea class="markdown-area"></textarea></div></form>', + ), ]); expect(vm.$emit).not.toHaveBeenCalled(); }); it('blurs preview link after click', done => { - const link = vm.$el.querySelector('li:nth-child(2) a'); + const link = vm.$el.querySelector('li:nth-child(2) button'); spyOn(HTMLElement.prototype, 'blur'); link.click(); @@ -76,6 +89,8 @@ describe('Markdown field header component', () => { }); it('renders markdown table template', () => { - expect(vm.mdTable).toEqual('| header | header |\n| ------ | ------ |\n| cell | cell |\n| cell | cell |'); + expect(vm.mdTable).toEqual( + '| header | header |\n| ------ | ------ |\n| cell | cell |\n| cell | cell |', + ); }); }); diff --git a/spec/javascripts/vue_shared/components/markdown/toolbar_spec.js b/spec/javascripts/vue_shared/components/markdown/toolbar_spec.js index 3e708f865c8..e6c7abd9d3b 100644 --- a/spec/javascripts/vue_shared/components/markdown/toolbar_spec.js +++ b/spec/javascripts/vue_shared/components/markdown/toolbar_spec.js @@ -25,9 +25,12 @@ describe('toolbar', () => { describe('user cannot attach file', () => { beforeEach(() => { - vm = mountComponent(Toolbar, Object.assign({}, props, { - canAttachFile: false, - })); + vm = mountComponent( + Toolbar, + Object.assign({}, props, { + canAttachFile: false, + }), + ); }); it('should not render uploading-container', () => { diff --git a/spec/javascripts/vue_shared/components/memory_graph_spec.js b/spec/javascripts/vue_shared/components/memory_graph_spec.js index 0982b3e1f38..78c3ae3ddb3 100644 --- a/spec/javascripts/vue_shared/components/memory_graph_spec.js +++ b/spec/javascripts/vue_shared/components/memory_graph_spec.js @@ -52,6 +52,7 @@ describe('MemoryGraph', () => { it('should show human readable median value based on provided median timestamp', () => { vm.deploymentTime = mockMedian; const formattedMedian = vm.getFormattedMedian; + expect(formattedMedian.indexOf('Deployed')).toBeGreaterThan(-1); expect(formattedMedian.indexOf('ago')).toBeGreaterThan(-1); }); @@ -62,6 +63,7 @@ describe('MemoryGraph', () => { describe('getMedianMetricIndex', () => { it('should return index of closest metric timestamp to that of median', () => { const matchingIndex = vm.getMedianMetricIndex(mockMedian, mockMetrics); + expect(matchingIndex).toBe(mockMedianIndex); }); }); @@ -69,6 +71,7 @@ describe('MemoryGraph', () => { describe('getGraphPlotValues', () => { it('should return Object containing values to plot graph', () => { const plotValues = vm.getGraphPlotValues(mockMedian, mockMetrics); + expect(plotValues.pathD).toBeDefined(); expect(Array.isArray(plotValues.pathD)).toBeTruthy(); @@ -90,7 +93,7 @@ describe('MemoryGraph', () => { expect(el.querySelector('svg')).toBeDefined(); }); - it('should render graph when renderGraph is called internally', (done) => { + it('should render graph when renderGraph is called internally', done => { const { pathD, pathViewBox, dotX, dotY } = vm.getGraphPlotValues(mockMedian, mockMetrics); vm.height = defaultHeight; vm.width = defaultWidth; @@ -101,16 +104,21 @@ describe('MemoryGraph', () => { Vue.nextTick(() => { const svgEl = el.querySelector('svg'); + expect(svgEl).toBeDefined(); expect(svgEl.getAttribute('height')).toBe(defaultHeight); expect(svgEl.getAttribute('width')).toBe(defaultWidth); const pathEl = el.querySelector('path'); + expect(pathEl).toBeDefined(); expect(pathEl.getAttribute('d')).toBe(`M ${pathD}`); - expect(pathEl.getAttribute('viewBox')).toBe(`0 0 ${pathViewBox.lineWidth} ${pathViewBox.diff}`); + expect(pathEl.getAttribute('viewBox')).toBe( + `0 0 ${pathViewBox.lineWidth} ${pathViewBox.diff}`, + ); const circleEl = el.querySelector('circle'); + expect(circleEl).toBeDefined(); expect(circleEl.getAttribute('r')).toBe('1.5'); expect(circleEl.getAttribute('transform')).toBe('translate(0 -1)'); diff --git a/spec/javascripts/vue_shared/components/navigation_tabs_spec.js b/spec/javascripts/vue_shared/components/navigation_tabs_spec.js index 09fda95d7d3..462bfc10664 100644 --- a/spec/javascripts/vue_shared/components/navigation_tabs_spec.js +++ b/spec/javascripts/vue_shared/components/navigation_tabs_spec.js @@ -46,7 +46,9 @@ describe('navigation tabs component', () => { it('should render badge', () => { expect(vm.$el.querySelector('.js-pipelines-tab-all .badge').textContent.trim()).toEqual('1'); - expect(vm.$el.querySelector('.js-pipelines-tab-pending .badge').textContent.trim()).toEqual('0'); + expect(vm.$el.querySelector('.js-pipelines-tab-pending .badge').textContent.trim()).toEqual( + '0', + ); }); it('should not render badge', () => { @@ -56,6 +58,7 @@ describe('navigation tabs component', () => { it('should trigger onTabClick', () => { spyOn(vm, '$emit'); vm.$el.querySelector('.js-pipelines-tab-pending').click(); + expect(vm.$emit).toHaveBeenCalledWith('onChangeTab', 'pending'); }); }); diff --git a/spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js b/spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js index db665fdaad3..45f131194ca 100644 --- a/spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js +++ b/spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js @@ -26,6 +26,7 @@ describe('issue placeholder system note component', () => { expect(vm.$el.querySelector('.user-avatar-link').getAttribute('href')).toEqual( userDataMock.path, ); + expect(vm.$el.querySelector('.user-avatar-link img').getAttribute('src')).toEqual( `${userDataMock.avatar_url}?width=40`, ); @@ -37,6 +38,7 @@ describe('issue placeholder system note component', () => { expect(vm.$el.querySelector('.note-header-info a').getAttribute('href')).toEqual( userDataMock.path, ); + expect( vm.$el.querySelector('.note-header-info .note-headline-light').textContent.trim(), ).toEqual(`@${userDataMock.username}`); diff --git a/spec/javascripts/vue_shared/components/notes/placeholder_system_note_spec.js b/spec/javascripts/vue_shared/components/notes/placeholder_system_note_spec.js index 262571efcb8..6013e85811a 100644 --- a/spec/javascripts/vue_shared/components/notes/placeholder_system_note_spec.js +++ b/spec/javascripts/vue_shared/components/notes/placeholder_system_note_spec.js @@ -20,6 +20,8 @@ describe('placeholder system note component', () => { }); expect(vm.$el.tagName).toEqual('LI'); - expect(vm.$el.querySelector('.timeline-content em').textContent.trim()).toEqual('This is a placeholder'); + expect(vm.$el.querySelector('.timeline-content em').textContent.trim()).toEqual( + 'This is a placeholder', + ); }); }); diff --git a/spec/javascripts/vue_shared/components/pagination_links_spec.js b/spec/javascripts/vue_shared/components/pagination_links_spec.js index c9d183872b4..d0cb3731050 100644 --- a/spec/javascripts/vue_shared/components/pagination_links_spec.js +++ b/spec/javascripts/vue_shared/components/pagination_links_spec.js @@ -23,13 +23,10 @@ describe('Pagination links component', () => { let destinationComponent; beforeEach(() => { - paginationLinks = mountComponent( - paginationLinksComponent, - { - change, - pageInfo, - }, - ); + paginationLinks = mountComponent(paginationLinksComponent, { + change, + pageInfo, + }); [glPagination] = paginationLinks.$children; [destinationComponent] = glPagination.$children; }); @@ -39,34 +36,24 @@ describe('Pagination links component', () => { }); it('should provide translated text to GitLab UI pagination', () => { - Object.entries(translations).forEach(entry => - expect( - destinationComponent[entry[0]], - ).toBe(entry[1]), - ); + Object.entries(translations).forEach(entry => { + expect(destinationComponent[entry[0]]).toBe(entry[1]); + }); }); it('should pass change to GitLab UI pagination', () => { - expect( - Object.is(glPagination.change, change), - ).toBe(true); + expect(Object.is(glPagination.change, change)).toBe(true); }); it('should pass page from pageInfo to GitLab UI pagination', () => { - expect( - destinationComponent.value, - ).toBe(pageInfo.page); + expect(destinationComponent.value).toBe(pageInfo.page); }); it('should pass per page from pageInfo to GitLab UI pagination', () => { - expect( - destinationComponent.perPage, - ).toBe(pageInfo.perPage); + expect(destinationComponent.perPage).toBe(pageInfo.perPage); }); it('should pass total items from pageInfo to GitLab UI pagination', () => { - expect( - destinationComponent.totalRows, - ).toBe(pageInfo.total); + expect(destinationComponent.totalRows).toBe(pageInfo.total); }); }); diff --git a/spec/javascripts/vue_shared/components/panel_resizer_spec.js b/spec/javascripts/vue_shared/components/panel_resizer_spec.js index f1e62069462..49a580be06b 100644 --- a/spec/javascripts/vue_shared/components/panel_resizer_spec.js +++ b/spec/javascripts/vue_shared/components/panel_resizer_spec.js @@ -8,8 +8,23 @@ describe('Panel Resizer component', () => { const triggerEvent = (eventName, el = vm.$el, clientX = 0) => { const event = document.createEvent('MouseEvents'); - event.initMouseEvent(eventName, true, true, window, 1, clientX, 0, clientX, 0, false, false, - false, false, 0, null); + event.initMouseEvent( + eventName, + true, + true, + window, + 1, + clientX, + 0, + clientX, + 0, + false, + false, + false, + false, + 0, + null, + ); el.dispatchEvent(event); }; @@ -53,7 +68,13 @@ describe('Panel Resizer component', () => { triggerEvent('mousedown', vm.$el); triggerEvent('mousemove', document); triggerEvent('mouseup', document); - expect(vm.$emit.calls.allArgs()).toEqual([['resize-start', 100], ['update:size', 100], ['resize-end', 100]]); + + expect(vm.$emit.calls.allArgs()).toEqual([ + ['resize-start', 100], + ['update:size', 100], + ['resize-end', 100], + ]); + expect(vm.size).toBe(100); }); }); diff --git a/spec/javascripts/vue_shared/components/pikaday_spec.js b/spec/javascripts/vue_shared/components/pikaday_spec.js index b349e2a2a81..61f05e7a230 100644 --- a/spec/javascripts/vue_shared/components/pikaday_spec.js +++ b/spec/javascripts/vue_shared/components/pikaday_spec.js @@ -24,6 +24,7 @@ describe('datePicker', () => { vm.$on('hidePicker', hidePicker); vm.$el.querySelector('.dropdown-menu-toggle').click(); + expect(hidePicker).toHaveBeenCalled(); }); }); diff --git a/spec/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon_spec.js b/spec/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon_spec.js index 8c296af6652..6bff1521695 100644 --- a/spec/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon_spec.js +++ b/spec/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon_spec.js @@ -30,6 +30,7 @@ describe('collapsedCalendarIcon', () => { vm.$on('click', click); vm.$el.click(); + expect(click).toHaveBeenCalled(); }); }); diff --git a/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js b/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js index 9d60f9c758f..026a0c7ea09 100644 --- a/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js +++ b/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js @@ -30,11 +30,13 @@ describe('collapsedGroupedDatePicker', () => { it('should emit when sidebar is toggled', () => { vm.$el.querySelector('.gutter-toggle').click(); + expect(vm.toggleSidebar).toHaveBeenCalled(); }); it('should emit when collapsed-calendar-icon is clicked', () => { vm.$el.querySelector('.sidebar-collapsed-icon').click(); + expect(vm.toggleSidebar).toHaveBeenCalled(); }); }); @@ -48,6 +50,7 @@ describe('collapsedGroupedDatePicker', () => { it('should render both collapsed-calendar-icon', () => { const icons = vm.$el.querySelectorAll('.sidebar-collapsed-icon'); + expect(icons.length).toEqual(2); expect(icons[0].innerText.trim()).toEqual('Jul 17 2016'); expect(icons[1].innerText.trim()).toEqual('Jul 17 2017'); @@ -62,6 +65,7 @@ describe('collapsedGroupedDatePicker', () => { it('should render minDate in collapsed-calendar-icon', () => { const icons = vm.$el.querySelectorAll('.sidebar-collapsed-icon'); + expect(icons.length).toEqual(1); expect(icons[0].innerText.trim()).toEqual('From Jul 17 2016'); }); @@ -75,6 +79,7 @@ describe('collapsedGroupedDatePicker', () => { it('should render maxDate in collapsed-calendar-icon', () => { const icons = vm.$el.querySelectorAll('.sidebar-collapsed-icon'); + expect(icons.length).toEqual(1); expect(icons[0].innerText.trim()).toEqual('Until Jul 17 2017'); }); @@ -83,6 +88,7 @@ describe('collapsedGroupedDatePicker', () => { describe('no dates', () => { it('should render None', () => { const icons = vm.$el.querySelectorAll('.sidebar-collapsed-icon'); + expect(icons.length).toEqual(1); expect(icons[0].innerText.trim()).toEqual('None'); }); diff --git a/spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js b/spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js index 8840a5a9dbf..1581f4e3eb1 100644 --- a/spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js +++ b/spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js @@ -17,6 +17,7 @@ describe('sidebarDatePicker', () => { vm.$on('toggleCollapse', toggleCollapse); vm.$el.querySelector('.issuable-sidebar-header .gutter-toggle').click(); + expect(toggleCollapse).toHaveBeenCalled(); }); @@ -62,6 +63,7 @@ describe('sidebarDatePicker', () => { vm.isLoading = false; Vue.nextTick(() => { vm.$el.querySelector('.title .btn-blank').click(); + expect(vm.editing).toEqual(true); done(); }); @@ -92,6 +94,7 @@ describe('sidebarDatePicker', () => { vm.$on('saveDate', saveDate); vm.$el.querySelector('.value-content .btn-blank').click(); + expect(saveDate).toHaveBeenCalled(); }); }); @@ -111,6 +114,7 @@ describe('sidebarDatePicker', () => { vm.$on('toggleCollapse', toggleCollapse); vm.$el.querySelector('.title .gutter-toggle').click(); + expect(toggleCollapse).toHaveBeenCalled(); }); }); diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/base_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/base_spec.js index e8685ab48be..c44b04009ca 100644 --- a/spec/javascripts/vue_shared/components/sidebar/labels_select/base_spec.js +++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/base_spec.js @@ -33,6 +33,7 @@ describe('BaseComponent', () => { it('returns correct string when showCreate prop is `false`', () => { const mockConfigNonEditable = Object.assign({}, mockConfig, { showCreate: false }); const vmNonEditable = createComponent(mockConfigNonEditable); + expect(vmNonEditable.hiddenInputName).toBe('label_id[]'); vmNonEditable.$destroy(); }); @@ -46,6 +47,7 @@ describe('BaseComponent', () => { it('return `Create group label` when `isProject` prop is false', () => { const mockConfigGroup = Object.assign({}, mockConfig, { isProject: false }); const vmGroup = createComponent(mockConfigGroup); + expect(vmGroup.createLabelTitle).toBe('Create group label'); vmGroup.$destroy(); }); @@ -59,6 +61,7 @@ describe('BaseComponent', () => { it('return `Manage group labels` when `isProject` prop is false', () => { const mockConfigGroup = Object.assign({}, mockConfig, { isProject: false }); const vmGroup = createComponent(mockConfigGroup); + expect(vmGroup.manageLabelsTitle).toBe('Manage group labels'); vmGroup.$destroy(); }); @@ -70,6 +73,7 @@ describe('BaseComponent', () => { it('emits onLabelClick event with label and list of labels as params', () => { spyOn(vm, '$emit'); vm.handleClick(mockLabels[0]); + expect(vm.$emit).toHaveBeenCalledWith('onLabelClick', mockLabels[0]); }); }); @@ -78,6 +82,7 @@ describe('BaseComponent', () => { it('emits toggleCollapse event on component', () => { spyOn(vm, '$emit'); vm.handleCollapsedValueClick(); + expect(vm.$emit).toHaveBeenCalledWith('toggleCollapse'); }); }); @@ -86,6 +91,7 @@ describe('BaseComponent', () => { it('emits onDropdownClose event on component', () => { spyOn(vm, '$emit'); vm.handleDropdownHidden(); + expect(vm.$emit).toHaveBeenCalledWith('onDropdownClose'); }); }); @@ -114,6 +120,7 @@ describe('BaseComponent', () => { it('renders `.dropdown-menu` element', () => { const dropdownMenuEl = vm.$el.querySelector('.dropdown-menu'); + expect(dropdownMenuEl).not.toBeNull(); expect(dropdownMenuEl.querySelector('.dropdown-page-one')).not.toBeNull(); expect(dropdownMenuEl.querySelector('.dropdown-content')).not.toBeNull(); diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_button_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_button_spec.js index f25c70db125..5cf6afebd7e 100644 --- a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_button_spec.js +++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_button_spec.js @@ -34,6 +34,7 @@ describe('DropdownButtonComponent', () => { it('returns text as `Label` when `labels` prop is empty array', () => { const mockEmptyLabels = Object.assign({}, componentConfig, { labels: [] }); const vmEmptyLabels = createComponent(mockEmptyLabels); + expect(vmEmptyLabels.dropdownToggleText).toBe('Label'); vmEmptyLabels.$destroy(); }); @@ -43,6 +44,7 @@ describe('DropdownButtonComponent', () => { labels: mockLabels.concat(mockLabels), }); const vmMoreLabels = createComponent(mockMoreLabels); + expect(vmMoreLabels.dropdownToggleText).toBe('Foo Label +1 more'); vmMoreLabels.$destroy(); }); @@ -69,12 +71,14 @@ describe('DropdownButtonComponent', () => { it('renders dropdown toggle text element', () => { const dropdownToggleTextEl = vm.$el.querySelector('.dropdown-toggle-text'); + expect(dropdownToggleTextEl).not.toBeNull(); expect(dropdownToggleTextEl.innerText.trim()).toBe('Foo Label'); }); it('renders dropdown button icon', () => { const dropdownIconEl = vm.$el.querySelector('i.fa'); + expect(dropdownIconEl).not.toBeNull(); expect(dropdownIconEl.classList.contains('fa-chevron-down')).toBe(true); }); diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_create_label_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_create_label_spec.js index ce559fe0335..b8f32f96332 100644 --- a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_create_label_spec.js +++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_create_label_spec.js @@ -6,7 +6,7 @@ import mountComponent from 'spec/helpers/vue_mount_component_helper'; import { mockSuggestedColors } from './mock_data'; -const createComponent = (headerTitle) => { +const createComponent = headerTitle => { const Component = Vue.extend(dropdownCreateLabelComponent); return mountComponent(Component, { @@ -38,13 +38,17 @@ describe('DropdownCreateLabelComponent', () => { }); it('renders `Go back` button on component header', () => { - const backButtonEl = vm.$el.querySelector('.dropdown-title button.dropdown-title-button.dropdown-menu-back'); + const backButtonEl = vm.$el.querySelector( + '.dropdown-title button.dropdown-title-button.dropdown-menu-back', + ); + expect(backButtonEl).not.toBe(null); expect(backButtonEl.querySelector('.fa-arrow-left')).not.toBe(null); }); it('renders component header element as `Create new label` when `headerTitle` prop is not provided', () => { const headerEl = vm.$el.querySelector('.dropdown-title'); + expect(headerEl.innerText.trim()).toContain('Create new label'); }); @@ -52,12 +56,16 @@ describe('DropdownCreateLabelComponent', () => { const headerTitle = 'Create project label'; const vmWithHeaderTitle = createComponent(headerTitle); const headerEl = vmWithHeaderTitle.$el.querySelector('.dropdown-title'); + expect(headerEl.innerText.trim()).toContain(headerTitle); vmWithHeaderTitle.$destroy(); }); it('renders `Close` button on component header', () => { - const closeButtonEl = vm.$el.querySelector('.dropdown-title button.dropdown-title-button.dropdown-menu-close'); + const closeButtonEl = vm.$el.querySelector( + '.dropdown-title button.dropdown-title-button.dropdown-menu-close', + ); + expect(closeButtonEl).not.toBe(null); expect(closeButtonEl.querySelector('.fa-times.dropdown-menu-close-icon')).not.toBe(null); }); @@ -69,23 +77,29 @@ describe('DropdownCreateLabelComponent', () => { it('renders suggested colors list elements', () => { const colorsListContainerEl = vm.$el.querySelector('.suggest-colors.suggest-colors-dropdown'); + expect(colorsListContainerEl).not.toBe(null); expect(colorsListContainerEl.querySelectorAll('a').length).toBe(mockSuggestedColors.length); const colorItemEl = colorsListContainerEl.querySelectorAll('a')[0]; + expect(colorItemEl.dataset.color).toBe(vm.suggestedColors[0]); expect(colorItemEl.getAttribute('style')).toBe('background-color: rgb(0, 51, 204);'); }); it('renders color input element', () => { expect(vm.$el.querySelector('.dropdown-label-color-input')).not.toBe(null); - expect(vm.$el.querySelector('.dropdown-label-color-preview.js-dropdown-label-color-preview')).not.toBe(null); + expect( + vm.$el.querySelector('.dropdown-label-color-preview.js-dropdown-label-color-preview'), + ).not.toBe(null); + expect(vm.$el.querySelector('input#new_label_color.default-dropdown-input')).not.toBe(null); }); it('renders component action buttons', () => { const createBtnEl = vm.$el.querySelector('button.js-new-label-btn'); const cancelBtnEl = vm.$el.querySelector('button.js-cancel-label-btn'); + expect(createBtnEl).not.toBe(null); expect(createBtnEl.innerText.trim()).toBe('Create'); expect(cancelBtnEl.innerText.trim()).toBe('Cancel'); diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_footer_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_footer_spec.js index debeab25bd6..3711e9dac8c 100644 --- a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_footer_spec.js +++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_footer_spec.js @@ -36,19 +36,24 @@ describe('DropdownFooterComponent', () => { describe('template', () => { it('renders link element with `Create new label` when `createLabelTitle` prop is not provided', () => { const createLabelEl = vm.$el.querySelector('.dropdown-footer-list .dropdown-toggle-page'); + expect(createLabelEl).not.toBeNull(); expect(createLabelEl.innerText.trim()).toBe('Create new label'); }); it('renders link element with value of `createLabelTitle` prop', () => { const vmWithCreateLabelTitle = createComponent(mockConfig.labelsWebUrl, createLabelTitle); - const createLabelEl = vmWithCreateLabelTitle.$el.querySelector('.dropdown-footer-list .dropdown-toggle-page'); + const createLabelEl = vmWithCreateLabelTitle.$el.querySelector( + '.dropdown-footer-list .dropdown-toggle-page', + ); + expect(createLabelEl.innerText.trim()).toBe(createLabelTitle); vmWithCreateLabelTitle.$destroy(); }); it('renders link element with `Manage labels` when `manageLabelsTitle` prop is not provided', () => { const manageLabelsEl = vm.$el.querySelector('.dropdown-footer-list .dropdown-external-link'); + expect(manageLabelsEl).not.toBeNull(); expect(manageLabelsEl.getAttribute('href')).toBe(vm.labelsWebUrl); expect(manageLabelsEl.innerText.trim()).toBe('Manage labels'); @@ -60,7 +65,10 @@ describe('DropdownFooterComponent', () => { createLabelTitle, manageLabelsTitle, ); - const manageLabelsEl = vmWithManageLabelsTitle.$el.querySelector('.dropdown-footer-list .dropdown-external-link'); + const manageLabelsEl = vmWithManageLabelsTitle.$el.querySelector( + '.dropdown-footer-list .dropdown-external-link', + ); + expect(manageLabelsEl.innerText.trim()).toBe(manageLabelsTitle); vmWithManageLabelsTitle.$destroy(); }); diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_header_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_header_spec.js index cdf234bb0c4..115e21e4f9f 100644 --- a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_header_spec.js +++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_header_spec.js @@ -24,11 +24,15 @@ describe('DropdownHeaderComponent', () => { describe('template', () => { it('renders header text element', () => { const headerEl = vm.$el.querySelector('.dropdown-title span'); + expect(headerEl.innerText.trim()).toBe('Assign labels'); }); it('renders `Close` button element', () => { - const closeBtnEl = vm.$el.querySelector('.dropdown-title button.dropdown-title-button.dropdown-menu-close'); + const closeBtnEl = vm.$el.querySelector( + '.dropdown-title button.dropdown-title-button.dropdown-menu-close', + ); + expect(closeBtnEl).not.toBeNull(); expect(closeBtnEl.querySelector('.fa-times.dropdown-menu-close-icon')).not.toBeNull(); }); diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_search_input_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_search_input_spec.js index 57608d957e7..c30e619e76b 100644 --- a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_search_input_spec.js +++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_search_input_spec.js @@ -24,6 +24,7 @@ describe('DropdownSearchInputComponent', () => { describe('template', () => { it('renders input element with type `search`', () => { const inputEl = vm.$el.querySelector('input.dropdown-input-field'); + expect(inputEl).not.toBeNull(); expect(inputEl.getAttribute('type')).toBe('search'); }); @@ -33,7 +34,9 @@ describe('DropdownSearchInputComponent', () => { }); it('renders clear search icon element', () => { - expect(vm.$el.querySelector('.fa-times.dropdown-input-clear.js-dropdown-input-clear')).not.toBeNull(); + expect( + vm.$el.querySelector('.fa-times.dropdown-input-clear.js-dropdown-input-clear'), + ).not.toBeNull(); }); }); }); diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_title_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_title_spec.js index 7c3d2711f65..6c84d2e167c 100644 --- a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_title_spec.js +++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_title_spec.js @@ -35,6 +35,7 @@ describe('DropdownTitleComponent', () => { it('renders `Edit` button element', () => { const editBtnEl = vm.$el.querySelector('button.edit-link.js-sidebar-dropdown-toggle'); + expect(editBtnEl).not.toBeNull(); expect(editBtnEl.innerText.trim()).toBe('Edit'); }); diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js index da74595bcdc..9a691116cf8 100644 --- a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js +++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js @@ -29,12 +29,14 @@ describe('DropdownValueCollapsedComponent', () => { describe('labelsList', () => { it('returns empty text when `labels` prop is empty array', () => { const vmEmptyLabels = createComponent([]); + expect(vmEmptyLabels.labelsList).toBe(''); vmEmptyLabels.$destroy(); }); it('returns labels names separated by coma when `labels` prop has more than one item', () => { const vmMoreLabels = createComponent(mockLabels.concat(mockLabels)); + expect(vmMoreLabels.labelsList).toBe('Foo Label, Foo Label'); vmMoreLabels.$destroy(); }); @@ -46,6 +48,7 @@ describe('DropdownValueCollapsedComponent', () => { } const vmMoreLabels = createComponent(mockMoreLabels); + expect(vmMoreLabels.labelsList).toBe('Foo Label, Foo Label, Foo Label, Foo Label, Foo Label, and 2 more'); vmMoreLabels.$destroy(); }); @@ -61,6 +64,7 @@ describe('DropdownValueCollapsedComponent', () => { it('emits onValueClick event on component', () => { spyOn(vm, '$emit'); vm.handleClick(); + expect(vm.$emit).toHaveBeenCalledWith('onValueClick'); }); }); diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_spec.js index 370a296bd8f..3fff781594f 100644 --- a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_spec.js +++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_spec.js @@ -33,6 +33,7 @@ describe('DropdownValueComponent', () => { describe('isEmpty', () => { it('returns true if `labels` prop is empty', () => { const vmEmptyLabels = createComponent([]); + expect(vmEmptyLabels.isEmpty).toBe(true); vmEmptyLabels.$destroy(); }); @@ -46,9 +47,11 @@ describe('DropdownValueComponent', () => { describe('methods', () => { describe('labelFilterUrl', () => { it('returns URL string starting with labelFilterBasePath and encoded label.title', () => { - expect(vm.labelFilterUrl({ - title: 'Foo bar', - })).toBe('/gitlab-org/my-project/issues?label_name[]=Foo%20bar'); + expect( + vm.labelFilterUrl({ + title: 'Foo bar', + }), + ).toBe('/gitlab-org/my-project/issues?label_name[]=Foo%20bar'); }); }); @@ -68,21 +71,29 @@ describe('DropdownValueComponent', () => { describe('template', () => { it('renders component container element with classes `hide-collapsed value issuable-show-labels`', () => { - expect(vm.$el.classList.contains('hide-collapsed', 'value', 'issuable-show-labels')).toBe(true); + expect(vm.$el.classList.contains('hide-collapsed', 'value', 'issuable-show-labels')).toBe( + true, + ); }); it('render slot content inside component when `labels` prop is empty', () => { const vmEmptyLabels = createComponent([]); - expect(vmEmptyLabels.$el.querySelector('.text-secondary').innerText.trim()).toBe(mockConfig.emptyValueText); + + expect(vmEmptyLabels.$el.querySelector('.text-secondary').innerText.trim()).toBe( + mockConfig.emptyValueText, + ); vmEmptyLabels.$destroy(); }); it('renders label element with filter URL', () => { - expect(vm.$el.querySelector('a').getAttribute('href')).toBe('/gitlab-org/my-project/issues?label_name[]=Foo%20Label'); + expect(vm.$el.querySelector('a').getAttribute('href')).toBe( + '/gitlab-org/my-project/issues?label_name[]=Foo%20Label', + ); }); it('renders label element with tooltip and styles based on label details', () => { const labelEl = vm.$el.querySelector('a span.badge.color-label'); + expect(labelEl).not.toBeNull(); expect(labelEl.dataset.placement).toBe('bottom'); expect(labelEl.dataset.container).toBe('body'); diff --git a/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js b/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js index f1fe2e996fc..073d111989c 100644 --- a/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js +++ b/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js @@ -4,16 +4,20 @@ import stackedProgressBarComponent from '~/vue_shared/components/stacked_progres import mountComponent from 'spec/helpers/vue_mount_component_helper'; -const createComponent = (config) => { +const createComponent = config => { const Component = Vue.extend(stackedProgressBarComponent); - const defaultConfig = Object.assign({}, { - successLabel: 'Synced', - failureLabel: 'Failed', - neutralLabel: 'Out of sync', - successCount: 25, - failureCount: 10, - totalCount: 5000, - }, config); + const defaultConfig = Object.assign( + {}, + { + successLabel: 'Synced', + failureLabel: 'Failed', + neutralLabel: 'Out of sync', + successCount: 25, + failureCount: 10, + totalCount: 5000, + }, + config, + ); return mountComponent(Component, defaultConfig); }; @@ -72,6 +76,7 @@ describe('StackedProgressBarComponent', () => { it('renders empty state when count is unavailable', () => { const vmX = createComponent({ totalCount: 0, successCount: 0, failureCount: 0 }); + expect(vmX.$el.querySelectorAll('.status-unavailable').length).not.toBe(0); vmX.$destroy(); }); diff --git a/spec/javascripts/vue_shared/components/table_pagination_spec.js b/spec/javascripts/vue_shared/components/table_pagination_spec.js index d193667210b..0dcb712e720 100644 --- a/spec/javascripts/vue_shared/components/table_pagination_spec.js +++ b/spec/javascripts/vue_shared/components/table_pagination_spec.js @@ -11,7 +11,7 @@ describe('Pagination component', () => { spy = jasmine.createSpy('spy'); PaginationComponent = Vue.extend(paginationComp); - mountComponent = function (props) { + mountComponent = function(props) { return new PaginationComponent({ propsData: props, }).$mount(); @@ -72,6 +72,7 @@ describe('Pagination component', () => { }); component.$el.querySelector('.js-previous-button a').click(); + expect(spy).toHaveBeenCalledWith(1); }); }); @@ -138,9 +139,7 @@ describe('Pagination component', () => { change: spy, }); - expect( - component.$el.querySelector('.js-next-button').textContent.trim(), - ).toEqual('Next'); + expect(component.$el.querySelector('.js-next-button').textContent.trim()).toEqual('Next'); component.$el.querySelector('.js-next-button a').click(); diff --git a/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js b/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js index b4fb568f1d4..745571d0a97 100644 --- a/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js +++ b/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js @@ -22,9 +22,10 @@ describe('Time ago with tooltip component', () => { }).$mount(); expect(vm.$el.tagName).toEqual('TIME'); - expect( - vm.$el.getAttribute('data-original-title'), - ).toEqual(formatDate('2017-05-08T14:57:39.781Z')); + expect(vm.$el.getAttribute('data-original-title')).toEqual( + formatDate('2017-05-08T14:57:39.781Z'), + ); + expect(vm.$el.getAttribute('data-placement')).toEqual('top'); const timeago = getTimeago(); diff --git a/spec/javascripts/vue_shared/components/toggle_button_spec.js b/spec/javascripts/vue_shared/components/toggle_button_spec.js index 71952cc39e0..444ca451534 100644 --- a/spec/javascripts/vue_shared/components/toggle_button_spec.js +++ b/spec/javascripts/vue_shared/components/toggle_button_spec.js @@ -51,9 +51,11 @@ describe('Toggle Button', () => { it('sets aria-label representing toggle state', () => { vm.value = true; + expect(vm.ariaLabel).toEqual('Toggle Status: ON'); vm.value = false; + expect(vm.ariaLabel).toEqual('Toggle Status: OFF'); }); diff --git a/spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js b/spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js index 8465757deb6..997d84dcc42 100644 --- a/spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js +++ b/spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js @@ -79,9 +79,7 @@ describe('TooltipOnTruncate component', () => { }, }; - vm = mountTooltipOnTruncate(options, (h) => [ - h('a', { style: STYLE_TRUNCATED }, TEST_TITLE), - ]); + vm = mountTooltipOnTruncate(options, h => [h('a', { style: STYLE_TRUNCATED }, TEST_TITLE)]); vm.$nextTick() .then(() => { @@ -99,9 +97,7 @@ describe('TooltipOnTruncate component', () => { }, }; - vm = mountTooltipOnTruncate(options, (h) => [ - h('a', { style: STYLE_NORMAL }, TEST_TITLE), - ]); + vm = mountTooltipOnTruncate(options, h => [h('a', { style: STYLE_NORMAL }, TEST_TITLE)]); vm.$nextTick() .then(() => { @@ -118,11 +114,11 @@ describe('TooltipOnTruncate component', () => { style: STYLE_NORMAL, props: { title: TEST_TITLE, - truncateTarget: (el) => el.childNodes[1], + truncateTarget: el => el.childNodes[1], }, }; - vm = mountTooltipOnTruncate(options, (h) => [ + vm = mountTooltipOnTruncate(options, h => [ h('a', { style: STYLE_NORMAL }, TEST_TITLE), h('span', { style: STYLE_TRUNCATED }, TEST_TITLE), ]); @@ -146,9 +142,7 @@ describe('TooltipOnTruncate component', () => { }, }; - vm = mountTooltipOnTruncate(options, (h) => [ - h('a', { style: STYLE_TRUNCATED }, TEST_TITLE), - ]); + vm = mountTooltipOnTruncate(options, h => [h('a', { style: STYLE_TRUNCATED }, TEST_TITLE)]); vm.$nextTick() .then(() => { diff --git a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js index 4c5c242cbb3..50b8d49d4bd 100644 --- a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js +++ b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js @@ -2,8 +2,8 @@ import _ from 'underscore'; import Vue from 'vue'; import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue'; -describe('User Avatar Link Component', function () { - beforeEach(function () { +describe('User Avatar Link Component', function() { + beforeEach(function() { this.propsData = { linkHref: 'myavatarurl.com', imgSize: 99, @@ -24,67 +24,76 @@ describe('User Avatar Link Component', function () { [this.userAvatarImage] = this.userAvatarLink.$children; }); - it('should return a defined Vue component', function () { + it('should return a defined Vue component', function() { expect(this.userAvatarLink).toBeDefined(); }); - it('should have user-avatar-image registered as child component', function () { + it('should have user-avatar-image registered as child component', function() { expect(this.userAvatarLink.$options.components.userAvatarImage).toBeDefined(); }); - it('user-avatar-link should have user-avatar-image as child component', function () { + it('user-avatar-link should have user-avatar-image as child component', function() { expect(this.userAvatarImage).toBeDefined(); }); - it('should render <a> as a child element', function () { + it('should render <a> as a child element', function() { expect(this.userAvatarLink.$el.tagName).toBe('A'); }); - it('should have <img> as a child element', function () { + it('should have <img> as a child element', function() { expect(this.userAvatarLink.$el.querySelector('img')).not.toBeNull(); }); - it('should return neccessary props as defined', function () { + it('should return neccessary props as defined', function() { _.each(this.propsData, (val, key) => { expect(this.userAvatarLink[key]).toBeDefined(); }); }); - describe('no username', function () { - beforeEach(function (done) { + describe('no username', function() { + beforeEach(function(done) { this.userAvatarLink.username = ''; Vue.nextTick(done); }); - it('should only render image tag in link', function () { + it('should only render image tag in link', function() { const childElements = this.userAvatarLink.$el.childNodes; + expect(childElements[0].tagName).toBe('IMG'); // Vue will render the hidden component as <!----> expect(childElements[1].tagName).toBeUndefined(); }); - it('should render avatar image tooltip', function () { - expect(this.userAvatarLink.$el.querySelector('img').dataset.originalTitle).toEqual(this.propsData.tooltipText); + it('should render avatar image tooltip', function() { + expect(this.userAvatarLink.$el.querySelector('img').dataset.originalTitle).toEqual( + this.propsData.tooltipText, + ); }); }); - describe('username', function () { - it('should not render avatar image tooltip', function () { + describe('username', function() { + it('should not render avatar image tooltip', function() { expect(this.userAvatarLink.$el.querySelector('img').dataset.originalTitle).toEqual(''); }); - it('should render username prop in <span>', function () { - expect(this.userAvatarLink.$el.querySelector('span').innerText.trim()).toEqual(this.propsData.username); + it('should render username prop in <span>', function() { + expect(this.userAvatarLink.$el.querySelector('span').innerText.trim()).toEqual( + this.propsData.username, + ); }); - it('should render text tooltip for <span>', function () { - expect(this.userAvatarLink.$el.querySelector('span').dataset.originalTitle).toEqual(this.propsData.tooltipText); + it('should render text tooltip for <span>', function() { + expect(this.userAvatarLink.$el.querySelector('span').dataset.originalTitle).toEqual( + this.propsData.tooltipText, + ); }); - it('should render text tooltip placement for <span>', function () { - expect(this.userAvatarLink.$el.querySelector('span').getAttribute('tooltip-placement')).toEqual(this.propsData.tooltipPlacement); + it('should render text tooltip placement for <span>', function() { + expect( + this.userAvatarLink.$el.querySelector('span').getAttribute('tooltip-placement'), + ).toEqual(this.propsData.tooltipPlacement); }); }); }); diff --git a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_svg_spec.js b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_svg_spec.js index b8d639ffbec..9152fa8e12f 100644 --- a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_svg_spec.js +++ b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_svg_spec.js @@ -4,9 +4,9 @@ import avatarSvg from 'icons/_icon_random.svg'; const UserAvatarSvgComponent = Vue.extend(UserAvatarSvg); -describe('User Avatar Svg Component', function () { - describe('Initialization', function () { - beforeEach(function () { +describe('User Avatar Svg Component', function() { + describe('Initialization', function() { + beforeEach(function() { this.propsData = { size: 99, svg: avatarSvg, @@ -17,11 +17,11 @@ describe('User Avatar Svg Component', function () { }).$mount(); }); - it('should return a defined Vue component', function () { + it('should return a defined Vue component', function() { expect(this.userAvatarSvg).toBeDefined(); }); - it('should have <svg> as a child element', function () { + it('should have <svg> as a child element', function() { expect(this.userAvatarSvg.$el.tagName).toEqual('svg'); expect(this.userAvatarSvg.$el.innerHTML).toContain('<path'); }); diff --git a/spec/javascripts/vue_shared/directives/tooltip_spec.js b/spec/javascripts/vue_shared/directives/tooltip_spec.js index 4a644913e44..305d2fd5af4 100644 --- a/spec/javascripts/vue_shared/directives/tooltip_spec.js +++ b/spec/javascripts/vue_shared/directives/tooltip_spec.js @@ -58,7 +58,11 @@ describe('Tooltip directive', () => { }); it('should have tooltip plugin applied to all instances', () => { - expect($(vm.$el).find('.js-look-for-tooltip').data('bs.tooltip')).toBeDefined(); + expect( + $(vm.$el) + .find('.js-look-for-tooltip') + .data('bs.tooltip'), + ).toBeDefined(); }); }); }); diff --git a/spec/javascripts/zen_mode_spec.js b/spec/javascripts/zen_mode_spec.js index bdeebe0de75..e5f1e6ae937 100644 --- a/spec/javascripts/zen_mode_spec.js +++ b/spec/javascripts/zen_mode_spec.js @@ -46,12 +46,14 @@ describe('ZenMode', () => { it('should not call dropzone if element is not dropzone valid', () => { $('.div-dropzone').addClass('js-invalid-dropzone'); exitZen(); + expect(dropzoneForElementSpy.calls.count()).toEqual(0); }); it('should call dropzone if element is dropzone valid', () => { $('.div-dropzone').removeClass('js-invalid-dropzone'); exitZen(); + expect(dropzoneForElementSpy.calls.count()).toEqual(2); }); }); @@ -60,12 +62,14 @@ describe('ZenMode', () => { it('pauses Mousetrap', () => { const mouseTrapPauseSpy = spyOn(Mousetrap, 'pause'); enterZen(); + expect(mouseTrapPauseSpy).toHaveBeenCalled(); }); it('removes textarea styling', () => { $('.notes-form textarea').attr('style', 'height: 400px'); enterZen(); + expect($('.notes-form textarea')).not.toHaveAttr('style'); }); }); @@ -75,6 +79,7 @@ describe('ZenMode', () => { it('exits on Escape', () => { escapeKeydown(); + expect($('.notes-form .zen-backdrop')).not.toHaveClass('fullscreen'); }); }); @@ -85,12 +90,14 @@ describe('ZenMode', () => { it('unpauses Mousetrap', () => { const mouseTrapUnpauseSpy = spyOn(Mousetrap, 'unpause'); exitZen(); + expect(mouseTrapUnpauseSpy).toHaveBeenCalled(); }); it('restores the scroll position', () => { spyOn(zen, 'scrollTo'); exitZen(); + expect(zen.scrollTo).toHaveBeenCalled(); }); }); |