Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@
</KModal>
<UserPrivilegeModal
v-model="addAdminPrivilegeDialog"
header="Add admin privileges"
title="Add admin privileges"
:text="`Are you sure you want to add admin privileges to user '${user.name}'?`"
confirmText="Add privileges"
:confirmAction="addAdminHandler"
/>
<UserPrivilegeModal
v-model="removeAdminPrivilegeDialog"
header="Remove admin privileges"
title="Remove admin privileges"
:text="`Are you sure you want to remove admin privileges from user '${user.name}'?`"
confirmText="Remove privileges"
:confirmAction="removeAdminHandler"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,63 +1,53 @@
<template>

<MessageDialog
v-model="dialog"
:header="header"
:text="text"
<KModal
v-if="dialog"
:title="title"
:submitText="confirmText"
cancelText="Cancel"
@submit="submit"
@cancel="close"
>
<VForm
ref="form"
lazy-validation
@submit.prevent="confirm"
>
<p>Enter your email address to continue</p>
<VTextField
v-model="emailConfirm"
box
maxlength="100"
counter
required
:rules="emailRules"
label="Email address"
@input="resetValidation"
/>
</VForm>
<template #buttons>
<VBtn
flat
data-test="cancel"
@click="close"
>
Cancel
</VBtn>
<VBtn
color="primary"
@click="confirm"
>
{{ confirmText }}
</VBtn>
</template>
</MessageDialog>
<p>{{ text }}</p>

<p>Enter your email address to continue</p>

<KTextbox
v-model="emailConfirm"
:maxlength="100"
label="Email address"
:invalid="errors.emailConfirm"
invalidText="Email does not match your account email"
:showInvalidText="true"
/>
</KModal>

</template>


<script>

import { mapState } from 'vuex';
import MessageDialog from 'shared/views/MessageDialog';
import { generateFormMixin } from 'shared/mixins';

const formMixin = generateFormMixin({
emailConfirm: {
required: true,
validator: (value, vm) => {
return value === vm.currentEmail;
},
},
});

