Commit 8ec89b59 authored by Jacques's avatar Jacques

Refactor blob content viewer spec

Refactored the spec for better maintainability
parent d850bb85
import { GlLoadingIcon } from '@gitlab/ui'; import { GlLoadingIcon } from '@gitlab/ui';
import { shallowMount, mount, createLocalVue } from '@vue/test-utils'; import { mount, shallowMount, createLocalVue } from '@vue/test-utils';
import axios from 'axios'; import axios from 'axios';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
...@@ -19,6 +19,14 @@ import TextViewer from '~/repository/components/blob_viewers/text_viewer.vue'; ...@@ -19,6 +19,14 @@ import TextViewer from '~/repository/components/blob_viewers/text_viewer.vue';
import blobInfoQuery from '~/repository/queries/blob_info.query.graphql'; import blobInfoQuery from '~/repository/queries/blob_info.query.graphql';
import { redirectTo } from '~/lib/utils/url_utility'; import { redirectTo } from '~/lib/utils/url_utility';
import { isLoggedIn } from '~/lib/utils/common_utils'; import { isLoggedIn } from '~/lib/utils/common_utils';
import {
simpleViewerMock,
richViewerMock,
projectMock,
userPermissionsMock,
propsMock,
refMock,
} from '../mock_data';
jest.mock('~/repository/components/blob_viewers'); jest.mock('~/repository/components/blob_viewers');
jest.mock('~/lib/utils/url_utility'); jest.mock('~/lib/utils/url_utility');
...@@ -27,147 +35,56 @@ jest.mock('~/lib/utils/common_utils'); ...@@ -27,147 +35,56 @@ jest.mock('~/lib/utils/common_utils');
let wrapper; let wrapper;
let mockResolver; let mockResolver;
const simpleMockData = {
name: 'some_file.js',
size: 123,
rawSize: 123,
rawTextBlob: 'raw content',
type: 'text',
fileType: 'text',
tooLarge: false,
path: 'some_file.js',
webPath: 'some_file.js',
editBlobPath: 'some_file.js/edit',
ideEditPath: 'some_file.js/ide/edit',
forkAndEditPath: 'some_file.js/fork/edit',
ideForkAndEditPath: 'some_file.js/fork/ide',
canModifyBlob: true,
storedExternally: false,
rawPath: 'some_file.js',
externalStorageUrl: 'some_file.js',
replacePath: 'some_file.js/replace',
deletePath: 'some_file.js/delete',
simpleViewer: {
fileType: 'text',
tooLarge: false,
type: 'simple',
renderError: null,
},
richViewer: null,
};
const richMockData = {
...simpleMockData,
richViewer: {
fileType: 'markup',
tooLarge: false,
type: 'rich',
renderError: null,
},
};
const projectMockData = {
userPermissions: {
pushCode: true,
downloadCode: true,
createMergeRequestIn: true,
forkProject: true,
},
repository: {
empty: false,
},
};
const localVue = createLocalVue(); const localVue = createLocalVue();
const mockAxios = new MockAdapter(axios); const mockAxios = new MockAdapter(axios);
const createComponentWithApollo = (mockData = {}, inject = {}) => { const createComponent = async (mockData = {}, mountFn = shallowMount) => {
localVue.use(VueApollo); localVue.use(VueApollo);
const defaultPushCode = projectMockData.userPermissions.pushCode;
const defaultDownloadCode = projectMockData.userPermissions.downloadCode;
const defaultEmptyRepo = projectMockData.repository.empty;
const { const {
blobs, blob = simpleViewerMock,
emptyRepo = defaultEmptyRepo, empty = projectMock.repository.empty,
canPushCode = defaultPushCode, pushCode = userPermissionsMock.pushCode,
canDownloadCode = defaultDownloadCode, forkProject = userPermissionsMock.forkProject,
createMergeRequestIn = projectMockData.userPermissions.createMergeRequestIn, downloadCode = userPermissionsMock.downloadCode,
forkProject = projectMockData.userPermissions.forkProject, createMergeRequestIn = userPermissionsMock.createMergeRequestIn,
pathLocks = [], isBinary,
inject = {},
} = mockData; } = mockData;
mockResolver = jest.fn().mockResolvedValue({ const project = {
data: { ...projectMock,
project: { userPermissions: {
id: '1234', pushCode,
userPermissions: { forkProject,
pushCode: canPushCode, downloadCode,
downloadCode: canDownloadCode, createMergeRequestIn,
createMergeRequestIn, },
forkProject, repository: {
}, empty,
pathLocks: { blobs: { nodes: [blob] },
nodes: pathLocks,
},
repository: {
empty: emptyRepo,
blobs: {
nodes: [blobs],
},
},
},
}, },
};
mockResolver = jest.fn().mockResolvedValue({
data: { isBinary, project },
}); });
const fakeApollo = createMockApollo([[blobInfoQuery, mockResolver]]); const fakeApollo = createMockApollo([[blobInfoQuery, mockResolver]]);
wrapper = shallowMount(BlobContentViewer, { wrapper = mountFn(BlobContentViewer, {
localVue, localVue,
apolloProvider: fakeApollo, apolloProvider: fakeApollo,
propsData: { propsData: propsMock,
path: 'some_file.js', mixins: [{ data: () => ({ ref: refMock }) }],
projectPath: 'some/path', provide: { ...inject },
},
mixins: [
{
data: () => ({ ref: 'default-ref' }),
},
],
provide: {
...inject,
},
}); });
};
const createFactory = (mountFn) => ( wrapper.setData({ project, isBinary });
{ props = {}, mockData = {}, stubs = {} } = {},
loading = false,
) => {
wrapper = mountFn(BlobContentViewer, {
propsData: {
path: 'some_file.js',
projectPath: 'some/path',
...props,
},
mocks: {
$apollo: {
queries: {
project: {
loading,
refetch: jest.fn(),
},
},
},
},
stubs,
});
wrapper.setData(mockData); await waitForPromises();
}; };
const factory = createFactory(shallowMount);
const fullFactory = createFactory(mount);
describe('Blob content viewer component', () => { describe('Blob content viewer component', () => {
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findBlobHeader = () => wrapper.findComponent(BlobHeader); const findBlobHeader = () => wrapper.findComponent(BlobHeader);
...@@ -187,25 +104,24 @@ describe('Blob content viewer component', () => { ...@@ -187,25 +104,24 @@ describe('Blob content viewer component', () => {
}); });
it('renders a GlLoadingIcon component', () => { it('renders a GlLoadingIcon component', () => {
factory({ mockData: { blobInfo: simpleMockData } }, true); createComponent();
expect(findLoadingIcon().exists()).toBe(true); expect(findLoadingIcon().exists()).toBe(true);
}); });
describe('simple viewer', () => { describe('simple viewer', () => {
beforeEach(() => { it('renders a BlobHeader component', async () => {
factory({ mockData: { blobInfo: simpleMockData } }); await createComponent();
});
it('renders a BlobHeader component', () => {
expect(findBlobHeader().props('activeViewerType')).toEqual('simple'); expect(findBlobHeader().props('activeViewerType')).toEqual('simple');
expect(findBlobHeader().props('hasRenderError')).toEqual(false); expect(findBlobHeader().props('hasRenderError')).toEqual(false);
expect(findBlobHeader().props('hideViewerSwitcher')).toEqual(true); expect(findBlobHeader().props('hideViewerSwitcher')).toEqual(true);
expect(findBlobHeader().props('blob')).toEqual(simpleMockData); expect(findBlobHeader().props('blob')).toEqual(simpleViewerMock);
}); });
it('renders a BlobContent component', () => { it('renders a BlobContent component', async () => {
expect(findBlobContent().props('loading')).toEqual(false); await createComponent();
expect(findBlobContent().props('isRawContent')).toBe(true); expect(findBlobContent().props('isRawContent')).toBe(true);
expect(findBlobContent().props('activeViewer')).toEqual({ expect(findBlobContent().props('activeViewer')).toEqual({
fileType: 'text', fileType: 'text',
...@@ -217,8 +133,7 @@ describe('Blob content viewer component', () => { ...@@ -217,8 +133,7 @@ describe('Blob content viewer component', () => {
describe('legacy viewers', () => { describe('legacy viewers', () => {
it('loads a legacy viewer when a viewer component is not available', async () => { it('loads a legacy viewer when a viewer component is not available', async () => {
createComponentWithApollo({ blobs: { ...simpleMockData, fileType: 'unknown' } }); await createComponent({ blob: { ...simpleViewerMock, fileType: 'unknown' } });
await waitForPromises();
expect(mockAxios.history.get).toHaveLength(1); expect(mockAxios.history.get).toHaveLength(1);
expect(mockAxios.history.get[0].url).toEqual('some_file.js?format=json&viewer=simple'); expect(mockAxios.history.get[0].url).toEqual('some_file.js?format=json&viewer=simple');
...@@ -227,21 +142,18 @@ describe('Blob content viewer component', () => { ...@@ -227,21 +142,18 @@ describe('Blob content viewer component', () => {
}); });
describe('rich viewer', () => { describe('rich viewer', () => {
beforeEach(() => { it('renders a BlobHeader component', async () => {
factory({ await createComponent({ blob: richViewerMock });
mockData: { blobInfo: richMockData, activeViewerType: 'rich' },
});
});
it('renders a BlobHeader component', () => {
expect(findBlobHeader().props('activeViewerType')).toEqual('rich'); expect(findBlobHeader().props('activeViewerType')).toEqual('rich');
expect(findBlobHeader().props('hasRenderError')).toEqual(false); expect(findBlobHeader().props('hasRenderError')).toEqual(false);
expect(findBlobHeader().props('hideViewerSwitcher')).toEqual(false); expect(findBlobHeader().props('hideViewerSwitcher')).toEqual(false);
expect(findBlobHeader().props('blob')).toEqual(richMockData); expect(findBlobHeader().props('blob')).toEqual(richViewerMock);
}); });
it('renders a BlobContent component', () => { it('renders a BlobContent component', async () => {
expect(findBlobContent().props('loading')).toEqual(false); await createComponent({ blob: richViewerMock });
expect(findBlobContent().props('isRawContent')).toBe(true); expect(findBlobContent().props('isRawContent')).toBe(true);
expect(findBlobContent().props('activeViewer')).toEqual({ expect(findBlobContent().props('activeViewer')).toEqual({
fileType: 'markup', fileType: 'markup',
...@@ -252,6 +164,8 @@ describe('Blob content viewer component', () => { ...@@ -252,6 +164,8 @@ describe('Blob content viewer component', () => {
}); });
it('updates viewer type when viewer changed is clicked', async () => { it('updates viewer type when viewer changed is clicked', async () => {
await createComponent({ blob: richViewerMock });
expect(findBlobContent().props('activeViewer')).toEqual( expect(findBlobContent().props('activeViewer')).toEqual(
expect.objectContaining({ expect.objectContaining({
type: 'rich', type: 'rich',
...@@ -273,8 +187,7 @@ describe('Blob content viewer component', () => { ...@@ -273,8 +187,7 @@ describe('Blob content viewer component', () => {
describe('legacy viewers', () => { describe('legacy viewers', () => {
it('loads a legacy viewer when a viewer component is not available', async () => { it('loads a legacy viewer when a viewer component is not available', async () => {
createComponentWithApollo({ blobs: { ...richMockData, fileType: 'unknown' } }); await createComponent({ blob: { ...richViewerMock, fileType: 'unknown' } });
await waitForPromises();
expect(mockAxios.history.get).toHaveLength(1); expect(mockAxios.history.get).toHaveLength(1);
expect(mockAxios.history.get[0].url).toEqual('some_file.js?format=json&viewer=rich'); expect(mockAxios.history.get[0].url).toEqual('some_file.js?format=json&viewer=rich');
...@@ -287,9 +200,9 @@ describe('Blob content viewer component', () => { ...@@ -287,9 +200,9 @@ describe('Blob content viewer component', () => {
viewerProps.mockRestore(); viewerProps.mockRestore();
}); });
it('does not render a BlobContent component if a Blob viewer is available', () => { it('does not render a BlobContent component if a Blob viewer is available', async () => {
loadViewer.mockReturnValueOnce(() => true); loadViewer.mockReturnValue(() => true);
factory({ mockData: { blobInfo: richMockData } }); await createComponent({ blob: richViewerMock });
expect(findBlobContent().exists()).toBe(false); expect(findBlobContent().exists()).toBe(false);
}); });
...@@ -305,15 +218,13 @@ describe('Blob content viewer component', () => { ...@@ -305,15 +218,13 @@ describe('Blob content viewer component', () => {
loadViewer.mockReturnValue(loadViewerReturnValue); loadViewer.mockReturnValue(loadViewerReturnValue);
viewerProps.mockReturnValue(viewerPropsReturnValue); viewerProps.mockReturnValue(viewerPropsReturnValue);
factory({ createComponent({
mockData: { blob: {
blobInfo: { ...simpleViewerMock,
...simpleMockData, fileType: 'null',
fileType: null, simpleViewer: {
simpleViewer: { ...simpleViewerMock.simpleViewer,
...simpleMockData.simpleViewer, fileType: viewer,
fileType: viewer,
},
}, },
}, },
}); });
...@@ -327,18 +238,10 @@ describe('Blob content viewer component', () => { ...@@ -327,18 +238,10 @@ describe('Blob content viewer component', () => {
}); });
describe('BlobHeader action slot', () => { describe('BlobHeader action slot', () => {
const { ideEditPath, editBlobPath } = simpleMockData; const { ideEditPath, editBlobPath } = simpleViewerMock;
it('renders BlobHeaderEdit buttons in simple viewer', async () => { it('renders BlobHeaderEdit buttons in simple viewer', async () => {
fullFactory({ await createComponent({ inject: { BlobContent: true, BlobReplace: true } }, mount);
mockData: { blobInfo: simpleMockData },
stubs: {
BlobContent: true,
BlobReplace: true,
},
});
await nextTick();
expect(findBlobEdit().props()).toMatchObject({ expect(findBlobEdit().props()).toMatchObject({
editPath: editBlobPath, editPath: editBlobPath,
...@@ -348,15 +251,7 @@ describe('Blob content viewer component', () => { ...@@ -348,15 +251,7 @@ describe('Blob content viewer component', () => {
}); });
it('renders BlobHeaderEdit button in rich viewer', async () => { it('renders BlobHeaderEdit button in rich viewer', async () => {
fullFactory({ await createComponent({ blob: richViewerMock }, mount);
mockData: { blobInfo: richMockData },
stubs: {
BlobContent: true,
BlobReplace: true,
},
});
await nextTick();
expect(findBlobEdit().props()).toMatchObject({ expect(findBlobEdit().props()).toMatchObject({
editPath: editBlobPath, editPath: editBlobPath,
...@@ -366,15 +261,7 @@ describe('Blob content viewer component', () => { ...@@ -366,15 +261,7 @@ describe('Blob content viewer component', () => {
}); });
it('renders BlobHeaderEdit button for binary files', async () => { it('renders BlobHeaderEdit button for binary files', async () => {
fullFactory({ await createComponent({ blob: richViewerMock, isBinary: true }, mount);
mockData: { blobInfo: richMockData, isBinary: true },
stubs: {
BlobContent: true,
BlobReplace: true,
},
});
await nextTick();
expect(findBlobEdit().props()).toMatchObject({ expect(findBlobEdit().props()).toMatchObject({
editPath: editBlobPath, editPath: editBlobPath,
...@@ -384,41 +271,26 @@ describe('Blob content viewer component', () => { ...@@ -384,41 +271,26 @@ describe('Blob content viewer component', () => {
}); });
describe('blob header binary file', () => { describe('blob header binary file', () => {
it.each([richMockData, { simpleViewer: { fileType: 'download' } }])( it('passes the correct isBinary value when viewing a binary file', async () => {
'passes the correct isBinary value when viewing a binary file', await createComponent({ blob: richViewerMock, isBinary: true });
async (blobInfo) => {
fullFactory({
mockData: {
blobInfo,
isBinary: true,
},
stubs: { BlobContent: true, BlobReplace: true },
});
await nextTick();
expect(findBlobHeader().props('isBinary')).toBe(true); expect(findBlobHeader().props('isBinary')).toBe(true);
}, });
);
it('passes the correct header props when viewing a non-text file', async () => { it('passes the correct header props when viewing a non-text file', async () => {
fullFactory({ await createComponent(
mockData: { {
blobInfo: { blob: {
...simpleMockData, ...simpleViewerMock,
simpleViewer: { simpleViewer: {
...simpleMockData.simpleViewer, ...simpleViewerMock.simpleViewer,
fileType: 'image', fileType: 'image',
}, },
}, },
isBinary: true,
}, },
stubs: { mount,
BlobContent: true, );
BlobReplace: true,
},
});
await nextTick();
expect(findBlobHeader().props('hideViewerSwitcher')).toBe(true); expect(findBlobHeader().props('hideViewerSwitcher')).toBe(true);
expect(findBlobHeader().props('isBinary')).toBe(true); expect(findBlobHeader().props('isBinary')).toBe(true);
...@@ -427,27 +299,16 @@ describe('Blob content viewer component', () => { ...@@ -427,27 +299,16 @@ describe('Blob content viewer component', () => {
}); });
describe('BlobButtonGroup', () => { describe('BlobButtonGroup', () => {
const { name, path, replacePath, webPath } = simpleMockData; const { name, path, replacePath, webPath } = simpleViewerMock;
const { const {
userPermissions: { pushCode, downloadCode }, userPermissions: { pushCode, downloadCode },
repository: { empty }, repository: { empty },
} = projectMockData; } = projectMock;
it('renders component', async () => { it('renders component', async () => {
window.gon.current_user_id = 1; window.gon.current_user_id = 1;
fullFactory({ await createComponent({ pushCode, downloadCode, empty }, mount);
mockData: {
blobInfo: simpleMockData,
project: { userPermissions: { pushCode, downloadCode }, repository: { empty } },
},
stubs: {
BlobContent: true,
BlobButtonGroup: true,
},
});
await nextTick();
expect(findBlobButtonGroup().props()).toMatchObject({ expect(findBlobButtonGroup().props()).toMatchObject({
name, name,
...@@ -467,21 +328,14 @@ describe('Blob content viewer component', () => { ...@@ -467,21 +328,14 @@ describe('Blob content viewer component', () => {
${false} | ${true} | ${false} ${false} | ${true} | ${false}
${true} | ${false} | ${false} ${true} | ${false} | ${false}
`('passes the correct lock states', async ({ canPushCode, canDownloadCode, canLock }) => { `('passes the correct lock states', async ({ canPushCode, canDownloadCode, canLock }) => {
fullFactory({ await createComponent(
mockData: { {
blobInfo: simpleMockData, pushCode: canPushCode,
project: { downloadCode: canDownloadCode,
userPermissions: { pushCode: canPushCode, downloadCode: canDownloadCode }, empty,
repository: { empty },
},
}, },
stubs: { mount,
BlobContent: true, );
BlobButtonGroup: true,
},
});
await nextTick();
expect(findBlobButtonGroup().props('canLock')).toBe(canLock); expect(findBlobButtonGroup().props('canLock')).toBe(canLock);
}); });
...@@ -489,15 +343,7 @@ describe('Blob content viewer component', () => { ...@@ -489,15 +343,7 @@ describe('Blob content viewer component', () => {
it('does not render if not logged in', async () => { it('does not render if not logged in', async () => {
isLoggedIn.mockReturnValueOnce(false); isLoggedIn.mockReturnValueOnce(false);
fullFactory({ await createComponent();
mockData: { blobInfo: simpleMockData },
stubs: {
BlobContent: true,
BlobReplace: true,
},
});
await nextTick();
expect(findBlobButtonGroup().exists()).toBe(false); expect(findBlobButtonGroup().exists()).toBe(false);
}); });
...@@ -506,10 +352,7 @@ describe('Blob content viewer component', () => { ...@@ -506,10 +352,7 @@ describe('Blob content viewer component', () => {
describe('blob info query', () => { describe('blob info query', () => {
it('is called with originalBranch value if the prop has a value', async () => { it('is called with originalBranch value if the prop has a value', async () => {
const inject = { originalBranch: 'some-branch' }; await createComponent({ inject: { originalBranch: 'some-branch' } });
createComponentWithApollo({ blobs: simpleMockData }, inject);
await waitForPromises();
expect(mockResolver).toHaveBeenCalledWith( expect(mockResolver).toHaveBeenCalledWith(
expect.objectContaining({ expect.objectContaining({
...@@ -519,10 +362,7 @@ describe('Blob content viewer component', () => { ...@@ -519,10 +362,7 @@ describe('Blob content viewer component', () => {
}); });
it('is called with ref value if the originalBranch prop has no value', async () => { it('is called with ref value if the originalBranch prop has no value', async () => {
const inject = { originalBranch: null }; await createComponent();
createComponentWithApollo({ blobs: simpleMockData }, inject);
await waitForPromises();
expect(mockResolver).toHaveBeenCalledWith( expect(mockResolver).toHaveBeenCalledWith(
expect.objectContaining({ expect.objectContaining({
...@@ -533,24 +373,16 @@ describe('Blob content viewer component', () => { ...@@ -533,24 +373,16 @@ describe('Blob content viewer component', () => {
}); });
describe('edit blob', () => { describe('edit blob', () => {
beforeEach(() => { beforeEach(() => createComponent({}, mount));
fullFactory({
mockData: { blobInfo: simpleMockData },
stubs: {
BlobContent: true,
BlobReplace: true,
},
});
});
it('simple edit redirects to the simple editor', () => { it('simple edit redirects to the simple editor', () => {
findBlobEdit().vm.$emit('edit', 'simple'); findBlobEdit().vm.$emit('edit', 'simple');
expect(redirectTo).toHaveBeenCalledWith(simpleMockData.editBlobPath); expect(redirectTo).toHaveBeenCalledWith(simpleViewerMock.editBlobPath);
}); });
it('IDE edit redirects to the IDE editor', () => { it('IDE edit redirects to the IDE editor', () => {
findBlobEdit().vm.$emit('edit', 'ide'); findBlobEdit().vm.$emit('edit', 'ide');
expect(redirectTo).toHaveBeenCalledWith(simpleMockData.ideEditPath); expect(redirectTo).toHaveBeenCalledWith(simpleViewerMock.ideEditPath);
}); });
it.each` it.each`
...@@ -569,16 +401,14 @@ describe('Blob content viewer component', () => { ...@@ -569,16 +401,14 @@ describe('Blob content viewer component', () => {
showForkSuggestion, showForkSuggestion,
}) => { }) => {
isLoggedIn.mockReturnValueOnce(loggedIn); isLoggedIn.mockReturnValueOnce(loggedIn);
fullFactory({ await createComponent(
mockData: { {
blobInfo: { ...simpleMockData, canModifyBlob }, blob: { ...simpleViewerMock, canModifyBlob },
project: { userPermissions: { createMergeRequestIn, forkProject } }, createMergeRequestIn,
forkProject,
}, },
stubs: { mount,
BlobContent: true, );
BlobButtonGroup: true,
},
});
findBlobEdit().vm.$emit('edit', 'simple'); findBlobEdit().vm.$emit('edit', 'simple');
await nextTick(); await nextTick();
......
export const simpleViewerMock = {
name: 'some_file.js',
size: 123,
rawSize: 123,
rawTextBlob: 'raw content',
fileType: 'text',
path: 'some_file.js',
webPath: 'some_file.js',
editBlobPath: 'some_file.js/edit',
ideEditPath: 'some_file.js/ide/edit',
forkAndEditPath: 'some_file.js/fork/edit',
ideForkAndEditPath: 'some_file.js/fork/ide',
canModifyBlob: true,
storedExternally: false,
rawPath: 'some_file.js',
replacePath: 'some_file.js/replace',
simpleViewer: {
fileType: 'text',
tooLarge: false,
type: 'simple',
renderError: null,
},
richViewer: null,
};
export const richViewerMock = {
...simpleViewerMock,
richViewer: {
fileType: 'markup',
tooLarge: false,
type: 'rich',
renderError: null,
},
};
export const userPermissionsMock = {
pushCode: true,
forkProject: true,
downloadCode: true,
createMergeRequestIn: true,
};
export const projectMock = {
id: '1234',
userPermissions: userPermissionsMock,
pathLocks: {
nodes: [],
},
repository: {
empty: false,
},
};
export const propsMock = { path: 'some_file.js', projectPath: 'some/path' };
export const refMock = 'default-ref';
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment