2020-05-07 08:09:46 -04:00
|
|
|
import { createLocalVue, shallowMount } from '@vue/test-utils';
|
2020-09-07 05:08:17 -04:00
|
|
|
import VueApollo, { ApolloMutation } from 'vue-apollo';
|
2020-08-13 11:10:03 -04:00
|
|
|
import VueDraggable from 'vuedraggable';
|
2020-05-07 08:09:46 -04:00
|
|
|
import VueRouter from 'vue-router';
|
|
|
|
import { GlEmptyState } from '@gitlab/ui';
|
2020-09-07 05:08:17 -04:00
|
|
|
import createMockApollo from 'jest/helpers/mock_apollo_helper';
|
2020-10-14 05:08:46 -04:00
|
|
|
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
|
2020-11-03 10:09:05 -05:00
|
|
|
import getDesignListQuery from 'shared_queries/design_management/get_design_list.query.graphql';
|
|
|
|
import permissionsQuery from 'shared_queries/design_management/design_permissions.query.graphql';
|
2020-05-07 08:09:46 -04:00
|
|
|
import Index from '~/design_management/pages/index.vue';
|
2020-06-18 08:09:25 -04:00
|
|
|
import uploadDesignQuery from '~/design_management/graphql/mutations/upload_design.mutation.graphql';
|
2020-05-07 08:09:46 -04:00
|
|
|
import DesignDestroyer from '~/design_management/components/design_destroyer.vue';
|
2020-11-10 04:08:45 -05:00
|
|
|
import DesignDropzone from '~/vue_shared/components/upload_dropzone/upload_dropzone.vue';
|
2020-05-07 08:09:46 -04:00
|
|
|
import DeleteButton from '~/design_management/components/delete_button.vue';
|
2020-09-07 05:08:17 -04:00
|
|
|
import Design from '~/design_management/components/list/item.vue';
|
2020-05-07 08:09:46 -04:00
|
|
|
import { DESIGNS_ROUTE_NAME } from '~/design_management/router/constants';
|
|
|
|
import {
|
|
|
|
EXISTING_DESIGN_DROP_MANY_FILES_MESSAGE,
|
|
|
|
EXISTING_DESIGN_DROP_INVALID_FILENAME_MESSAGE,
|
|
|
|
} from '~/design_management/utils/error_messages';
|
2020-10-28 14:08:52 -04:00
|
|
|
import createFlash from '~/flash';
|
2020-06-01 05:08:28 -04:00
|
|
|
import createRouter from '~/design_management/router';
|
|
|
|
import * as utils from '~/design_management/utils/design_management_utils';
|
2020-09-07 05:08:17 -04:00
|
|
|
import {
|
|
|
|
designListQueryResponse,
|
2020-10-14 05:08:46 -04:00
|
|
|
designUploadMutationCreatedResponse,
|
|
|
|
designUploadMutationUpdatedResponse,
|
2020-09-07 05:08:17 -04:00
|
|
|
permissionsQueryResponse,
|
|
|
|
moveDesignMutationResponse,
|
|
|
|
reorderedDesigns,
|
|
|
|
moveDesignMutationResponseWithErrors,
|
|
|
|
} from '../mock_data/apollo_mock';
|
|
|
|
import moveDesignMutation from '~/design_management/graphql/mutations/move_design.mutation.graphql';
|
2020-12-07 19:09:45 -05:00
|
|
|
import {
|
|
|
|
DESIGN_TRACKING_PAGE_NAME,
|
|
|
|
DESIGN_SNOWPLOW_EVENT_TYPES,
|
|
|
|
} from '~/design_management/utils/tracking';
|
2020-06-01 05:08:28 -04:00
|
|
|
|
|
|
|
jest.mock('~/flash.js');
|
|
|
|
const mockPageEl = {
|
|
|
|
classList: {
|
|
|
|
remove: jest.fn(),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
jest.spyOn(utils, 'getPageLayoutElement').mockReturnValue(mockPageEl);
|
2020-05-07 08:09:46 -04:00
|
|
|
|
2020-07-28 08:09:49 -04:00
|
|
|
const scrollIntoViewMock = jest.fn();
|
|
|
|
HTMLElement.prototype.scrollIntoView = scrollIntoViewMock;
|
|
|
|
|
2020-05-07 08:09:46 -04:00
|
|
|
const localVue = createLocalVue();
|
2020-06-01 05:08:28 -04:00
|
|
|
const router = createRouter();
|
2020-05-07 08:09:46 -04:00
|
|
|
localVue.use(VueRouter);
|
|
|
|
|
|
|
|
const mockDesigns = [
|
|
|
|
{
|
|
|
|
id: 'design-1',
|
|
|
|
image: 'design-1-image',
|
|
|
|
filename: 'design-1-name',
|
|
|
|
event: 'NONE',
|
|
|
|
notesCount: 0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 'design-2',
|
|
|
|
image: 'design-2-image',
|
|
|
|
filename: 'design-2-name',
|
|
|
|
event: 'NONE',
|
|
|
|
notesCount: 1,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 'design-3',
|
|
|
|
image: 'design-3-image',
|
|
|
|
filename: 'design-3-name',
|
|
|
|
event: 'NONE',
|
|
|
|
notesCount: 0,
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
const mockVersion = {
|
2020-07-30 08:09:33 -04:00
|
|
|
id: 'gid://gitlab/DesignManagement::Version/1',
|
2020-05-07 08:09:46 -04:00
|
|
|
};
|
|
|
|
|
2020-09-07 05:08:17 -04:00
|
|
|
const designToMove = {
|
|
|
|
__typename: 'Design',
|
|
|
|
id: '2',
|
|
|
|
event: 'NONE',
|
|
|
|
filename: 'fox_2.jpg',
|
|
|
|
notesCount: 2,
|
|
|
|
image: 'image-2',
|
|
|
|
imageV432x230: 'image-2',
|
|
|
|
};
|
|
|
|
|
2020-05-07 08:09:46 -04:00
|
|
|
describe('Design management index page', () => {
|
|
|
|
let mutate;
|
|
|
|
let wrapper;
|
2020-09-07 05:08:17 -04:00
|
|
|
let fakeApollo;
|
|
|
|
let moveDesignHandler;
|
2020-05-07 08:09:46 -04:00
|
|
|
|
|
|
|
const findDesignCheckboxes = () => wrapper.findAll('.design-checkbox');
|
|
|
|
const findSelectAllButton = () => wrapper.find('.js-select-all');
|
|
|
|
const findToolbar = () => wrapper.find('.qa-selector-toolbar');
|
2020-09-22 11:09:37 -04:00
|
|
|
const findDesignCollectionIsCopying = () =>
|
|
|
|
wrapper.find('[data-testid="design-collection-is-copying"');
|
2020-05-07 08:09:46 -04:00
|
|
|
const findDeleteButton = () => wrapper.find(DeleteButton);
|
|
|
|
const findDropzone = () => wrapper.findAll(DesignDropzone).at(0);
|
2020-07-28 08:09:49 -04:00
|
|
|
const dropzoneClasses = () => findDropzone().classes();
|
|
|
|
const findDropzoneWrapper = () => wrapper.find('[data-testid="design-dropzone-wrapper"]');
|
2020-05-07 08:09:46 -04:00
|
|
|
const findFirstDropzoneWithDesign = () => wrapper.findAll(DesignDropzone).at(1);
|
2020-07-30 17:09:35 -04:00
|
|
|
const findDesignsWrapper = () => wrapper.find('[data-testid="designs-root"]');
|
2020-09-07 05:08:17 -04:00
|
|
|
const findDesigns = () => wrapper.findAll(Design);
|
2020-09-22 05:09:43 -04:00
|
|
|
const draggableAttributes = () => wrapper.find(VueDraggable).vm.$attrs;
|
2020-11-10 04:08:45 -05:00
|
|
|
const findDesignUploadButton = () => wrapper.find('[data-testid="design-upload-button"]');
|
|
|
|
const findDesignToolbarWrapper = () => wrapper.find('[data-testid="design-toolbar-wrapper"]');
|
2020-09-07 05:08:17 -04:00
|
|
|
|
|
|
|
async function moveDesigns(localWrapper) {
|
|
|
|
await jest.runOnlyPendingTimers();
|
|
|
|
await localWrapper.vm.$nextTick();
|
|
|
|
|
|
|
|
localWrapper.find(VueDraggable).vm.$emit('input', reorderedDesigns);
|
|
|
|
localWrapper.find(VueDraggable).vm.$emit('change', {
|
|
|
|
moved: {
|
|
|
|
newIndex: 0,
|
|
|
|
element: designToMove,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
2020-05-07 08:09:46 -04:00
|
|
|
|
|
|
|
function createComponent({
|
|
|
|
loading = false,
|
|
|
|
allVersions = [],
|
2020-09-22 11:09:37 -04:00
|
|
|
designCollection = { designs: mockDesigns, copyState: 'READY' },
|
2020-05-07 08:09:46 -04:00
|
|
|
createDesign = true,
|
|
|
|
stubs = {},
|
|
|
|
mockMutate = jest.fn().mockResolvedValue(),
|
|
|
|
} = {}) {
|
|
|
|
mutate = mockMutate;
|
|
|
|
const $apollo = {
|
|
|
|
queries: {
|
2020-09-22 11:09:37 -04:00
|
|
|
designCollection: {
|
2020-05-07 08:09:46 -04:00
|
|
|
loading,
|
|
|
|
},
|
|
|
|
permissions: {
|
|
|
|
loading,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
mutate,
|
|
|
|
};
|
|
|
|
|
|
|
|
wrapper = shallowMount(Index, {
|
2020-07-28 08:09:49 -04:00
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
allVersions,
|
2020-09-22 11:09:37 -04:00
|
|
|
designCollection,
|
2020-07-28 08:09:49 -04:00
|
|
|
permissions: {
|
|
|
|
createDesign,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
},
|
2020-05-07 08:09:46 -04:00
|
|
|
mocks: { $apollo },
|
|
|
|
localVue,
|
|
|
|
router,
|
2020-08-13 11:10:03 -04:00
|
|
|
stubs: { DesignDestroyer, ApolloMutation, VueDraggable, ...stubs },
|
2020-05-07 08:09:46 -04:00
|
|
|
attachToDocument: true,
|
2020-07-28 08:09:49 -04:00
|
|
|
provide: {
|
|
|
|
projectPath: 'project-path',
|
|
|
|
issueIid: '1',
|
2020-05-07 08:09:46 -04:00
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-09-07 05:08:17 -04:00
|
|
|
function createComponentWithApollo({
|
|
|
|
moveHandler = jest.fn().mockResolvedValue(moveDesignMutationResponse),
|
|
|
|
}) {
|
|
|
|
localVue.use(VueApollo);
|
|
|
|
moveDesignHandler = moveHandler;
|
|
|
|
|
|
|
|
const requestHandlers = [
|
|
|
|
[getDesignListQuery, jest.fn().mockResolvedValue(designListQueryResponse)],
|
|
|
|
[permissionsQuery, jest.fn().mockResolvedValue(permissionsQueryResponse)],
|
|
|
|
[moveDesignMutation, moveDesignHandler],
|
|
|
|
];
|
|
|
|
|
|
|
|
fakeApollo = createMockApollo(requestHandlers);
|
|
|
|
wrapper = shallowMount(Index, {
|
|
|
|
localVue,
|
|
|
|
apolloProvider: fakeApollo,
|
|
|
|
router,
|
|
|
|
stubs: { VueDraggable },
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-05-07 08:09:46 -04:00
|
|
|
afterEach(() => {
|
|
|
|
wrapper.destroy();
|
2020-09-07 05:08:17 -04:00
|
|
|
wrapper = null;
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('designs', () => {
|
|
|
|
it('renders loading icon', () => {
|
|
|
|
createComponent({ loading: true });
|
|
|
|
|
2020-07-28 08:09:49 -04:00
|
|
|
expect(wrapper.element).toMatchSnapshot();
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('renders error', () => {
|
|
|
|
createComponent();
|
|
|
|
|
|
|
|
wrapper.setData({ error: true });
|
|
|
|
|
|
|
|
return wrapper.vm.$nextTick().then(() => {
|
|
|
|
expect(wrapper.element).toMatchSnapshot();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders a toolbar with buttons when there are designs', () => {
|
2020-09-22 11:09:37 -04:00
|
|
|
createComponent({ allVersions: [mockVersion] });
|
2020-05-07 08:09:46 -04:00
|
|
|
|
2020-07-28 08:09:49 -04:00
|
|
|
expect(findToolbar().exists()).toBe(true);
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('renders designs list and header with upload button', () => {
|
2020-09-22 11:09:37 -04:00
|
|
|
createComponent({ allVersions: [mockVersion] });
|
2020-05-07 08:09:46 -04:00
|
|
|
|
2020-11-10 04:08:45 -05:00
|
|
|
expect(findDesignsWrapper().exists()).toBe(true);
|
|
|
|
expect(findDesigns().length).toBe(3);
|
|
|
|
expect(findDesignToolbarWrapper().exists()).toBe(true);
|
|
|
|
expect(findDesignUploadButton().exists()).toBe(true);
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('does not render toolbar when there is no permission', () => {
|
|
|
|
createComponent({ designs: mockDesigns, allVersions: [mockVersion], createDesign: false });
|
|
|
|
|
2020-11-10 04:08:45 -05:00
|
|
|
expect(findDesignToolbarWrapper().exists()).toBe(false);
|
|
|
|
expect(findDesignUploadButton().exists()).toBe(false);
|
2020-07-28 08:09:49 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('has correct classes applied to design dropzone', () => {
|
|
|
|
createComponent({ designs: mockDesigns, allVersions: [mockVersion] });
|
|
|
|
expect(dropzoneClasses()).toContain('design-list-item');
|
|
|
|
expect(dropzoneClasses()).toContain('design-list-item-new');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('has correct classes applied to dropzone wrapper', () => {
|
|
|
|
createComponent({ designs: mockDesigns, allVersions: [mockVersion] });
|
|
|
|
expect(findDropzoneWrapper().classes()).toEqual([
|
|
|
|
'gl-flex-direction-column',
|
|
|
|
'col-md-6',
|
|
|
|
'col-lg-3',
|
|
|
|
'gl-mb-3',
|
|
|
|
]);
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when has no designs', () => {
|
|
|
|
beforeEach(() => {
|
2020-09-22 11:09:37 -04:00
|
|
|
createComponent({ designCollection: { designs: [], copyState: 'READY' } });
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
|
2020-07-28 08:09:49 -04:00
|
|
|
it('renders design dropzone', () =>
|
2020-05-07 08:09:46 -04:00
|
|
|
wrapper.vm.$nextTick().then(() => {
|
2020-11-10 04:08:45 -05:00
|
|
|
expect(findDropzone().exists()).toBe(true);
|
2020-05-07 08:09:46 -04:00
|
|
|
}));
|
|
|
|
|
2020-07-28 08:09:49 -04:00
|
|
|
it('has correct classes applied to design dropzone', () => {
|
|
|
|
expect(dropzoneClasses()).not.toContain('design-list-item');
|
|
|
|
expect(dropzoneClasses()).not.toContain('design-list-item-new');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('has correct classes applied to dropzone wrapper', () => {
|
|
|
|
expect(findDropzoneWrapper().classes()).toEqual(['col-12']);
|
|
|
|
});
|
|
|
|
|
2020-05-07 08:09:46 -04:00
|
|
|
it('does not render a toolbar with buttons', () =>
|
|
|
|
wrapper.vm.$nextTick().then(() => {
|
|
|
|
expect(findToolbar().exists()).toBe(false);
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
|
2020-09-22 11:09:37 -04:00
|
|
|
describe('handling design collection copy state', () => {
|
|
|
|
it.each`
|
2020-09-30 11:09:46 -04:00
|
|
|
copyState | isRendered | description
|
|
|
|
${'IN_PROGRESS'} | ${true} | ${'renders'}
|
|
|
|
${'READY'} | ${false} | ${'does not render'}
|
|
|
|
${'ERROR'} | ${false} | ${'does not render'}
|
2020-09-22 11:09:37 -04:00
|
|
|
`(
|
|
|
|
'$description the copying message if design collection copyState is $copyState',
|
|
|
|
({ copyState, isRendered }) => {
|
|
|
|
createComponent({ designCollection: { designs: [], copyState } });
|
|
|
|
expect(findDesignCollectionIsCopying().exists()).toBe(isRendered);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2020-05-07 08:09:46 -04:00
|
|
|
describe('uploading designs', () => {
|
|
|
|
it('calls mutation on upload', () => {
|
|
|
|
createComponent({ stubs: { GlEmptyState } });
|
|
|
|
|
|
|
|
const mutationVariables = {
|
|
|
|
update: expect.anything(),
|
|
|
|
context: {
|
|
|
|
hasUpload: true,
|
|
|
|
},
|
|
|
|
mutation: uploadDesignQuery,
|
|
|
|
variables: {
|
|
|
|
files: [{ name: 'test' }],
|
2020-07-28 08:09:49 -04:00
|
|
|
projectPath: 'project-path',
|
2020-05-07 08:09:46 -04:00
|
|
|
iid: '1',
|
|
|
|
},
|
|
|
|
optimisticResponse: {
|
|
|
|
__typename: 'Mutation',
|
|
|
|
designManagementUpload: {
|
|
|
|
__typename: 'DesignManagementUploadPayload',
|
|
|
|
designs: [
|
|
|
|
{
|
|
|
|
__typename: 'Design',
|
|
|
|
id: expect.anything(),
|
2020-09-22 11:09:37 -04:00
|
|
|
currentUserTodos: {
|
|
|
|
__typename: 'TodoConnection',
|
|
|
|
nodes: [],
|
|
|
|
},
|
2020-05-07 08:09:46 -04:00
|
|
|
image: '',
|
|
|
|
imageV432x230: '',
|
|
|
|
filename: 'test',
|
|
|
|
fullPath: '',
|
|
|
|
event: 'NONE',
|
|
|
|
notesCount: 0,
|
|
|
|
diffRefs: {
|
|
|
|
__typename: 'DiffRefs',
|
|
|
|
baseSha: '',
|
|
|
|
startSha: '',
|
|
|
|
headSha: '',
|
|
|
|
},
|
|
|
|
discussions: {
|
|
|
|
__typename: 'DesignDiscussion',
|
|
|
|
nodes: [],
|
|
|
|
},
|
|
|
|
versions: {
|
|
|
|
__typename: 'DesignVersionConnection',
|
2020-07-30 08:09:33 -04:00
|
|
|
nodes: {
|
|
|
|
__typename: 'DesignVersion',
|
|
|
|
id: expect.anything(),
|
|
|
|
sha: expect.anything(),
|
2020-05-07 08:09:46 -04:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
skippedDesigns: [],
|
|
|
|
errors: [],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2020-07-28 08:09:49 -04:00
|
|
|
return wrapper.vm
|
|
|
|
.$nextTick()
|
|
|
|
.then(() => {
|
|
|
|
findDropzone().vm.$emit('change', [{ name: 'test' }]);
|
|
|
|
expect(mutate).toHaveBeenCalledWith(mutationVariables);
|
|
|
|
expect(wrapper.vm.filesToBeSaved).toEqual([{ name: 'test' }]);
|
|
|
|
expect(wrapper.vm.isSaving).toBeTruthy();
|
|
|
|
})
|
|
|
|
.then(() => {
|
|
|
|
expect(dropzoneClasses()).toContain('design-list-item');
|
|
|
|
expect(dropzoneClasses()).toContain('design-list-item-new');
|
|
|
|
});
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('sets isSaving', () => {
|
|
|
|
createComponent();
|
|
|
|
|
|
|
|
const uploadDesign = wrapper.vm.onUploadDesign([
|
|
|
|
{
|
|
|
|
name: 'test',
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
|
|
|
|
expect(wrapper.vm.isSaving).toBe(true);
|
|
|
|
|
|
|
|
return uploadDesign.then(() => {
|
|
|
|
expect(wrapper.vm.isSaving).toBe(false);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('updates state appropriately after upload complete', () => {
|
|
|
|
createComponent({ stubs: { GlEmptyState } });
|
|
|
|
wrapper.setData({ filesToBeSaved: [{ name: 'test' }] });
|
|
|
|
|
2020-10-14 05:08:46 -04:00
|
|
|
wrapper.vm.onUploadDesignDone(designUploadMutationCreatedResponse);
|
2020-05-07 08:09:46 -04:00
|
|
|
return wrapper.vm.$nextTick().then(() => {
|
|
|
|
expect(wrapper.vm.filesToBeSaved).toEqual([]);
|
|
|
|
expect(wrapper.vm.isSaving).toBeFalsy();
|
|
|
|
expect(wrapper.vm.isLatestVersion).toBe(true);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('updates state appropriately after upload error', () => {
|
|
|
|
createComponent({ stubs: { GlEmptyState } });
|
|
|
|
wrapper.setData({ filesToBeSaved: [{ name: 'test' }] });
|
|
|
|
|
|
|
|
wrapper.vm.onUploadDesignError();
|
|
|
|
return wrapper.vm.$nextTick().then(() => {
|
|
|
|
expect(wrapper.vm.filesToBeSaved).toEqual([]);
|
|
|
|
expect(wrapper.vm.isSaving).toBeFalsy();
|
|
|
|
expect(createFlash).toHaveBeenCalled();
|
|
|
|
|
|
|
|
createFlash.mockReset();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('does not call mutation if createDesign is false', () => {
|
|
|
|
createComponent({ createDesign: false });
|
|
|
|
|
|
|
|
wrapper.vm.onUploadDesign([]);
|
|
|
|
|
|
|
|
expect(mutate).not.toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('upload count limit', () => {
|
|
|
|
const MAXIMUM_FILE_UPLOAD_LIMIT = 10;
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
createFlash.mockReset();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('does not warn when the max files are uploaded', () => {
|
|
|
|
createComponent();
|
|
|
|
|
|
|
|
wrapper.vm.onUploadDesign(new Array(MAXIMUM_FILE_UPLOAD_LIMIT).fill(mockDesigns[0]));
|
|
|
|
|
|
|
|
expect(createFlash).not.toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('warns when too many files are uploaded', () => {
|
|
|
|
createComponent();
|
|
|
|
|
|
|
|
wrapper.vm.onUploadDesign(new Array(MAXIMUM_FILE_UPLOAD_LIMIT + 1).fill(mockDesigns[0]));
|
|
|
|
|
|
|
|
expect(createFlash).toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('flashes warning if designs are skipped', () => {
|
|
|
|
createComponent({
|
|
|
|
mockMutate: () =>
|
|
|
|
Promise.resolve({
|
|
|
|
data: { designManagementUpload: { skippedDesigns: [{ filename: 'test.jpg' }] } },
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
|
|
|
|
const uploadDesign = wrapper.vm.onUploadDesign([
|
|
|
|
{
|
|
|
|
name: 'test',
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
|
|
|
|
return uploadDesign.then(() => {
|
|
|
|
expect(createFlash).toHaveBeenCalledTimes(1);
|
2020-10-28 14:08:52 -04:00
|
|
|
expect(createFlash).toHaveBeenCalledWith({
|
|
|
|
message: 'Upload skipped. test.jpg did not change.',
|
|
|
|
types: 'warning',
|
|
|
|
});
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('dragging onto an existing design', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
createComponent({ designs: mockDesigns, allVersions: [mockVersion] });
|
|
|
|
});
|
|
|
|
|
|
|
|
it('calls onUploadDesign with valid upload', () => {
|
|
|
|
wrapper.setMethods({
|
|
|
|
onUploadDesign: jest.fn(),
|
|
|
|
});
|
|
|
|
|
|
|
|
const mockUploadPayload = [
|
|
|
|
{
|
|
|
|
name: mockDesigns[0].filename,
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
const designDropzone = findFirstDropzoneWithDesign();
|
|
|
|
designDropzone.vm.$emit('change', mockUploadPayload);
|
|
|
|
|
|
|
|
expect(wrapper.vm.onUploadDesign).toHaveBeenCalledTimes(1);
|
|
|
|
expect(wrapper.vm.onUploadDesign).toHaveBeenCalledWith(mockUploadPayload);
|
|
|
|
});
|
|
|
|
|
|
|
|
it.each`
|
|
|
|
description | eventPayload | message
|
|
|
|
${'> 1 file'} | ${[{ name: 'test' }, { name: 'test-2' }]} | ${EXISTING_DESIGN_DROP_MANY_FILES_MESSAGE}
|
|
|
|
${'different filename'} | ${[{ name: 'wrong-name' }]} | ${EXISTING_DESIGN_DROP_INVALID_FILENAME_MESSAGE}
|
|
|
|
`('calls createFlash when upload has $description', ({ eventPayload, message }) => {
|
|
|
|
const designDropzone = findFirstDropzoneWithDesign();
|
|
|
|
designDropzone.vm.$emit('change', eventPayload);
|
|
|
|
|
|
|
|
expect(createFlash).toHaveBeenCalledTimes(1);
|
2020-10-28 14:08:52 -04:00
|
|
|
expect(createFlash).toHaveBeenCalledWith({ message });
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
});
|
2020-10-14 05:08:46 -04:00
|
|
|
|
|
|
|
describe('tracking', () => {
|
|
|
|
let trackingSpy;
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
trackingSpy = mockTracking('_category_', undefined, jest.spyOn);
|
|
|
|
|
|
|
|
createComponent({ stubs: { GlEmptyState } });
|
|
|
|
});
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
unmockTracking();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('tracks design creation', () => {
|
|
|
|
wrapper.vm.onUploadDesignDone(designUploadMutationCreatedResponse);
|
|
|
|
|
|
|
|
expect(trackingSpy).toHaveBeenCalledTimes(1);
|
2020-12-07 19:09:45 -05:00
|
|
|
expect(trackingSpy).toHaveBeenCalledWith(
|
|
|
|
DESIGN_TRACKING_PAGE_NAME,
|
|
|
|
DESIGN_SNOWPLOW_EVENT_TYPES.CREATE_DESIGN,
|
|
|
|
);
|
2020-10-14 05:08:46 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('tracks design modification', () => {
|
|
|
|
wrapper.vm.onUploadDesignDone(designUploadMutationUpdatedResponse);
|
|
|
|
|
|
|
|
expect(trackingSpy).toHaveBeenCalledTimes(1);
|
2020-12-07 19:09:45 -05:00
|
|
|
expect(trackingSpy).toHaveBeenCalledWith(
|
|
|
|
DESIGN_TRACKING_PAGE_NAME,
|
|
|
|
DESIGN_SNOWPLOW_EVENT_TYPES.UPDATE_DESIGN,
|
|
|
|
);
|
2020-10-14 05:08:46 -04:00
|
|
|
});
|
|
|
|
});
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('on latest version when has designs', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
createComponent({ designs: mockDesigns, allVersions: [mockVersion] });
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders design checkboxes', () => {
|
|
|
|
expect(findDesignCheckboxes()).toHaveLength(mockDesigns.length);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders toolbar buttons', () => {
|
|
|
|
expect(findToolbar().exists()).toBe(true);
|
2020-07-28 08:09:49 -04:00
|
|
|
expect(findToolbar().isVisible()).toBe(true);
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('adds two designs to selected designs when their checkboxes are checked', () => {
|
|
|
|
findDesignCheckboxes()
|
|
|
|
.at(0)
|
|
|
|
.trigger('click');
|
|
|
|
|
|
|
|
return wrapper.vm
|
|
|
|
.$nextTick()
|
|
|
|
.then(() => {
|
|
|
|
findDesignCheckboxes()
|
|
|
|
.at(1)
|
|
|
|
.trigger('click');
|
|
|
|
|
|
|
|
return wrapper.vm.$nextTick();
|
|
|
|
})
|
|
|
|
.then(() => {
|
|
|
|
expect(findDeleteButton().exists()).toBe(true);
|
|
|
|
expect(findSelectAllButton().text()).toBe('Deselect all');
|
|
|
|
findDeleteButton().vm.$emit('deleteSelectedDesigns');
|
|
|
|
const [{ variables }] = mutate.mock.calls[0];
|
|
|
|
expect(variables.filenames).toStrictEqual([
|
|
|
|
mockDesigns[0].filename,
|
|
|
|
mockDesigns[1].filename,
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('adds all designs to selected designs when Select All button is clicked', () => {
|
|
|
|
findSelectAllButton().vm.$emit('click');
|
|
|
|
|
|
|
|
return wrapper.vm.$nextTick().then(() => {
|
|
|
|
expect(findDeleteButton().props().hasSelectedDesigns).toBe(true);
|
|
|
|
expect(findSelectAllButton().text()).toBe('Deselect all');
|
|
|
|
expect(wrapper.vm.selectedDesigns).toEqual(mockDesigns.map(design => design.filename));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('removes all designs from selected designs when at least one design was selected', () => {
|
|
|
|
findDesignCheckboxes()
|
|
|
|
.at(0)
|
|
|
|
.trigger('click');
|
|
|
|
|
|
|
|
return wrapper.vm
|
|
|
|
.$nextTick()
|
|
|
|
.then(() => {
|
|
|
|
findSelectAllButton().vm.$emit('click');
|
|
|
|
})
|
|
|
|
.then(() => {
|
|
|
|
expect(findDeleteButton().props().hasSelectedDesigns).toBe(false);
|
|
|
|
expect(findSelectAllButton().text()).toBe('Select all');
|
|
|
|
expect(wrapper.vm.selectedDesigns).toEqual([]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-07-28 08:09:49 -04:00
|
|
|
it('on latest version when has no designs toolbar buttons are invisible', () => {
|
2020-09-22 11:09:37 -04:00
|
|
|
createComponent({
|
|
|
|
designCollection: { designs: [], copyState: 'READY' },
|
|
|
|
allVersions: [mockVersion],
|
|
|
|
});
|
2020-07-28 08:09:49 -04:00
|
|
|
expect(findToolbar().isVisible()).toBe(false);
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('on non-latest version', () => {
|
|
|
|
beforeEach(() => {
|
2020-09-22 11:09:37 -04:00
|
|
|
createComponent({ allVersions: [mockVersion] });
|
2020-08-27 14:10:29 -04:00
|
|
|
});
|
2020-05-07 08:09:46 -04:00
|
|
|
|
2020-08-27 14:10:29 -04:00
|
|
|
it('does not render design checkboxes', async () => {
|
|
|
|
await router.replace({
|
2020-05-07 08:09:46 -04:00
|
|
|
name: DESIGNS_ROUTE_NAME,
|
|
|
|
query: {
|
|
|
|
version: '2',
|
|
|
|
},
|
|
|
|
});
|
|
|
|
expect(findDesignCheckboxes()).toHaveLength(0);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('does not render Delete selected button', () => {
|
|
|
|
expect(findDeleteButton().exists()).toBe(false);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('does not render Select All button', () => {
|
|
|
|
expect(findSelectAllButton().exists()).toBe(false);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('pasting a design', () => {
|
|
|
|
let event;
|
|
|
|
beforeEach(() => {
|
|
|
|
createComponent({ designs: mockDesigns, allVersions: [mockVersion] });
|
|
|
|
|
|
|
|
wrapper.setMethods({
|
|
|
|
onUploadDesign: jest.fn(),
|
|
|
|
});
|
|
|
|
|
|
|
|
event = new Event('paste');
|
2020-07-30 17:09:35 -04:00
|
|
|
event.clipboardData = {
|
|
|
|
files: [{ name: 'image.png', type: 'image/png' }],
|
|
|
|
getData: () => 'test.png',
|
|
|
|
};
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
|
2020-07-30 17:09:35 -04:00
|
|
|
it('does not call paste event if designs wrapper is not hovered', () => {
|
2020-05-07 08:09:46 -04:00
|
|
|
document.dispatchEvent(event);
|
|
|
|
|
2020-07-30 17:09:35 -04:00
|
|
|
expect(wrapper.vm.onUploadDesign).not.toHaveBeenCalled();
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
|
2020-07-30 17:09:35 -04:00
|
|
|
describe('when designs wrapper is hovered', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
findDesignsWrapper().trigger('mouseenter');
|
|
|
|
});
|
2020-05-07 08:09:46 -04:00
|
|
|
|
2020-07-30 17:09:35 -04:00
|
|
|
it('calls onUploadDesign with valid paste', () => {
|
|
|
|
document.dispatchEvent(event);
|
2020-05-07 08:09:46 -04:00
|
|
|
|
2020-07-30 17:09:35 -04:00
|
|
|
expect(wrapper.vm.onUploadDesign).toHaveBeenCalledTimes(1);
|
|
|
|
expect(wrapper.vm.onUploadDesign).toHaveBeenCalledWith([
|
|
|
|
new File([{ name: 'image.png' }], 'test.png'),
|
|
|
|
]);
|
|
|
|
});
|
2020-05-07 08:09:46 -04:00
|
|
|
|
2020-07-30 17:09:35 -04:00
|
|
|
it('renames a design if it has an image.png filename', () => {
|
|
|
|
document.dispatchEvent(event);
|
2020-05-07 08:09:46 -04:00
|
|
|
|
2020-07-30 17:09:35 -04:00
|
|
|
expect(wrapper.vm.onUploadDesign).toHaveBeenCalledTimes(1);
|
|
|
|
expect(wrapper.vm.onUploadDesign).toHaveBeenCalledWith([
|
|
|
|
new File([{ name: 'image.png' }], `design_${Date.now()}.png`),
|
|
|
|
]);
|
|
|
|
});
|
2020-05-07 08:09:46 -04:00
|
|
|
|
2020-07-30 17:09:35 -04:00
|
|
|
it('does not call onUploadDesign with invalid paste', () => {
|
|
|
|
event.clipboardData = {
|
|
|
|
items: [{ type: 'text/plain' }, { type: 'text' }],
|
|
|
|
files: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
document.dispatchEvent(event);
|
|
|
|
|
|
|
|
expect(wrapper.vm.onUploadDesign).not.toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('removes onPaste listener after mouseleave event', async () => {
|
|
|
|
findDesignsWrapper().trigger('mouseleave');
|
|
|
|
document.dispatchEvent(event);
|
|
|
|
|
|
|
|
expect(wrapper.vm.onUploadDesign).not.toHaveBeenCalled();
|
|
|
|
});
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|
|
|
|
});
|
2020-06-01 05:08:28 -04:00
|
|
|
|
|
|
|
describe('when navigating', () => {
|
2020-07-28 08:09:49 -04:00
|
|
|
it('should trigger a scrollIntoView method if designs route is detected', () => {
|
|
|
|
router.replace({
|
|
|
|
path: '/designs',
|
|
|
|
});
|
2020-10-08 05:08:40 -04:00
|
|
|
createComponent({ loading: true });
|
2020-07-28 08:09:49 -04:00
|
|
|
|
2020-08-31 23:10:22 -04:00
|
|
|
return wrapper.vm.$nextTick().then(() => {
|
|
|
|
expect(scrollIntoViewMock).toHaveBeenCalled();
|
|
|
|
});
|
2020-07-28 08:09:49 -04:00
|
|
|
});
|
2020-06-01 05:08:28 -04:00
|
|
|
});
|
2020-09-07 05:08:17 -04:00
|
|
|
|
|
|
|
describe('with mocked Apollo client', () => {
|
|
|
|
it('has a design with id 1 as a first one', async () => {
|
|
|
|
createComponentWithApollo({});
|
|
|
|
|
|
|
|
await jest.runOnlyPendingTimers();
|
|
|
|
await wrapper.vm.$nextTick();
|
|
|
|
|
|
|
|
expect(findDesigns()).toHaveLength(3);
|
|
|
|
expect(
|
|
|
|
findDesigns()
|
|
|
|
.at(0)
|
|
|
|
.props('id'),
|
|
|
|
).toBe('1');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('calls a mutation with correct parameters and reorders designs', async () => {
|
|
|
|
createComponentWithApollo({});
|
|
|
|
|
|
|
|
await moveDesigns(wrapper);
|
|
|
|
|
|
|
|
expect(moveDesignHandler).toHaveBeenCalled();
|
|
|
|
|
|
|
|
await wrapper.vm.$nextTick();
|
|
|
|
|
|
|
|
expect(
|
|
|
|
findDesigns()
|
|
|
|
.at(0)
|
|
|
|
.props('id'),
|
|
|
|
).toBe('2');
|
|
|
|
});
|
|
|
|
|
2020-09-22 05:09:43 -04:00
|
|
|
it('prevents reordering when reorderDesigns mutation is in progress', async () => {
|
|
|
|
createComponentWithApollo({});
|
|
|
|
|
|
|
|
await moveDesigns(wrapper);
|
|
|
|
|
|
|
|
expect(draggableAttributes().disabled).toBe(true);
|
|
|
|
|
|
|
|
await jest.runOnlyPendingTimers(); // kick off the mocked GQL stuff (promises)
|
|
|
|
await wrapper.vm.$nextTick(); // kick off the DOM update
|
|
|
|
await wrapper.vm.$nextTick(); // kick off the DOM update for finally block
|
|
|
|
|
|
|
|
expect(draggableAttributes().disabled).toBe(false);
|
|
|
|
});
|
|
|
|
|
2020-09-07 05:08:17 -04:00
|
|
|
it('displays flash if mutation had a recoverable error', async () => {
|
|
|
|
createComponentWithApollo({
|
|
|
|
moveHandler: jest.fn().mockResolvedValue(moveDesignMutationResponseWithErrors),
|
|
|
|
});
|
|
|
|
|
|
|
|
await moveDesigns(wrapper);
|
|
|
|
|
|
|
|
await wrapper.vm.$nextTick();
|
|
|
|
|
2020-10-28 14:08:52 -04:00
|
|
|
expect(createFlash).toHaveBeenCalledWith({ message: 'Houston, we have a problem' });
|
2020-09-07 05:08:17 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('displays flash if mutation had a non-recoverable error', async () => {
|
|
|
|
createComponentWithApollo({
|
|
|
|
moveHandler: jest.fn().mockRejectedValue('Error'),
|
|
|
|
});
|
|
|
|
|
|
|
|
await moveDesigns(wrapper);
|
|
|
|
|
|
|
|
await wrapper.vm.$nextTick(); // kick off the DOM update
|
|
|
|
await jest.runOnlyPendingTimers(); // kick off the mocked GQL stuff (promises)
|
|
|
|
await wrapper.vm.$nextTick(); // kick off the DOM update for flash
|
|
|
|
|
2020-10-28 14:08:52 -04:00
|
|
|
expect(createFlash).toHaveBeenCalledWith({
|
|
|
|
message: 'Something went wrong when reordering designs. Please try again',
|
|
|
|
});
|
2020-09-07 05:08:17 -04:00
|
|
|
});
|
|
|
|
});
|
2020-05-07 08:09:46 -04:00
|
|
|
});
|