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:
Diffstat (limited to 'spec/frontend/repository')
-rw-r--r--spec/frontend/repository/commits_service_spec.js19
-rw-r--r--spec/frontend/repository/components/blob_content_viewer_spec.js31
-rw-r--r--spec/frontend/repository/components/blob_controls_spec.js3
-rw-r--r--spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap6
-rw-r--r--spec/frontend/repository/components/tree_content_spec.js21
-rw-r--r--spec/frontend/repository/mixins/highlight_mixin_spec.js74
6 files changed, 119 insertions, 35 deletions
diff --git a/spec/frontend/repository/commits_service_spec.js b/spec/frontend/repository/commits_service_spec.js
index 5fb683bd370..d779abcbfd6 100644
--- a/spec/frontend/repository/commits_service_spec.js
+++ b/spec/frontend/repository/commits_service_spec.js
@@ -14,7 +14,7 @@ describe('commits service', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
-
+ window.gon.features = { encodingLogsTree: true };
mock.onGet(url).reply(HTTP_STATUS_OK, [], {});
jest.spyOn(axios, 'get');
@@ -48,14 +48,27 @@ describe('commits service', () => {
});
it('encodes the path and ref', async () => {
- const encodedRef = encodeURIComponent(refWithSpecialCharMock);
- const encodedUrl = `/some-project/-/refs/${encodedRef}/logs_tree/with%20%24peci%40l%20ch%40rs%2F`;
+ const encodedRef = encodeURI(refWithSpecialCharMock);
+ const encodedUrl = `/some-project/-/refs/${encodedRef}/logs_tree/with%20$peci@l%20ch@rs/`;
await requestCommits(1, 'some-project', 'with $peci@l ch@rs/', refWithSpecialCharMock);
expect(axios.get).toHaveBeenCalledWith(encodedUrl, expect.anything());
});
+ describe('when encodingLogsTree FF is off', () => {
+ beforeEach(() => {
+ window.gon.features = {};
+ });
+
+ it('encodes the path and ref with encodeURIComponent', async () => {
+ const encodedRef = encodeURIComponent(refWithSpecialCharMock);
+ const encodedUrl = `/some-project/-/refs/${encodedRef}/logs_tree/with%20%24peci%40l%20ch%40rs%2F`;
+ await requestCommits(1, 'some-project', 'with $peci@l ch@rs/', refWithSpecialCharMock);
+ expect(axios.get).toHaveBeenCalledWith(encodedUrl, expect.anything());
+ });
+ });
+
it('calls axios get once per batch', async () => {
await Promise.all([requestCommits(0), requestCommits(1), requestCommits(23)]);
diff --git a/spec/frontend/repository/components/blob_content_viewer_spec.js b/spec/frontend/repository/components/blob_content_viewer_spec.js
index e0d2984893b..cd5bc08faf0 100644
--- a/spec/frontend/repository/components/blob_content_viewer_spec.js
+++ b/spec/frontend/repository/components/blob_content_viewer_spec.js
@@ -75,6 +75,7 @@ const createComponent = async (mockData = {}, mountFn = shallowMount, mockRoute
createMergeRequestIn = userPermissionsMock.createMergeRequestIn,
isBinary,
inject = {},
+ blobBlameInfo = true,
} = mockData;
const blobInfo = {
@@ -138,7 +139,7 @@ const createComponent = async (mockData = {}, mountFn = shallowMount, mockRoute
...inject,
glFeatures: {
highlightJsWorker: false,
- blobBlameInfo: true,
+ blobBlameInfo,
},
},
}),
@@ -185,7 +186,7 @@ describe('Blob content viewer component', () => {
expect(findBlobHeader().props('hideViewerSwitcher')).toEqual(false);
expect(findBlobHeader().props('blob')).toEqual(simpleViewerMock);
expect(findBlobHeader().props('showForkSuggestion')).toEqual(false);
- expect(findBlobHeader().props('showBlameToggle')).toEqual(false);
+ expect(findBlobHeader().props('showBlameToggle')).toEqual(true);
expect(findBlobHeader().props('projectPath')).toEqual(propsMock.projectPath);
expect(findBlobHeader().props('projectId')).toEqual(projectMock.id);
expect(mockRouterPush).not.toHaveBeenCalled();
@@ -197,15 +198,15 @@ describe('Blob content viewer component', () => {
await nextTick();
};
- it('renders a blame toggle for JSON files', async () => {
- await createComponent({ blob: { ...simpleViewerMock, language: 'json' } });
+ it('renders a blame toggle', async () => {
+ await createComponent({ blob: simpleViewerMock });
expect(findBlobHeader().props('showBlameToggle')).toEqual(true);
});
it('adds blame param to the URL and passes `showBlame` to the SourceViewer', async () => {
loadViewer.mockReturnValueOnce(SourceViewerNew);
- await createComponent({ blob: { ...simpleViewerMock, language: 'json' } });
+ await createComponent({ blob: simpleViewerMock });
await triggerBlame();
@@ -217,6 +218,25 @@ describe('Blob content viewer component', () => {
expect(mockRouterPush).toHaveBeenCalledWith({ query: { blame: '0' } });
expect(findSourceViewerNew().props('showBlame')).toBe(false);
});
+
+ describe('blobBlameInfo feature flag disabled', () => {
+ it('does not render a blame toggle', async () => {
+ await createComponent({ blob: simpleViewerMock, blobBlameInfo: false });
+
+ expect(findBlobHeader().props('showBlameToggle')).toEqual(false);
+ });
+ });
+
+ describe('when viewing rich content', () => {
+ it('always shows the blame when clicking on the blame button', async () => {
+ loadViewer.mockReturnValueOnce(SourceViewerNew);
+ const query = { plain: '0', blame: '1' };
+ await createComponent({ blob: simpleViewerMock }, shallowMount, { query });
+ await triggerBlame();
+
+ expect(findSourceViewerNew().props('showBlame')).toBe(true);
+ });
+ });
});
it('creates an alert when the BlobHeader component emits an error', async () => {
@@ -260,6 +280,7 @@ describe('Blob content viewer component', () => {
expect(mockAxios.history.get).toHaveLength(1);
expect(mockAxios.history.get[0].url).toBe(legacyViewerUrl);
+ expect(findBlobHeader().props('showBlameToggle')).toEqual(false);
});
it('loads a legacy viewer when a viewer component is not available', async () => {
diff --git a/spec/frontend/repository/components/blob_controls_spec.js b/spec/frontend/repository/components/blob_controls_spec.js
index 3ced5f6c4d2..53ebabebf1d 100644
--- a/spec/frontend/repository/components/blob_controls_spec.js
+++ b/spec/frontend/repository/components/blob_controls_spec.js
@@ -8,6 +8,7 @@ import blobControlsQuery from '~/repository/queries/blob_controls.query.graphql'
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createRouter from '~/repository/router';
import { updateElementsVisibility } from '~/repository/utils/dom';
+import { resetShortcutsForTests } from '~/behaviors/shortcuts';
import ShortcutsBlob from '~/behaviors/shortcuts/shortcuts_blob';
import BlobLinePermalinkUpdater from '~/blob/blob_line_permalink_updater';
import { blobControlsDataMock, refMock } from '../mock_data';
@@ -32,6 +33,8 @@ const createComponent = async () => {
mockResolver = jest.fn().mockResolvedValue({ data: { project } });
+ await resetShortcutsForTests();
+
wrapper = shallowMountExtended(BlobControls, {
router,
apolloProvider: createMockApollo([[blobControlsQuery, mockResolver]]),
diff --git a/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap b/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap
index e14f41e2ed2..378aacd47fa 100644
--- a/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap
+++ b/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap
@@ -47,7 +47,7 @@ exports[`Repository table row component renders a symlink table row 1`] = `
<gl-intersection-observer-stub>
<timeago-tooltip-stub
cssclass=""
- datetimeformat="DATE_WITH_TIME_FORMAT"
+ datetimeformat="asDateTime"
time="2019-01-01"
tooltipplacement="top"
/>
@@ -103,7 +103,7 @@ exports[`Repository table row component renders table row 1`] = `
<gl-intersection-observer-stub>
<timeago-tooltip-stub
cssclass=""
- datetimeformat="DATE_WITH_TIME_FORMAT"
+ datetimeformat="asDateTime"
time="2019-01-01"
tooltipplacement="top"
/>
@@ -159,7 +159,7 @@ exports[`Repository table row component renders table row for path with special
<gl-intersection-observer-stub>
<timeago-tooltip-stub
cssclass=""
- datetimeformat="DATE_WITH_TIME_FORMAT"
+ datetimeformat="asDateTime"
time="2019-01-01"
tooltipplacement="top"
/>
diff --git a/spec/frontend/repository/components/tree_content_spec.js b/spec/frontend/repository/components/tree_content_spec.js
index c0eb65b28fe..311e5ca86f8 100644
--- a/spec/frontend/repository/components/tree_content_spec.js
+++ b/spec/frontend/repository/components/tree_content_spec.js
@@ -162,26 +162,19 @@ describe('Repository table component', () => {
describe('commit data', () => {
const path = '';
- it('loads commit data for both top and bottom batches when row-appear event is emitted', () => {
- const rowNumber = 50;
-
+ it('loads commit data for the nearest page', () => {
createComponent({ path });
- findFileTable().vm.$emit('row-appear', rowNumber);
+ findFileTable().vm.$emit('row-appear', 49);
+ findFileTable().vm.$emit('row-appear', 15);
- expect(isRequested).toHaveBeenCalledWith(rowNumber);
+ expect(isRequested).toHaveBeenCalledWith(49);
+ expect(isRequested).toHaveBeenCalledWith(15);
expect(loadCommits.mock.calls).toEqual([
- ['', path, '', rowNumber, 'heads'],
- ['', path, '', rowNumber - 25, 'heads'],
+ ['', path, '', 25, 'heads'],
+ ['', path, '', 0, 'heads'],
]);
});
-
- it('loads commit data once if rowNumber is zero', () => {
- createComponent({ path });
- findFileTable().vm.$emit('row-appear', 0);
-
- expect(loadCommits.mock.calls).toEqual([['', path, '', 0, 'heads']]);
- });
});
describe('error handling', () => {
diff --git a/spec/frontend/repository/mixins/highlight_mixin_spec.js b/spec/frontend/repository/mixins/highlight_mixin_spec.js
index 50cfd71d686..c635c09d1aa 100644
--- a/spec/frontend/repository/mixins/highlight_mixin_spec.js
+++ b/spec/frontend/repository/mixins/highlight_mixin_spec.js
@@ -1,9 +1,18 @@
import { shallowMount } from '@vue/test-utils';
+import axios from 'axios';
+import MockAdapter from 'axios-mock-adapter';
import { splitIntoChunks } from '~/vue_shared/components/source_viewer/workers/highlight_utils';
import highlightMixin from '~/repository/mixins/highlight_mixin';
import LineHighlighter from '~/blob/line_highlighter';
+import waitForPromises from 'helpers/wait_for_promises';
+import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
import { TEXT_FILE_TYPE } from '~/repository/constants';
-import { LINES_PER_CHUNK } from '~/vue_shared/components/source_viewer/constants';
+import {
+ LINES_PER_CHUNK,
+ EVENT_ACTION,
+ EVENT_LABEL_FALLBACK,
+} from '~/vue_shared/components/source_viewer/constants';
+import Tracking from '~/tracking';
const lineHighlighter = new LineHighlighter();
jest.mock('~/blob/line_highlighter', () => jest.fn().mockReturnValue({ highlightHash: jest.fn() }));
@@ -11,6 +20,7 @@ jest.mock('~/vue_shared/components/source_viewer/workers/highlight_utils', () =>
splitIntoChunks: jest.fn().mockResolvedValue([]),
}));
+const mockAxios = new MockAdapter(axios);
const workerMock = { postMessage: jest.fn() };
const onErrorMock = jest.fn();
@@ -21,7 +31,10 @@ describe('HighlightMixin', () => {
const rawTextBlob = contentArray.join('\n');
const languageMock = 'json';
- const createComponent = ({ fileType = TEXT_FILE_TYPE, language = languageMock } = {}) => {
+ const createComponent = (
+ { fileType = TEXT_FILE_TYPE, language = languageMock, externalStorageUrl, rawPath } = {},
+ isUsingLfs = false,
+ ) => {
const simpleViewer = { fileType };
const dummyComponent = {
@@ -32,7 +45,10 @@ describe('HighlightMixin', () => {
},
template: '<div>{{chunks[0]?.highlightedContent}}</div>',
created() {
- this.initHighlightWorker({ rawTextBlob, simpleViewer, language, fileType });
+ this.initHighlightWorker(
+ { rawTextBlob, simpleViewer, language, fileType, externalStorageUrl, rawPath },
+ isUsingLfs,
+ );
},
methods: { onError: onErrorMock },
};
@@ -45,13 +61,6 @@ describe('HighlightMixin', () => {
describe('initHighlightWorker', () => {
const firstSeventyLines = contentArray.slice(0, LINES_PER_CHUNK).join('\n');
- it('does not instruct worker if file is not a JSON file', () => {
- workerMock.postMessage.mockClear();
- createComponent({ language: 'javascript' });
-
- expect(workerMock.postMessage).not.toHaveBeenCalled();
- });
-
it('generates a chunk for the first 70 lines of raw text', () => {
expect(splitIntoChunks).toHaveBeenCalledWith(languageMock, firstSeventyLines);
});
@@ -74,6 +83,23 @@ describe('HighlightMixin', () => {
});
});
+ describe('auto-detects if a language cannot be loaded', () => {
+ const unknownLanguage = 'some_unknown_language';
+ beforeEach(() => {
+ jest.spyOn(Tracking, 'event');
+ createComponent({ language: unknownLanguage });
+ });
+
+ it('emits a tracking event for the fallback', () => {
+ const eventData = { label: EVENT_LABEL_FALLBACK, property: unknownLanguage };
+ expect(Tracking.event).toHaveBeenCalledWith(undefined, EVENT_ACTION, eventData);
+ });
+
+ it('calls the onError method', () => {
+ expect(onErrorMock).toHaveBeenCalled();
+ });
+ });
+
describe('worker message handling', () => {
const CHUNK_MOCK = { startingFrom: 0, totalLines: 70, highlightedContent: 'some content' };
@@ -87,4 +113,32 @@ describe('HighlightMixin', () => {
expect(lineHighlighter.highlightHash).toHaveBeenCalledWith(hash);
});
});
+
+ describe('LFS blobs', () => {
+ const rawPath = '/org/project/-/raw/file.xml';
+ const externalStorageUrl = 'http://127.0.0.1:9000/lfs-objects/91/12/1341234';
+ const mockParams = { content: rawTextBlob, language: languageMock, fileType: TEXT_FILE_TYPE };
+
+ afterEach(() => mockAxios.reset());
+
+ it('Uses externalStorageUrl to fetch content if present', async () => {
+ mockAxios.onGet(externalStorageUrl).replyOnce(HTTP_STATUS_OK, rawTextBlob);
+ createComponent({ rawPath, externalStorageUrl }, true);
+ await waitForPromises();
+
+ expect(mockAxios.history.get).toHaveLength(1);
+ expect(mockAxios.history.get[0].url).toBe(externalStorageUrl);
+ expect(workerMock.postMessage).toHaveBeenCalledWith(mockParams);
+ });
+
+ it('Falls back to rawPath to fetch content', async () => {
+ mockAxios.onGet(rawPath).replyOnce(HTTP_STATUS_OK, rawTextBlob);
+ createComponent({ rawPath }, true);
+ await waitForPromises();
+
+ expect(mockAxios.history.get).toHaveLength(1);
+ expect(mockAxios.history.get[0].url).toBe(rawPath);
+ expect(workerMock.postMessage).toHaveBeenCalledWith(mockParams);
+ });
+ });
});