summaryrefslogtreecommitdiff
path: root/spec/frontend/pages
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-10-10 00:06:44 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2019-10-10 00:06:44 +0000
commit308146dc398fd4c13453048105498018459e0985 (patch)
treed843eb63c1672e4b18c483907e2cd4aa7fca708e /spec/frontend/pages
parent4b28d5ae770c6bd332283a3f13ceae06329c409b (diff)
downloadgitlab-ce-308146dc398fd4c13453048105498018459e0985.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/pages')
-rw-r--r--spec/frontend/pages/admin/users/components/__snapshots__/delete_user_modal_spec.js.snap63
-rw-r--r--spec/frontend/pages/admin/users/components/__snapshots__/user_operation_confirmation_modal_spec.js.snap33
-rw-r--r--spec/frontend/pages/admin/users/components/delete_user_modal_spec.js85
-rw-r--r--spec/frontend/pages/admin/users/components/stubs/modal_stub.js23
-rw-r--r--spec/frontend/pages/admin/users/components/user_modal_manager_spec.js148
-rw-r--r--spec/frontend/pages/admin/users/components/user_operation_confirmation_modal_spec.js47
6 files changed, 399 insertions, 0 deletions
diff --git a/spec/frontend/pages/admin/users/components/__snapshots__/delete_user_modal_spec.js.snap b/spec/frontend/pages/admin/users/components/__snapshots__/delete_user_modal_spec.js.snap
new file mode 100644
index 00000000000..78a736a9060
--- /dev/null
+++ b/spec/frontend/pages/admin/users/components/__snapshots__/delete_user_modal_spec.js.snap
@@ -0,0 +1,63 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`User Operation confirmation modal renders modal with form included 1`] = `
+<div>
+ <p>
+ content
+ </p>
+
+ <p>
+ To confirm, type
+ <code>
+ username
+ </code>
+ </p>
+
+ <form
+ action="delete-url"
+ method="post"
+ >
+ <input
+ name="_method"
+ type="hidden"
+ value="delete"
+ />
+
+ <input
+ name="authenticity_token"
+ type="hidden"
+ value="csrf"
+ />
+
+ <glforminput-stub
+ autocomplete="off"
+ autofocus=""
+ name="username"
+ type="text"
+ value=""
+ />
+ </form>
+
+ <glbutton-stub
+ variant="secondary"
+ >
+ Cancel
+ </glbutton-stub>
+
+ <glbutton-stub
+ disabled="true"
+ variant="warning"
+ >
+
+ secondaryAction
+
+ </glbutton-stub>
+
+ <glbutton-stub
+ disabled="true"
+ variant="danger"
+ >
+ action
+ </glbutton-stub>
+</div>
+`;
diff --git a/spec/frontend/pages/admin/users/components/__snapshots__/user_operation_confirmation_modal_spec.js.snap b/spec/frontend/pages/admin/users/components/__snapshots__/user_operation_confirmation_modal_spec.js.snap
new file mode 100644
index 00000000000..4a3989f5192
--- /dev/null
+++ b/spec/frontend/pages/admin/users/components/__snapshots__/user_operation_confirmation_modal_spec.js.snap
@@ -0,0 +1,33 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`User Operation confirmation modal renders modal with form included 1`] = `
+<glmodal-stub
+ modalclass=""
+ modalid="user-operation-modal"
+ ok-title="action"
+ ok-variant="warning"
+ title="title"
+ titletag="h4"
+>
+ <form
+ action="/url"
+ method="post"
+ >
+ <span>
+ content
+ </span>
+
+ <input
+ name="_method"
+ type="hidden"
+ value="method"
+ />
+
+ <input
+ name="authenticity_token"
+ type="hidden"
+ value="csrf"
+ />
+ </form>
+</glmodal-stub>
+`;
diff --git a/spec/frontend/pages/admin/users/components/delete_user_modal_spec.js b/spec/frontend/pages/admin/users/components/delete_user_modal_spec.js
new file mode 100644
index 00000000000..57802a41bb5
--- /dev/null
+++ b/spec/frontend/pages/admin/users/components/delete_user_modal_spec.js
@@ -0,0 +1,85 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlButton, GlFormInput } from '@gitlab/ui';
+import DeleteUserModal from '~/pages/admin/users/components/delete_user_modal.vue';
+import ModalStub from './stubs/modal_stub';
+
+describe('User Operation confirmation modal', () => {
+ let wrapper;
+
+ const findButton = variant =>
+ wrapper
+ .findAll(GlButton)
+ .filter(w => w.attributes('variant') === variant)
+ .at(0);
+
+ const createComponent = (props = {}) => {
+ wrapper = shallowMount(DeleteUserModal, {
+ propsData: {
+ title: 'title',
+ content: 'content',
+ action: 'action',
+ secondaryAction: 'secondaryAction',
+ deleteUserUrl: 'delete-url',
+ blockUserUrl: 'block-url',
+ username: 'username',
+ csrfToken: 'csrf',
+ ...props,
+ },
+ stubs: {
+ GlModal: ModalStub,
+ },
+ sync: false,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('renders modal with form included', () => {
+ createComponent();
+ expect(wrapper.element).toMatchSnapshot();
+ });
+
+ it.each`
+ variant | prop | action
+ ${'danger'} | ${'deleteUserUrl'} | ${'delete'}
+ ${'warning'} | ${'blockUserUrl'} | ${'block'}
+ `('closing modal with $variant button triggers $action', ({ variant, prop }) => {
+ createComponent();
+ const form = wrapper.find('form');
+ jest.spyOn(form.element, 'submit').mockReturnValue();
+ const modalButton = findButton(variant);
+ modalButton.vm.$emit('click');
+ return wrapper.vm.$nextTick().then(() => {
+ expect(form.element.submit).toHaveBeenCalled();
+ expect(form.element.action).toContain(wrapper.props(prop));
+ expect(new FormData(form.element).get('authenticity_token')).toEqual(
+ wrapper.props('csrfToken'),
+ );
+ });
+ });
+
+ it('disables buttons by default', () => {
+ createComponent();
+ const blockButton = findButton('warning');
+ const deleteButton = findButton('danger');
+ expect(blockButton.attributes().disabled).toBeTruthy();
+ expect(deleteButton.attributes().disabled).toBeTruthy();
+ });
+
+ it('enables button when username is typed', () => {
+ createComponent({
+ username: 'some-username',
+ });
+ wrapper.find(GlFormInput).vm.$emit('input', 'some-username');
+ const blockButton = findButton('warning');
+ const deleteButton = findButton('danger');
+
+ return wrapper.vm.$nextTick().then(() => {
+ expect(blockButton.attributes().disabled).toBeFalsy();
+ expect(deleteButton.attributes().disabled).toBeFalsy();
+ });
+ });
+});
diff --git a/spec/frontend/pages/admin/users/components/stubs/modal_stub.js b/spec/frontend/pages/admin/users/components/stubs/modal_stub.js
new file mode 100644
index 00000000000..4dc55e909a0
--- /dev/null
+++ b/spec/frontend/pages/admin/users/components/stubs/modal_stub.js
@@ -0,0 +1,23 @@
+const ModalStub = {
+ inheritAttrs: false,
+ name: 'glmodal-stub',
+ data() {
+ return {
+ showWasCalled: false,
+ };
+ },
+ methods: {
+ show() {
+ this.showWasCalled = true;
+ },
+ hide() {},
+ },
+ render(h) {
+ const children = [this.$slots.default, this.$slots['modal-footer']]
+ .filter(Boolean)
+ .reduce((acc, nodes) => acc.concat(nodes), []);
+ return h('div', children);
+ },
+};
+
+export default ModalStub;
diff --git a/spec/frontend/pages/admin/users/components/user_modal_manager_spec.js b/spec/frontend/pages/admin/users/components/user_modal_manager_spec.js
new file mode 100644
index 00000000000..7653fffc502
--- /dev/null
+++ b/spec/frontend/pages/admin/users/components/user_modal_manager_spec.js
@@ -0,0 +1,148 @@
+import { shallowMount } from '@vue/test-utils';
+import UserModalManager from '~/pages/admin/users/components/user_modal_manager.vue';
+import ModalStub from './stubs/modal_stub';
+
+describe('Users admin page Modal Manager', () => {
+ const modalConfiguration = {
+ action1: {
+ title: 'action1',
+ content: 'Action Modal 1',
+ },
+ action2: {
+ title: 'action2',
+ content: 'Action Modal 2',
+ },
+ };
+
+ const actionModals = {
+ action1: ModalStub,
+ action2: ModalStub,
+ };
+
+ let wrapper;
+
+ const createComponent = (props = {}) => {
+ wrapper = shallowMount(UserModalManager, {
+ propsData: {
+ actionModals,
+ modalConfiguration,
+ csrfToken: 'dummyCSRF',
+ ...props,
+ },
+ stubs: {
+ dummyComponent1: true,
+ dummyComponent2: true,
+ },
+ sync: false,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('render behavior', () => {
+ it('does not renders modal when initialized', () => {
+ createComponent();
+ expect(wrapper.find({ ref: 'modal' }).exists()).toBeFalsy();
+ });
+
+ it('throws if non-existing action is requested', () => {
+ createComponent();
+ expect(() => wrapper.vm.show({ glModalAction: 'non-existing' })).toThrow();
+ });
+
+ it('throws if action has no proper configuration', () => {
+ createComponent({
+ modalConfiguration: {},
+ });
+ expect(() => wrapper.vm.show({ glModalAction: 'action1' })).toThrow();
+ });
+
+ it('renders modal with expected props when valid configuration is passed', () => {
+ createComponent();
+ wrapper.vm.show({
+ glModalAction: 'action1',
+ extraProp: 'extraPropValue',
+ });
+
+ return wrapper.vm.$nextTick().then(() => {
+ const modal = wrapper.find({ ref: 'modal' });
+ expect(modal.exists()).toBeTruthy();
+ expect(modal.vm.$attrs.csrfToken).toEqual('dummyCSRF');
+ expect(modal.vm.$attrs.extraProp).toEqual('extraPropValue');
+ expect(modal.vm.showWasCalled).toBeTruthy();
+ });
+ });
+ });
+
+ describe('global listener', () => {
+ beforeEach(() => {
+ jest.spyOn(document, 'addEventListener');
+ jest.spyOn(document, 'removeEventListener');
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ afterAll(() => {
+ jest.restoreAllMocks();
+ });
+
+ it('registers global listener on mount', () => {
+ createComponent();
+ expect(document.addEventListener).toHaveBeenCalledWith('click', expect.any(Function));
+ });
+
+ it('removes global listener on destroy', () => {
+ createComponent();
+ wrapper.destroy();
+ expect(document.removeEventListener).toHaveBeenCalledWith('click', expect.any(Function));
+ });
+ });
+
+ describe('click handling', () => {
+ let node;
+
+ beforeEach(() => {
+ node = document.createElement('div');
+ document.body.appendChild(node);
+ });
+
+ afterEach(() => {
+ node.remove();
+ node = null;
+ });
+
+ it('ignores wrong clicks', () => {
+ createComponent();
+ const event = new window.MouseEvent('click', {
+ bubbles: true,
+ cancellable: true,
+ });
+ jest.spyOn(event, 'preventDefault');
+ node.dispatchEvent(event);
+ expect(event.preventDefault).not.toHaveBeenCalled();
+ });
+
+ it('captures click with glModalAction', () => {
+ createComponent();
+ node.dataset.glModalAction = 'action1';
+ const event = new window.MouseEvent('click', {
+ bubbles: true,
+ cancellable: true,
+ });
+ jest.spyOn(event, 'preventDefault');
+ node.dispatchEvent(event);
+
+ expect(event.preventDefault).toHaveBeenCalled();
+ return wrapper.vm.$nextTick().then(() => {
+ const modal = wrapper.find({ ref: 'modal' });
+ expect(modal.exists()).toBeTruthy();
+ expect(modal.vm.showWasCalled).toBeTruthy();
+ });
+ });
+ });
+});
diff --git a/spec/frontend/pages/admin/users/components/user_operation_confirmation_modal_spec.js b/spec/frontend/pages/admin/users/components/user_operation_confirmation_modal_spec.js
new file mode 100644
index 00000000000..0ecdae2618c
--- /dev/null
+++ b/spec/frontend/pages/admin/users/components/user_operation_confirmation_modal_spec.js
@@ -0,0 +1,47 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlModal } from '@gitlab/ui';
+import UserOperationConfirmationModal from '~/pages/admin/users/components/user_operation_confirmation_modal.vue';
+
+describe('User Operation confirmation modal', () => {
+ let wrapper;
+
+ const createComponent = (props = {}) => {
+ wrapper = shallowMount(UserOperationConfirmationModal, {
+ propsData: {
+ title: 'title',
+ content: 'content',
+ action: 'action',
+ url: '/url',
+ username: 'username',
+ csrfToken: 'csrf',
+ method: 'method',
+ ...props,
+ },
+ sync: false,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('renders modal with form included', () => {
+ createComponent();
+ expect(wrapper.element).toMatchSnapshot();
+ });
+
+ it('closing modal with ok button triggers form submit', () => {
+ createComponent();
+ const form = wrapper.find('form');
+ jest.spyOn(form.element, 'submit').mockReturnValue();
+ wrapper.find(GlModal).vm.$emit('ok');
+ return wrapper.vm.$nextTick().then(() => {
+ expect(form.element.submit).toHaveBeenCalled();
+ expect(form.element.action).toContain(wrapper.props('url'));
+ expect(new FormData(form.element).get('authenticity_token')).toEqual(
+ wrapper.props('csrfToken'),
+ );
+ });
+ });
+});