summaryrefslogtreecommitdiff
path: root/spec/frontend/line_highlighter_spec.js
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/line_highlighter_spec.js')
-rw-r--r--spec/frontend/line_highlighter_spec.js268
1 files changed, 268 insertions, 0 deletions
diff --git a/spec/frontend/line_highlighter_spec.js b/spec/frontend/line_highlighter_spec.js
new file mode 100644
index 00000000000..0da1ea1df2d
--- /dev/null
+++ b/spec/frontend/line_highlighter_spec.js
@@ -0,0 +1,268 @@
+/* eslint-disable no-return-assign, no-new, no-underscore-dangle */
+
+import $ from 'jquery';
+import LineHighlighter from '~/line_highlighter';
+
+describe('LineHighlighter', () => {
+ const testContext = {};
+
+ preloadFixtures('static/line_highlighter.html');
+ 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('scrolls to the first highlighted line on initial load', () => {
+ const spy = jest.spyOn($, 'scrollTo');
+ new LineHighlighter({ hash: '#L5-25' });
+
+ expect(spy).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 i')
+ .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');
+ });
+ });
+});