Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-02-06 15:10:29 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-02-06 15:10:29 +0300
commit5564275a0b378298dc6281599cbfe71a937109ff (patch)
treea468e1e60046356410219c35c23a8a428c5e2c5e /spec/frontend
parentd87918510a866a5fcbbc2f899ad65c6938ebf5f5 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
-rw-r--r--spec/frontend/code_navigation/components/__snapshots__/popover_spec.js.snap39
-rw-r--r--spec/frontend/code_navigation/components/app_spec.js64
-rw-r--r--spec/frontend/code_navigation/components/popover_spec.js58
-rw-r--r--spec/frontend/code_navigation/store/actions_spec.js221
-rw-r--r--spec/frontend/code_navigation/store/mutations_spec.js63
-rw-r--r--spec/frontend/code_navigation/utils/index_spec.js58
6 files changed, 503 insertions, 0 deletions
diff --git a/spec/frontend/code_navigation/components/__snapshots__/popover_spec.js.snap b/spec/frontend/code_navigation/components/__snapshots__/popover_spec.js.snap
new file mode 100644
index 00000000000..dda6d68018e
--- /dev/null
+++ b/spec/frontend/code_navigation/components/__snapshots__/popover_spec.js.snap
@@ -0,0 +1,39 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Code navigation popover component renders popover 1`] = `
+<div
+ class="popover code-navigation-popover popover-font-size-normal gl-popover bs-popover-bottom show"
+ style="left: 0px; top: 0px;"
+>
+ <div
+ class="arrow"
+ style="left: 0px;"
+ />
+
+ <div
+ class="border-bottom"
+ >
+ <pre
+ class="border-0 bg-transparent m-0 code highlight"
+ >
+ console.log
+ </pre>
+ </div>
+
+ <div
+ class="popover-body"
+ >
+ <gl-button-stub
+ class="w-100"
+ href="http://test.com"
+ size="md"
+ target="_blank"
+ variant="default"
+ >
+
+ Go to definition
+
+ </gl-button-stub>
+ </div>
+</div>
+`;
diff --git a/spec/frontend/code_navigation/components/app_spec.js b/spec/frontend/code_navigation/components/app_spec.js
new file mode 100644
index 00000000000..cfdc0dcc6cc
--- /dev/null
+++ b/spec/frontend/code_navigation/components/app_spec.js
@@ -0,0 +1,64 @@
+import Vuex from 'vuex';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import createState from '~/code_navigation/store/state';
+import App from '~/code_navigation/components/app.vue';
+import Popover from '~/code_navigation/components/popover.vue';
+
+const localVue = createLocalVue();
+const fetchData = jest.fn();
+const showDefinition = jest.fn();
+let wrapper;
+
+localVue.use(Vuex);
+
+function factory(initialState = {}) {
+ const store = new Vuex.Store({
+ state: {
+ ...createState(),
+ ...initialState,
+ },
+ actions: {
+ fetchData,
+ showDefinition,
+ },
+ });
+
+ wrapper = shallowMount(App, { store, localVue });
+}
+
+describe('Code navigation app component', () => {
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('fetches data on mount', () => {
+ factory();
+
+ expect(fetchData).toHaveBeenCalled();
+ });
+
+ it('hides popover when no definition set', () => {
+ factory();
+
+ expect(wrapper.find(Popover).exists()).toBe(false);
+ });
+
+ it('renders popover when definition set', () => {
+ factory({
+ currentDefinition: { hover: 'console' },
+ currentDefinitionPosition: { x: 0 },
+ });
+
+ expect(wrapper.find(Popover).exists()).toBe(true);
+ });
+
+ it('calls showDefinition when clicking blob viewer', () => {
+ setFixtures('<div class="blob-viewer"></div>');
+
+ factory();
+
+ document.querySelector('.blob-viewer').click();
+
+ expect(showDefinition).toHaveBeenCalled();
+ });
+});
diff --git a/spec/frontend/code_navigation/components/popover_spec.js b/spec/frontend/code_navigation/components/popover_spec.js
new file mode 100644
index 00000000000..ad05504a224
--- /dev/null
+++ b/spec/frontend/code_navigation/components/popover_spec.js
@@ -0,0 +1,58 @@
+import { shallowMount } from '@vue/test-utils';
+import Popover from '~/code_navigation/components/popover.vue';
+
+const MOCK_CODE_DATA = Object.freeze({
+ hover: [
+ {
+ language: 'javascript',
+ value: 'console.log',
+ },
+ ],
+ definition_url: 'http://test.com',
+});
+
+const MOCK_DOCS_DATA = Object.freeze({
+ hover: [
+ {
+ language: null,
+ value: 'console.log',
+ },
+ ],
+ definition_url: 'http://test.com',
+});
+
+let wrapper;
+
+function factory(position, data) {
+ wrapper = shallowMount(Popover, { propsData: { position, data } });
+}
+
+describe('Code navigation popover component', () => {
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders popover', () => {
+ factory({ x: 0, y: 0, height: 0 }, MOCK_CODE_DATA);
+
+ expect(wrapper.element).toMatchSnapshot();
+ });
+
+ describe('code output', () => {
+ it('renders code output', () => {
+ factory({ x: 0, y: 0, height: 0 }, MOCK_CODE_DATA);
+
+ expect(wrapper.find({ ref: 'code-output' }).exists()).toBe(true);
+ expect(wrapper.find({ ref: 'doc-output' }).exists()).toBe(false);
+ });
+ });
+
+ describe('documentation output', () => {
+ it('renders code output', () => {
+ factory({ x: 0, y: 0, height: 0 }, MOCK_DOCS_DATA);
+
+ expect(wrapper.find({ ref: 'code-output' }).exists()).toBe(false);
+ expect(wrapper.find({ ref: 'doc-output' }).exists()).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/code_navigation/store/actions_spec.js b/spec/frontend/code_navigation/store/actions_spec.js
new file mode 100644
index 00000000000..5e29a76f804
--- /dev/null
+++ b/spec/frontend/code_navigation/store/actions_spec.js
@@ -0,0 +1,221 @@
+import MockAdapter from 'axios-mock-adapter';
+import testAction from 'helpers/vuex_action_helper';
+import actions from '~/code_navigation/store/actions';
+import createFlash from '~/flash';
+import axios from '~/lib/utils/axios_utils';
+import { setCurrentHoverElement, addInteractionClass } from '~/code_navigation/utils';
+
+jest.mock('~/flash');
+jest.mock('~/code_navigation/utils');
+
+describe('Code navigation actions', () => {
+ describe('setInitialData', () => {
+ it('commits SET_INITIAL_DATA', done => {
+ testAction(
+ actions.setInitialData,
+ { projectPath: 'test' },
+ {},
+ [{ type: 'SET_INITIAL_DATA', payload: { projectPath: 'test' } }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('requestDataError', () => {
+ it('commits REQUEST_DATA_ERROR', () =>
+ testAction(actions.requestDataError, null, {}, [{ type: 'REQUEST_DATA_ERROR' }], []));
+
+ it('creates a flash message', () =>
+ testAction(actions.requestDataError, null, {}, [{ type: 'REQUEST_DATA_ERROR' }], []).then(
+ () => {
+ expect(createFlash).toHaveBeenCalled();
+ },
+ ));
+ });
+
+ describe('fetchData', () => {
+ let mock;
+ const state = {
+ projectPath: 'gitlab-org/gitlab',
+ commitId: '123',
+ blobPath: 'index',
+ };
+ const apiUrl = '/api/1/projects/gitlab-org%2Fgitlab/commits/123/lsif/info';
+
+ beforeEach(() => {
+ window.gon = { api_version: '1' };
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ describe('success', () => {
+ beforeEach(() => {
+ mock.onGet(apiUrl).replyOnce(200, [
+ {
+ start_line: 0,
+ start_char: 0,
+ hover: { value: '123' },
+ },
+ {
+ start_line: 1,
+ start_char: 0,
+ hover: null,
+ },
+ ]);
+ });
+
+ it('commits REQUEST_DATA_SUCCESS with normalized data', done => {
+ testAction(
+ actions.fetchData,
+ null,
+ state,
+ [
+ { type: 'REQUEST_DATA' },
+ {
+ type: 'REQUEST_DATA_SUCCESS',
+ payload: { '0:0': { start_line: 0, start_char: 0, hover: { value: '123' } } },
+ },
+ ],
+ [],
+ done,
+ );
+ });
+
+ it('calls addInteractionClass with data', done => {
+ testAction(
+ actions.fetchData,
+ null,
+ state,
+ [
+ { type: 'REQUEST_DATA' },
+ {
+ type: 'REQUEST_DATA_SUCCESS',
+ payload: { '0:0': { start_line: 0, start_char: 0, hover: { value: '123' } } },
+ },
+ ],
+ [],
+ )
+ .then(() => {
+ expect(addInteractionClass).toHaveBeenCalledWith({
+ start_line: 0,
+ start_char: 0,
+ hover: { value: '123' },
+ });
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('error', () => {
+ beforeEach(() => {
+ mock.onGet(apiUrl).replyOnce(500);
+ });
+
+ it('dispatches requestDataError', done => {
+ testAction(
+ actions.fetchData,
+ null,
+ state,
+ [{ type: 'REQUEST_DATA' }],
+ [{ type: 'requestDataError' }],
+ done,
+ );
+ });
+ });
+ });
+
+ describe('showDefinition', () => {
+ let target;
+
+ beforeEach(() => {
+ target = document.createElement('div');
+ });
+
+ it('returns early when no data exists', done => {
+ testAction(actions.showDefinition, { target }, {}, [], [], done);
+ });
+
+ it('commits SET_CURRENT_DEFINITION when target is not code navitation element', done => {
+ testAction(
+ actions.showDefinition,
+ { target },
+ { data: {} },
+ [
+ {
+ type: 'SET_CURRENT_DEFINITION',
+ payload: { definition: undefined, position: undefined },
+ },
+ ],
+ [],
+ done,
+ );
+ });
+
+ it('commits SET_CURRENT_DEFINITION with LSIF data', done => {
+ target.classList.add('js-code-navigation');
+ target.setAttribute('data-line-index', '0');
+ target.setAttribute('data-char-index', '0');
+
+ testAction(
+ actions.showDefinition,
+ { target },
+ { data: { '0:0': { hover: 'test' } } },
+ [
+ {
+ type: 'SET_CURRENT_DEFINITION',
+ payload: { definition: { hover: 'test' }, position: { height: 0, x: 0, y: 0 } },
+ },
+ ],
+ [],
+ done,
+ );
+ });
+
+ it('adds hll class to target element', () => {
+ target.classList.add('js-code-navigation');
+ target.setAttribute('data-line-index', '0');
+ target.setAttribute('data-char-index', '0');
+
+ return testAction(
+ actions.showDefinition,
+ { target },
+ { data: { '0:0': { hover: 'test' } } },
+ [
+ {
+ type: 'SET_CURRENT_DEFINITION',
+ payload: { definition: { hover: 'test' }, position: { height: 0, x: 0, y: 0 } },
+ },
+ ],
+ [],
+ ).then(() => {
+ expect(target.classList).toContain('hll');
+ });
+ });
+
+ it('caches current target element', () => {
+ target.classList.add('js-code-navigation');
+ target.setAttribute('data-line-index', '0');
+ target.setAttribute('data-char-index', '0');
+
+ return testAction(
+ actions.showDefinition,
+ { target },
+ { data: { '0:0': { hover: 'test' } } },
+ [
+ {
+ type: 'SET_CURRENT_DEFINITION',
+ payload: { definition: { hover: 'test' }, position: { height: 0, x: 0, y: 0 } },
+ },
+ ],
+ [],
+ ).then(() => {
+ expect(setCurrentHoverElement).toHaveBeenCalledWith(target);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/code_navigation/store/mutations_spec.js b/spec/frontend/code_navigation/store/mutations_spec.js
new file mode 100644
index 00000000000..117a2ed2f14
--- /dev/null
+++ b/spec/frontend/code_navigation/store/mutations_spec.js
@@ -0,0 +1,63 @@
+import mutations from '~/code_navigation/store/mutations';
+import createState from '~/code_navigation/store/state';
+
+let state;
+
+describe('Code navigation mutations', () => {
+ beforeEach(() => {
+ state = createState();
+ });
+
+ describe('SET_INITIAL_DATA', () => {
+ it('sets initial data', () => {
+ mutations.SET_INITIAL_DATA(state, {
+ projectPath: 'test',
+ commitId: '123',
+ blobPath: 'index.js',
+ });
+
+ expect(state.projectPath).toBe('test');
+ expect(state.commitId).toBe('123');
+ expect(state.blobPath).toBe('index.js');
+ });
+ });
+
+ describe('REQUEST_DATA', () => {
+ it('sets loading true', () => {
+ mutations.REQUEST_DATA(state);
+
+ expect(state.loading).toBe(true);
+ });
+ });
+
+ describe('REQUEST_DATA_SUCCESS', () => {
+ it('sets loading false', () => {
+ mutations.REQUEST_DATA_SUCCESS(state, ['test']);
+
+ expect(state.loading).toBe(false);
+ });
+
+ it('sets data', () => {
+ mutations.REQUEST_DATA_SUCCESS(state, ['test']);
+
+ expect(state.data).toEqual(['test']);
+ });
+ });
+
+ describe('REQUEST_DATA_ERROR', () => {
+ it('sets loading false', () => {
+ mutations.REQUEST_DATA_ERROR(state);
+
+ expect(state.loading).toBe(false);
+ });
+ });
+
+ describe('SET_CURRENT_DEFINITION', () => {
+ it('sets current definition and position', () => {
+ mutations.SET_CURRENT_DEFINITION(state, { definition: 'test', position: { x: 0 } });
+
+ expect(state.currentDefinition).toBe('test');
+ expect(state.currentDefinitionPosition).toEqual({ x: 0 });
+ });
+ });
+});
diff --git a/spec/frontend/code_navigation/utils/index_spec.js b/spec/frontend/code_navigation/utils/index_spec.js
new file mode 100644
index 00000000000..458cc536635
--- /dev/null
+++ b/spec/frontend/code_navigation/utils/index_spec.js
@@ -0,0 +1,58 @@
+import {
+ cachedData,
+ getCurrentHoverElement,
+ setCurrentHoverElement,
+ addInteractionClass,
+} from '~/code_navigation/utils';
+
+afterEach(() => {
+ if (cachedData.has('current')) {
+ cachedData.delete('current');
+ }
+});
+
+describe('getCurrentHoverElement', () => {
+ it.each`
+ value
+ ${'test'}
+ ${undefined}
+ `('it returns cached current key', ({ value }) => {
+ if (value) {
+ cachedData.set('current', value);
+ }
+
+ expect(getCurrentHoverElement()).toEqual(value);
+ });
+});
+
+describe('setCurrentHoverElement', () => {
+ it('sets cached current key', () => {
+ setCurrentHoverElement('test');
+
+ expect(getCurrentHoverElement()).toEqual('test');
+ });
+});
+
+describe('addInteractionClass', () => {
+ beforeEach(() => {
+ setFixtures(
+ '<div id="LC1"><span>console</span><span>.</span><span>log</span></div><div id="LC2"><span>function</span></div>',
+ );
+ });
+
+ it.each`
+ line | char | index
+ ${0} | ${0} | ${0}
+ ${0} | ${8} | ${2}
+ ${1} | ${0} | ${0}
+ `(
+ 'it sets code navigation attributes for line $line and character $char',
+ ({ line, char, index }) => {
+ addInteractionClass({ start_line: line, start_char: char });
+
+ expect(document.querySelectorAll(`#LC${line + 1} span`)[index].classList).toContain(
+ 'js-code-navigation',
+ );
+ },
+ );
+});