export default {
name: 'UserPrivilegeModal',
components: {
MessageDialog,
},
mixins: [formMixin],
props: {
value: {
type: Boolean,
default: false,
},
header: {
title: {
type: String,
required: true,
},
Expand All @@ -74,13 +64,9 @@
required: true,
},
},
data() {
return {
emailConfirm: '',
};
},
computed: {
...mapState({
// eslint-disable-next-line kolibri/vue-no-unused-vuex-properties, vue/no-unused-properties
currentEmail: state => state.session.currentUser.email,
}),
dialog: {
Expand All @@ -91,28 +77,26 @@
this.$emit('input', value);
},
},
emailRules() {
return [
v => Boolean(v) || 'Field is required',
v => v === this.currentEmail || 'Email does not match your account email',
];
},
watch: {
value(val) {
if (val) {
this.reset();
}
},
},
methods: {
close() {
this.emailConfirm = '';
this.resetValidation();
this.dialog = false;
this.$emit('input', false);
},
resetValidation() {
this.$refs.form.resetValidation();
},
confirm() {
if (this.$refs.form.validate()) {
return this.confirmAction();
} else {
return Promise.resolve();
}

// This is called from formMixin
// eslint-disable-next-line kolibri/vue-no-unused-methods, vue/no-unused-properties
onSubmit() {
return Promise.resolve(this.confirmAction()).then(result => {
this.dialog = false;
return result;
});
},
},
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,70 +1,174 @@
import { mount } from '@vue/test-utils';
import { render, screen } from '@testing-library/vue';
import userEvent from '@testing-library/user-event';
import { createLocalVue } from '@vue/test-utils';
import Vuex, { Store } from 'vuex';
import VueRouter from 'vue-router';
import UserPrivilegeModal from '../UserPrivilegeModal';

const userId = 'test-user-id';
const currentEmail = 'mytest@email.com';
const localVue = createLocalVue();
localVue.use(Vuex);
localVue.use(VueRouter);

function makeWrapper(props = {}) {
return mount(UserPrivilegeModal, {
propsData: {
userId,
value: true,
...props,
},
computed: {
user() {
return {
id: userId,
};
},
currentEmail() {
return currentEmail;
const currentEmail = 'myadminaccount@email.com';

const createMockStore = () => {
return new Store({
state: {
session: {
currentUser: {
email: currentEmail,
},
},
},
});
}
};

const renderComponent = (props = {}) => {
const confirmAction = jest.fn(() => Promise.resolve());
const store = createMockStore();

const defaultProps = {
value: true,
title: 'Add admin privileges',
text: 'Are you sure you want to add admin privileges to user ...?',
confirmText: 'Add privileges',
confirmAction,
...props,
};

return {
...render(UserPrivilegeModal, {
localVue,
store,
routes: new VueRouter(),
props: defaultProps,
}),
confirmAction,
};
};

describe('userPrivilegeModal', () => {
let wrapper;
let confirmAction;
it('should render modal with correct title, text, and confirm button label', () => {
renderComponent();

beforeEach(() => {
confirmAction = jest.fn();
wrapper = makeWrapper({ confirmAction });
expect(screen.getByText('Add admin privileges')).toBeInTheDocument();
expect(
screen.getByText('Are you sure you want to add admin privileges to user ...?'),
).toBeInTheDocument();
expect(screen.getByText('Add privileges')).toBeInTheDocument();
});

it('clicking cancel should reset the values', async () => {
wrapper = makeWrapper({ emailConfirm: 'testing' });
wrapper.findComponent('[data-test="cancel"]').trigger('click');
await wrapper.vm.$nextTick();
expect(wrapper.vm.emailConfirm).toBe('');
});
describe('clicking cancel', () => {
let user;
let confirmAction;
let emitted;

it('submitting form should call confirm', async () => {
const confirm = jest.spyOn(wrapper.vm, 'confirm');
confirm.mockImplementation(() => {});
wrapper.findComponent({ ref: 'form' }).trigger('submit');
await wrapper.vm.$nextTick();
expect(confirm).toHaveBeenCalled();
beforeEach(async () => {
user = userEvent.setup();
const result = renderComponent();
confirmAction = result.confirmAction;
emitted = result.emitted;

const cancelButton = screen.getByRole('button', { name: /cancel/i });
await user.click(cancelButton);
});

it('should not call confirmAction', () => {
expect(confirmAction).not.toHaveBeenCalled();
});

it('should emit input event with false', () => {
expect(emitted().input[0]).toEqual([false]);
});
});

it('confirm should not call confirmAction if emailConfirm is blank', async () => {
wrapper.vm.confirm();
await wrapper.vm.$nextTick();
expect(confirmAction).not.toHaveBeenCalled();
describe('submitting the form with empty e-mail', () => {
let user;
let confirmAction;
let emitted;

beforeEach(async () => {
user = userEvent.setup();
const result = renderComponent();
confirmAction = result.confirmAction;
emitted = result.emitted;

const submitButton = screen.getByRole('button', { name: /add privileges/i });
await user.click(submitButton);
});

it('should show validation error', () => {
expect(screen.getByText('Email does not match your account email')).toBeInTheDocument();
});

it('should not call confirmAction', () => {
expect(confirmAction).not.toHaveBeenCalled();
});

it('should not emit input event', () => {
expect(emitted().input).toBeUndefined();
});
});

it('confirm should not call confirmAction if emailConfirm is not correct', async () => {
await wrapper.setData({ emailConfirm: 'notmytest@email.com' });
wrapper.vm.confirm();
await wrapper.vm.$nextTick();
expect(confirmAction).not.toHaveBeenCalled();
describe('submitting the form with an e-mail different from the current e-mail', () => {
let user;
let confirmAction;
let emitted;

beforeEach(async () => {
user = userEvent.setup();
const result = renderComponent();
confirmAction = result.confirmAction;
emitted = result.emitted;

const emailInput = screen.getByLabelText(/email address/i);
await user.type(emailInput, 'differentemail@email.com');

const submitButton = screen.getByRole('button', { name: /add privileges/i });
await user.click(submitButton);
});

it('should show validation error', () => {
expect(screen.getByText('Email does not match your account email')).toBeInTheDocument();
});

it('should not call confirmAction', () => {
expect(confirmAction).not.toHaveBeenCalled();
});

it('should not emit input event', () => {
expect(emitted().input).toBeUndefined();
});
});

it('confirm should call confirmAction if form is valid', async () => {
await wrapper.setData({ emailConfirm: currentEmail });
wrapper.vm.confirm();
await wrapper.vm.$nextTick();
expect(confirmAction).toHaveBeenCalled();
describe('submitting the form with the current e-mail', () => {
let user;
let confirmAction;
let emitted;

beforeEach(async () => {
user = userEvent.setup();
const result = renderComponent();
confirmAction = result.confirmAction;
emitted = result.emitted;

const emailInput = screen.getByLabelText(/email address/i);
await user.type(emailInput, currentEmail);

const submitButton = screen.getByRole('button', { name: /add privileges/i });
await user.click(submitButton);
});

it('should not show validation error', () => {
expect(screen.queryByText('Email does not match your account email')).not.toBeInTheDocument();
});

it('should call confirmAction', () => {
expect(confirmAction).toHaveBeenCalled();
});

it('should emit input event with false', () => {
expect(emitted().input[0]).toEqual([false]);
});
});
});