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>2021-07-14 21:08:31 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-07-14 21:08:31 +0300
commit06ac12d53c3f0b7cee2755a1254bf1af05d55044 (patch)
tree95d0be0bd751a22d6135f496c425c44d774fbe54 /spec/frontend/admin
parentb689f371350fbf1b71f266764ee018befc9b91f7 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/admin')
-rw-r--r--spec/frontend/admin/users/components/modals/__snapshots__/delete_user_modal_spec.js.snap80
-rw-r--r--spec/frontend/admin/users/components/modals/delete_user_modal_spec.js167
-rw-r--r--spec/frontend/admin/users/components/modals/stubs/modal_stub.js23
-rw-r--r--spec/frontend/admin/users/components/modals/user_modal_manager_spec.js126
4 files changed, 396 insertions, 0 deletions
diff --git a/spec/frontend/admin/users/components/modals/__snapshots__/delete_user_modal_spec.js.snap b/spec/frontend/admin/users/components/modals/__snapshots__/delete_user_modal_spec.js.snap
new file mode 100644
index 00000000000..5e367891337
--- /dev/null
+++ b/spec/frontend/admin/users/components/modals/__snapshots__/delete_user_modal_spec.js.snap
@@ -0,0 +1,80 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`User Operation confirmation modal renders modal with form included 1`] = `
+<div>
+ <p>
+ <gl-sprintf-stub
+ message="content"
+ />
+ </p>
+
+ <oncall-schedules-list-stub
+ schedules="schedule1,schedule2"
+ username="username"
+ />
+
+ <p>
+ <gl-sprintf-stub
+ message="To confirm, type %{username}"
+ />
+ </p>
+
+ <form
+ action="delete-url"
+ method="post"
+ >
+ <input
+ name="_method"
+ type="hidden"
+ value="delete"
+ />
+
+ <input
+ name="authenticity_token"
+ type="hidden"
+ value="csrf"
+ />
+
+ <gl-form-input-stub
+ autocomplete="off"
+ autofocus=""
+ name="username"
+ type="text"
+ value=""
+ />
+ </form>
+ <gl-button-stub
+ buttontextclasses=""
+ category="primary"
+ icon=""
+ size="medium"
+ variant="default"
+ >
+ Cancel
+ </gl-button-stub>
+
+ <gl-button-stub
+ buttontextclasses=""
+ category="secondary"
+ disabled="true"
+ icon=""
+ size="medium"
+ variant="danger"
+ >
+
+ secondaryAction
+
+ </gl-button-stub>
+
+ <gl-button-stub
+ buttontextclasses=""
+ category="primary"
+ disabled="true"
+ icon=""
+ size="medium"
+ variant="danger"
+ >
+ action
+ </gl-button-stub>
+</div>
+`;
diff --git a/spec/frontend/admin/users/components/modals/delete_user_modal_spec.js b/spec/frontend/admin/users/components/modals/delete_user_modal_spec.js
new file mode 100644
index 00000000000..fee74764645
--- /dev/null
+++ b/spec/frontend/admin/users/components/modals/delete_user_modal_spec.js
@@ -0,0 +1,167 @@
+import { GlButton, GlFormInput } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import DeleteUserModal from '~/admin/users/components/modals/delete_user_modal.vue';
+import OncallSchedulesList from '~/vue_shared/components/oncall_schedules_list.vue';
+import ModalStub from './stubs/modal_stub';
+
+const TEST_DELETE_USER_URL = 'delete-url';
+const TEST_BLOCK_USER_URL = 'block-url';
+const TEST_CSRF = 'csrf';
+
+describe('User Operation confirmation modal', () => {
+ let wrapper;
+ let formSubmitSpy;
+
+ const findButton = (variant, category) =>
+ wrapper
+ .findAll(GlButton)
+ .filter((w) => w.attributes('variant') === variant && w.attributes('category') === category)
+ .at(0);
+ const findForm = () => wrapper.find('form');
+ const findUsernameInput = () => wrapper.findComponent(GlFormInput);
+ const findPrimaryButton = () => findButton('danger', 'primary');
+ const findSecondaryButton = () => findButton('danger', 'secondary');
+ const findAuthenticityToken = () => new FormData(findForm().element).get('authenticity_token');
+ const getUsername = () => findUsernameInput().attributes('value');
+ const getMethodParam = () => new FormData(findForm().element).get('_method');
+ const getFormAction = () => findForm().attributes('action');
+ const findOnCallSchedulesList = () => wrapper.findComponent(OncallSchedulesList);
+
+ const setUsername = (username) => {
+ findUsernameInput().vm.$emit('input', username);
+ };
+
+ const username = 'username';
+ const badUsername = 'bad_username';
+ const oncallSchedules = '["schedule1", "schedule2"]';
+
+ const createComponent = (props = {}) => {
+ wrapper = shallowMount(DeleteUserModal, {
+ propsData: {
+ username,
+ title: 'title',
+ content: 'content',
+ action: 'action',
+ secondaryAction: 'secondaryAction',
+ deleteUserUrl: TEST_DELETE_USER_URL,
+ blockUserUrl: TEST_BLOCK_USER_URL,
+ csrfToken: TEST_CSRF,
+ oncallSchedules,
+ ...props,
+ },
+ stubs: {
+ GlModal: ModalStub,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ formSubmitSpy = jest.spyOn(HTMLFormElement.prototype, 'submit').mockImplementation();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('renders modal with form included', () => {
+ createComponent();
+ expect(wrapper.element).toMatchSnapshot();
+ });
+
+ describe('on created', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('has disabled buttons', () => {
+ expect(findPrimaryButton().attributes('disabled')).toBeTruthy();
+ expect(findSecondaryButton().attributes('disabled')).toBeTruthy();
+ });
+ });
+
+ describe('with incorrect username', () => {
+ beforeEach(() => {
+ createComponent();
+ setUsername(badUsername);
+
+ return wrapper.vm.$nextTick();
+ });
+
+ it('shows incorrect username', () => {
+ expect(getUsername()).toEqual(badUsername);
+ });
+
+ it('has disabled buttons', () => {
+ expect(findPrimaryButton().attributes('disabled')).toBeTruthy();
+ expect(findSecondaryButton().attributes('disabled')).toBeTruthy();
+ });
+ });
+
+ describe('with correct username', () => {
+ beforeEach(() => {
+ createComponent();
+ setUsername(username);
+
+ return wrapper.vm.$nextTick();
+ });
+
+ it('shows correct username', () => {
+ expect(getUsername()).toEqual(username);
+ });
+
+ it('has enabled buttons', () => {
+ expect(findPrimaryButton().attributes('disabled')).toBeFalsy();
+ expect(findSecondaryButton().attributes('disabled')).toBeFalsy();
+ });
+
+ describe('when primary action is submitted', () => {
+ beforeEach(() => {
+ findPrimaryButton().vm.$emit('click');
+
+ return wrapper.vm.$nextTick();
+ });
+
+ it('clears the input', () => {
+ expect(getUsername()).toEqual('');
+ });
+
+ it('has correct form attributes and calls submit', () => {
+ expect(getFormAction()).toBe(TEST_DELETE_USER_URL);
+ expect(getMethodParam()).toBe('delete');
+ expect(findAuthenticityToken()).toBe(TEST_CSRF);
+ expect(formSubmitSpy).toHaveBeenCalled();
+ });
+ });
+
+ describe('when secondary action is submitted', () => {
+ beforeEach(() => {
+ findSecondaryButton().vm.$emit('click');
+
+ return wrapper.vm.$nextTick();
+ });
+
+ it('has correct form attributes and calls submit', () => {
+ expect(getFormAction()).toBe(TEST_BLOCK_USER_URL);
+ expect(getMethodParam()).toBe('put');
+ expect(findAuthenticityToken()).toBe(TEST_CSRF);
+ expect(formSubmitSpy).toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('Related oncall-schedules list', () => {
+ it('does NOT render the list when user has no related schedules', () => {
+ createComponent({ oncallSchedules: '[]' });
+ expect(findOnCallSchedulesList().exists()).toBe(false);
+ });
+
+ it('renders the list when user has related schedules', () => {
+ createComponent();
+
+ const schedules = findOnCallSchedulesList();
+ expect(schedules.exists()).toBe(true);
+ expect(schedules.props('schedules')).toEqual(JSON.parse(oncallSchedules));
+ });
+ });
+});
diff --git a/spec/frontend/admin/users/components/modals/stubs/modal_stub.js b/spec/frontend/admin/users/components/modals/stubs/modal_stub.js
new file mode 100644
index 00000000000..4dc55e909a0
--- /dev/null
+++ b/spec/frontend/admin/users/components/modals/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/admin/users/components/modals/user_modal_manager_spec.js b/spec/frontend/admin/users/components/modals/user_modal_manager_spec.js
new file mode 100644
index 00000000000..65ce242662b
--- /dev/null
+++ b/spec/frontend/admin/users/components/modals/user_modal_manager_spec.js
@@ -0,0 +1,126 @@
+import { mount } from '@vue/test-utils';
+import UserModalManager from '~/admin/users/components/modals/user_modal_manager.vue';
+import ModalStub from './stubs/modal_stub';
+
+describe('Users admin page Modal Manager', () => {
+ let wrapper;
+
+ const modalConfiguration = {
+ action1: {
+ title: 'action1',
+ content: 'Action Modal 1',
+ },
+ action2: {
+ title: 'action2',
+ content: 'Action Modal 2',
+ },
+ };
+
+ const findModal = () => wrapper.find({ ref: 'modal' });
+
+ const createComponent = (props = {}) => {
+ wrapper = mount(UserModalManager, {
+ propsData: {
+ selector: '.js-delete-user-modal-button',
+ modalConfiguration,
+ csrfToken: 'dummyCSRF',
+ ...props,
+ },
+ stubs: {
+ DeleteUserModal: ModalStub,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('render behavior', () => {
+ it('does not renders modal when initialized', () => {
+ createComponent();
+ expect(findModal().exists()).toBeFalsy();
+ });
+
+ 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 = findModal();
+ expect(modal.exists()).toBeTruthy();
+ expect(modal.vm.$attrs.csrfToken).toEqual('dummyCSRF');
+ expect(modal.vm.$attrs.extraProp).toEqual('extraPropValue');
+ expect(modal.vm.showWasCalled).toBeTruthy();
+ });
+ });
+ });
+
+ describe('click handling', () => {
+ let button;
+ let button2;
+
+ const createButtons = () => {
+ button = document.createElement('button');
+ button2 = document.createElement('button');
+ button.setAttribute('class', 'js-delete-user-modal-button');
+ button.setAttribute('data-username', 'foo');
+ button.setAttribute('data-gl-modal-action', 'action1');
+ button.setAttribute('data-block-user-url', '/block');
+ button.setAttribute('data-delete-user-url', '/delete');
+ document.body.appendChild(button);
+ document.body.appendChild(button2);
+ };
+ const removeButtons = () => {
+ button.remove();
+ button = null;
+ button2.remove();
+ button2 = null;
+ };
+
+ beforeEach(() => {
+ createButtons();
+ createComponent();
+ });
+
+ afterEach(() => {
+ removeButtons();
+ });
+
+ it('renders the modal when the button is clicked', async () => {
+ button.click();
+
+ await wrapper.vm.$nextTick();
+
+ expect(findModal().exists()).toBe(true);
+ });
+
+ it('does not render the modal when a misconfigured button is clicked', async () => {
+ button.removeAttribute('data-gl-modal-action');
+ button.click();
+
+ await wrapper.vm.$nextTick();
+
+ expect(findModal().exists()).toBe(false);
+ });
+
+ it('does not render the modal when a button without the selector class is clicked', async () => {
+ button2.click();
+
+ await wrapper.vm.$nextTick();
+
+ expect(findModal().exists()).toBe(false);
+ });
+ });
+});