diff options
Diffstat (limited to 'spec/frontend/blob')
6 files changed, 295 insertions, 7 deletions
diff --git a/spec/frontend/blob/components/__snapshots__/blob_header_filepath_spec.js.snap b/spec/frontend/blob/components/__snapshots__/blob_header_filepath_spec.js.snap index 46a5631b028..d698ee72ea4 100644 --- a/spec/frontend/blob/components/__snapshots__/blob_header_filepath_spec.js.snap +++ b/spec/frontend/blob/components/__snapshots__/blob_header_filepath_spec.js.snap @@ -20,12 +20,6 @@ exports[`Blob Header Filepath rendering matches the snapshot 1`] = ` foo/bar/dummy.md </strong> - <small - class="mr-2" - > - a lot - </small> - <clipboard-button-stub category="tertiary" cssclass="btn-clipboard btn-transparent lh-100 position-static" @@ -36,5 +30,13 @@ exports[`Blob Header Filepath rendering matches the snapshot 1`] = ` tooltipplacement="top" variant="default" /> + + <small + class="mr-2" + > + a lot + </small> + + <!----> </div> `; diff --git a/spec/frontend/blob/components/__snapshots__/blob_header_spec.js.snap b/spec/frontend/blob/components/__snapshots__/blob_header_spec.js.snap index db9684239a1..22bec77276b 100644 --- a/spec/frontend/blob/components/__snapshots__/blob_header_spec.js.snap +++ b/spec/frontend/blob/components/__snapshots__/blob_header_spec.js.snap @@ -17,7 +17,7 @@ exports[`Blob Header Default Actions rendering matches the snapshot 1`] = ` </div> <div - class="gl-display-none gl-sm-display-flex" + class="gl-sm-display-flex file-actions" > <viewer-switcher-stub value="simple" diff --git a/spec/frontend/blob/components/blob_edit_header_spec.js b/spec/frontend/blob/components/blob_edit_header_spec.js index ac3080c65a5..910fc5c946d 100644 --- a/spec/frontend/blob/components/blob_edit_header_spec.js +++ b/spec/frontend/blob/components/blob_edit_header_spec.js @@ -44,6 +44,8 @@ describe('Blob Header Editing', () => { const inputComponent = wrapper.find(GlFormInput); const newValue = 'bar.txt'; + // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details + // eslint-disable-next-line no-restricted-syntax wrapper.setData({ name: newValue, }); diff --git a/spec/frontend/blob/components/blob_header_filepath_spec.js b/spec/frontend/blob/components/blob_header_filepath_spec.js index d935f73c0d1..8220b598ff6 100644 --- a/spec/frontend/blob/components/blob_header_filepath_spec.js +++ b/spec/frontend/blob/components/blob_header_filepath_spec.js @@ -1,3 +1,4 @@ +import { GlBadge } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import BlobHeaderFilepath from '~/blob/components/blob_header_filepath.vue'; import { numberToHumanSize } from '~/lib/utils/number_utils'; @@ -24,6 +25,8 @@ describe('Blob Header Filepath', () => { wrapper.destroy(); }); + const findBadge = () => wrapper.find(GlBadge); + describe('rendering', () => { it('matches the snapshot', () => { createComponent(); @@ -54,6 +57,11 @@ describe('Blob Header Filepath', () => { expect(wrapper.vm.blobSize).toBe('a lot'); }); + it('renders LFS badge if LFS if enabled', () => { + createComponent({ storedExternally: true, externalStorage: 'lfs' }); + expect(findBadge().text()).toBe('LFS'); + }); + it('renders a slot and prepends its contents to the existing one', () => { const slotContent = 'Foo Bar'; createComponent( diff --git a/spec/frontend/blob/line_highlighter_spec.js b/spec/frontend/blob/line_highlighter_spec.js new file mode 100644 index 00000000000..330f1f3137e --- /dev/null +++ b/spec/frontend/blob/line_highlighter_spec.js @@ -0,0 +1,275 @@ +/* eslint-disable no-return-assign, no-new, no-underscore-dangle */ + +import $ from 'jquery'; +import LineHighlighter from '~/blob/line_highlighter'; +import * as utils from '~/lib/utils/common_utils'; + +describe('LineHighlighter', () => { + const testContext = {}; + + const clickLine = (number, eventData = {}) => { + if ($.isEmptyObject(eventData)) { + return $(`#L${number}`).click(); + } + const e = $.Event('click', eventData); + return $(`#L${number}`).trigger(e); + }; + beforeEach(() => { + loadFixtures('static/line_highlighter.html'); + testContext.class = new LineHighlighter(); + testContext.css = testContext.class.highlightLineClass; + return (testContext.spies = { + __setLocationHash__: jest + .spyOn(testContext.class, '__setLocationHash__') + .mockImplementation(() => {}), + }); + }); + + describe('behavior', () => { + it('highlights one line given in the URL hash', () => { + new LineHighlighter({ hash: '#L13' }); + + expect($('#LC13')).toHaveClass(testContext.css); + }); + + it('highlights one line given in the URL hash with given CSS class name', () => { + 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', () => { + new LineHighlighter({ hash: '#L5-25' }); + + expect($(`.${testContext.css}`).length).toBe(21); + for (let line = 5; line <= 25; line += 1) { + expect($(`#LC${line}`)).toHaveClass(testContext.css); + } + }); + + it('highlights a range of lines given in the URL hash using GitHub format', () => { + new LineHighlighter({ hash: '#L5-L25' }); + + expect($(`.${testContext.css}`).length).toBe(21); + for (let line = 5; line <= 25; line += 1) { + expect($(`#LC${line}`)).toHaveClass(testContext.css); + } + }); + + it('scrolls to the first highlighted line on initial load', () => { + jest.spyOn(utils, 'scrollToElement'); + new LineHighlighter({ hash: '#L5-25' }); + + expect(utils.scrollToElement).toHaveBeenCalledWith('#L5', expect.anything()); + }); + + it('discards click events', () => { + const clickSpy = jest.fn(); + + $('a[data-line-number]').click(clickSpy); + + clickLine(13); + + expect(clickSpy.mock.calls[0][0].isDefaultPrevented()).toEqual(true); + }); + + it('handles garbage input from the hash', () => { + const func = () => { + return new LineHighlighter({ fileHolderSelector: '#blob-content-holder' }); + }; + + expect(func).not.toThrow(); + }); + + it('handles hashchange event', () => { + const highlighter = new LineHighlighter(); + + jest.spyOn(highlighter, 'highlightHash').mockImplementation(() => {}); + + window.dispatchEvent(new Event('hashchange'), 'L15'); + + expect(highlighter.highlightHash).toHaveBeenCalled(); + }); + }); + + describe('clickHandler', () => { + it('handles clicking on a child icon element', () => { + const spy = jest.spyOn(testContext.class, 'setHash'); + $('#L13 [data-testid="link-icon"]').mousedown().click(); + + expect(spy).toHaveBeenCalledWith(13); + expect($('#LC13')).toHaveClass(testContext.css); + }); + + describe('without shiftKey', () => { + it('highlights one line when clicked', () => { + clickLine(13); + + expect($('#LC13')).toHaveClass(testContext.css); + }); + + it('unhighlights previously highlighted lines', () => { + clickLine(13); + clickLine(20); + + expect($('#LC13')).not.toHaveClass(testContext.css); + expect($('#LC20')).toHaveClass(testContext.css); + }); + + it('sets the hash', () => { + const spy = jest.spyOn(testContext.class, 'setHash'); + clickLine(13); + + expect(spy).toHaveBeenCalledWith(13); + }); + }); + + describe('with shiftKey', () => { + it('sets the hash', () => { + const spy = jest.spyOn(testContext.class, 'setHash'); + clickLine(13); + clickLine(20, { + shiftKey: true, + }); + + expect(spy).toHaveBeenCalledWith(13); + expect(spy).toHaveBeenCalledWith(13, 20); + }); + + describe('without existing highlight', () => { + it('highlights the clicked line', () => { + clickLine(13, { + shiftKey: true, + }); + + expect($('#LC13')).toHaveClass(testContext.css); + expect($(`.${testContext.css}`).length).toBe(1); + }); + + it('sets the hash', () => { + const spy = jest.spyOn(testContext.class, 'setHash'); + clickLine(13, { + shiftKey: true, + }); + + expect(spy).toHaveBeenCalledWith(13); + }); + }); + + describe('with existing single-line highlight', () => { + it('uses existing line as last line when target is lesser', () => { + clickLine(20); + clickLine(15, { + shiftKey: true, + }); + + expect($(`.${testContext.css}`).length).toBe(6); + for (let line = 15; line <= 20; line += 1) { + expect($(`#LC${line}`)).toHaveClass(testContext.css); + } + }); + + it('uses existing line as first line when target is greater', () => { + clickLine(5); + clickLine(10, { + shiftKey: true, + }); + + expect($(`.${testContext.css}`).length).toBe(6); + for (let line = 5; line <= 10; line += 1) { + expect($(`#LC${line}`)).toHaveClass(testContext.css); + } + }); + }); + + describe('with existing multi-line highlight', () => { + beforeEach(() => { + clickLine(10, { + shiftKey: true, + }); + clickLine(13, { + shiftKey: true, + }); + }); + + it('uses target as first line when it is less than existing first line', () => { + clickLine(5, { + shiftKey: true, + }); + + expect($(`.${testContext.css}`).length).toBe(6); + for (let line = 5; line <= 10; line += 1) { + expect($(`#LC${line}`)).toHaveClass(testContext.css); + } + }); + + it('uses target as last line when it is greater than existing first line', () => { + clickLine(15, { + shiftKey: true, + }); + + expect($(`.${testContext.css}`).length).toBe(6); + for (let line = 10; line <= 15; line += 1) { + expect($(`#LC${line}`)).toHaveClass(testContext.css); + } + }); + }); + }); + }); + + describe('hashToRange', () => { + beforeEach(() => { + testContext.subject = testContext.class.hashToRange; + }); + + it('extracts a single line number from the hash', () => { + expect(testContext.subject('#L5')).toEqual([5, null]); + }); + + it('extracts a range of line numbers from the hash', () => { + expect(testContext.subject('#L5-15')).toEqual([5, 15]); + }); + + it('returns [null, null] when the hash is not a line number', () => { + expect(testContext.subject('#foo')).toEqual([null, null]); + }); + }); + + describe('highlightLine', () => { + beforeEach(() => { + testContext.subject = testContext.class.highlightLine; + }); + + it('highlights the specified line', () => { + testContext.subject(13); + + expect($('#LC13')).toHaveClass(testContext.css); + }); + + it('accepts a String-based number', () => { + testContext.subject('13'); + + expect($('#LC13')).toHaveClass(testContext.css); + }); + }); + + describe('setHash', () => { + beforeEach(() => { + testContext.subject = testContext.class.setHash; + }); + + it('sets the location hash for a single line', () => { + testContext.subject(5); + + expect(testContext.spies.__setLocationHash__).toHaveBeenCalledWith('#L5'); + }); + + it('sets the location hash for a range', () => { + testContext.subject(5, 15); + + expect(testContext.spies.__setLocationHash__).toHaveBeenCalledWith('#L5-15'); + }); + }); +}); diff --git a/spec/frontend/blob/viewer/index_spec.js b/spec/frontend/blob/viewer/index_spec.js index 061ac7ad167..9e9f866d40c 100644 --- a/spec/frontend/blob/viewer/index_spec.js +++ b/spec/frontend/blob/viewer/index_spec.js @@ -21,6 +21,7 @@ describe('Blob viewer', () => { setTestTimeout(2000); beforeEach(() => { + window.gon.features = { refactorBlobViewer: false }; // This file is based on the old (non-refactored) blob viewer jest.spyOn(window, 'requestIdleCallback').mockImplementation(execImmediately); $.fn.extend(jQueryMock); mock = new MockAdapter(axios); |