mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-18 07:07:35 +01:00
Vue component unit test isolation (#12154)
* Issue 10786 - Add unit test for Home component * Issue 10786 - Improve test setup and test invite parameter variations * Issue 10786 - Improve Vue.js test isolation by adding async keyword to dispatch function * Issue 10786 - Missing action does not need to be awaited * Use localVue for groupsUtilities test and revert partial zone fix
This commit is contained in:
@@ -28,8 +28,8 @@
|
||||
<div class="inner-content">
|
||||
<questDialogContent :item="item" />
|
||||
<div
|
||||
class="purchase-amount"
|
||||
v-if="!item.locked"
|
||||
class="purchase-amount"
|
||||
>
|
||||
<div class="how-many-to-buy">
|
||||
<strong>{{ $t('howManyToBuy') }}</strong>
|
||||
|
||||
@@ -913,6 +913,7 @@ export default {
|
||||
if (username.length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$store.dispatch('auth:verifyUsername', {
|
||||
username: this.username,
|
||||
}).then(res => {
|
||||
@@ -942,15 +943,7 @@ export default {
|
||||
groupInvite,
|
||||
});
|
||||
|
||||
let redirectTo;
|
||||
|
||||
if (this.$route.query.redirectTo) {
|
||||
redirectTo = this.$route.query.redirectTo;
|
||||
} else {
|
||||
redirectTo = '/';
|
||||
}
|
||||
|
||||
window.location.href = redirectTo;
|
||||
window.location.href = this.$route.query.redirectTo || '/';
|
||||
},
|
||||
playButtonClick () {
|
||||
Analytics.track({
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
>
|
||||
<div
|
||||
class="svg-icon color"
|
||||
v-html="icons.pin">
|
||||
v-html="icons.pin"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -60,7 +60,7 @@ export function mapActions (actions) {
|
||||
const res = {};
|
||||
|
||||
normalizeMap(actions).forEach(({ key, val }) => {
|
||||
res[key] = function mappedAction (...args) {
|
||||
res[key] = async function mappedAction (...args) {
|
||||
return this.$store.dispatch.apply(this.$store, [val].concat(args)); // eslint-disable-line prefer-spread, max-len
|
||||
};
|
||||
});
|
||||
|
||||
@@ -41,7 +41,7 @@ export default class Store {
|
||||
|
||||
// Actions should be called using store.dispatch(ACTION_NAME, ...ARGS)
|
||||
// They get passed the store instance and any additional argument passed to dispatch()
|
||||
dispatch (type, ...args) {
|
||||
async dispatch (type, ...args) {
|
||||
const action = this._actions[type];
|
||||
|
||||
if (!action) throw new Error(`Action "${type}" not found.`);
|
||||
|
||||
108
website/client/tests/unit/components/home.spec.js
Normal file
108
website/client/tests/unit/components/home.spec.js
Normal file
@@ -0,0 +1,108 @@
|
||||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
|
||||
import Home from '@/components/static/home.vue';
|
||||
import Store from '@/libs/store';
|
||||
import * as Analytics from '@/libs/analytics';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
localVue.use(Store);
|
||||
|
||||
describe('Home', () => {
|
||||
let registerStub;
|
||||
let socialAuthStub;
|
||||
let store;
|
||||
let wrapper;
|
||||
|
||||
function mountWrapper (query) {
|
||||
return shallowMount(Home, {
|
||||
store,
|
||||
localVue,
|
||||
mocks: {
|
||||
$t: string => string,
|
||||
$route: { query: query || {} },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function fillOutUserForm (username, email, password) {
|
||||
await wrapper.find('#usernameInput').setValue(username);
|
||||
await wrapper.find('input[type=email]').setValue(email);
|
||||
await wrapper.findAll('input[type=password]').setValue(password);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
registerStub = sinon.stub();
|
||||
socialAuthStub = sinon.stub();
|
||||
store = new Store({
|
||||
state: {},
|
||||
getters: {},
|
||||
actions: {
|
||||
'auth:register': registerStub,
|
||||
'auth:socialAuth': socialAuthStub,
|
||||
},
|
||||
});
|
||||
|
||||
sinon.stub(Analytics, 'track');
|
||||
|
||||
wrapper = mountWrapper();
|
||||
});
|
||||
|
||||
afterEach(sinon.restore);
|
||||
|
||||
it('has a visible title', () => {
|
||||
expect(wrapper.find('h1').text()).to.equal('motivateYourself');
|
||||
});
|
||||
|
||||
describe('signup form', () => {
|
||||
it('registers a user from the form', async () => {
|
||||
const username = 'newUser';
|
||||
const email = 'rookie@habitica.com';
|
||||
const password = 'ImmaG3tProductive!';
|
||||
await fillOutUserForm(username, email, password);
|
||||
|
||||
await wrapper.find('form').trigger('submit');
|
||||
|
||||
expect(registerStub.calledOnce).to.be.true;
|
||||
expect(registerStub.getCall(0).args[1]).to.deep.equal({
|
||||
username,
|
||||
email,
|
||||
password,
|
||||
passwordConfirm: password,
|
||||
groupInvite: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('registers a user with group invite if groupInvite in the query', async () => {
|
||||
const groupInvite = 'TheBestGroup';
|
||||
wrapper = mountWrapper({ groupInvite });
|
||||
await fillOutUserForm('invitedUser', 'invited@habitica.com', '1veGotFri3ndsHooray!');
|
||||
|
||||
await wrapper.find('form').trigger('submit');
|
||||
|
||||
expect(registerStub.calledOnce).to.be.true;
|
||||
expect(registerStub.getCall(0).args[1].groupInvite).to.equal(groupInvite);
|
||||
});
|
||||
|
||||
it('registers a user with group invite if p in the query', async () => {
|
||||
const p = 'ThePiGroup';
|
||||
wrapper = mountWrapper({ p });
|
||||
await fillOutUserForm('alsoInvitedUser', 'invited2@habitica.com', '1veGotFri3nds2!');
|
||||
|
||||
await wrapper.find('form').trigger('submit');
|
||||
|
||||
expect(registerStub.calledOnce).to.be.true;
|
||||
expect(registerStub.getCall(0).args[1].groupInvite).to.equal(p);
|
||||
});
|
||||
|
||||
it('registers a user with group invite invite if both p and groupInvite are in the query', async () => {
|
||||
const groupInvite = 'StillTheBestGroup';
|
||||
wrapper = mountWrapper({ p: 'LesserGroup', groupInvite });
|
||||
await fillOutUserForm('doublyInvitedUser', 'invited3@habitica.com', '1veGotSm4rtFri3nds!');
|
||||
|
||||
await wrapper.find('form').trigger('submit');
|
||||
|
||||
expect(registerStub.calledOnce).to.be.true;
|
||||
expect(registerStub.getCall(0).args[1].groupInvite).to.equal(groupInvite);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -76,15 +76,15 @@ describe('Store', () => {
|
||||
});
|
||||
|
||||
describe('actions', () => {
|
||||
it('can dispatch an action', () => {
|
||||
expect(store.dispatch('getName', 1, 2, 3)).to.deep.equal(['test', 1, 2, 3]);
|
||||
it('can dispatch an action', async () => {
|
||||
expect(await store.dispatch('getName', 1, 2, 3)).to.deep.equal(['test', 1, 2, 3]);
|
||||
});
|
||||
|
||||
it('can dispatch a nested action', () => {
|
||||
expect(store.dispatch('nested:getName', 1, 2, 3)).to.deep.equal(['test', 1, 2, 3]);
|
||||
it('can dispatch a nested action', async () => {
|
||||
expect(await store.dispatch('nested:getName', 1, 2, 3)).to.deep.equal(['test', 1, 2, 3]);
|
||||
});
|
||||
|
||||
it('throws an error is the action doesn\'t exists', () => {
|
||||
it('throws an error if the action doesn\'t exists', () => {
|
||||
expect(() => store.dispatched('wrong')).to.throw;
|
||||
});
|
||||
});
|
||||
@@ -140,9 +140,9 @@ describe('Store', () => {
|
||||
data: {
|
||||
title: 'internal',
|
||||
},
|
||||
created () {
|
||||
expect(this.getName('123')).to.deep.equal(['test', '123']);
|
||||
expect(this.getNameRenamed('123')).to.deep.equal(['test', '123']);
|
||||
async created () {
|
||||
expect(await this.getName('123')).to.deep.equal(['test', '123']);
|
||||
expect(await this.getNameRenamed('123')).to.deep.equal(['test', '123']);
|
||||
done();
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import Vue from 'vue';
|
||||
import { createLocalVue } from '@vue/test-utils';
|
||||
import groupsUtilities from '@/mixins/groupsUtilities';
|
||||
import { TAVERN_ID } from '@/../../common/script/constants';
|
||||
import generateStore from '@/store';
|
||||
import Store from '@/libs/store';
|
||||
|
||||
const LocalVue = createLocalVue();
|
||||
LocalVue.use(Store);
|
||||
|
||||
describe('Groups Utilities Mixin', () => {
|
||||
let instance; let
|
||||
user;
|
||||
let instance;
|
||||
let user;
|
||||
|
||||
before(() => {
|
||||
instance = new Vue({
|
||||
instance = new LocalVue({
|
||||
store: generateStore(),
|
||||
mixins: [groupsUtilities],
|
||||
});
|
||||
|
||||
@@ -69,13 +69,13 @@ export function getGroupUrl (group) {
|
||||
export async function sendTxn (mailingInfoArray, emailType, variables, personalVariables) {
|
||||
mailingInfoArray = Array.isArray(mailingInfoArray) ? mailingInfoArray : [mailingInfoArray]; // eslint-disable-line no-param-reassign, max-len
|
||||
|
||||
variables = [ // eslint-disable-line no-param-reassign, max-len
|
||||
variables = [ // eslint-disable-line no-param-reassign
|
||||
{ name: 'BASE_URL', content: BASE_URL },
|
||||
].concat(variables || []);
|
||||
|
||||
// It's important to pass at least a user with its `preferences`
|
||||
// as we need to check if he unsubscribed
|
||||
mailingInfoArray = mailingInfoArray // eslint-disable-line no-param-reassign, max-len
|
||||
mailingInfoArray = mailingInfoArray // eslint-disable-line no-param-reassign
|
||||
.map(mailingInfo => (mailingInfo._id ? getUserInfo(mailingInfo, ['_id', 'email', 'name', 'canSend']) : mailingInfo))
|
||||
// Always send reset-password emails
|
||||
// Don't check canSend for non registered users as already checked before
|
||||
|
||||
@@ -166,9 +166,7 @@ async function inviteByEmail (invite, group, inviter, req, res) {
|
||||
} else {
|
||||
userReturnInfo = invite.email;
|
||||
|
||||
let cancelledPlan = false;
|
||||
if (group.hasActiveGroupPlan() && !group.hasNotCancelled()) cancelledPlan = true;
|
||||
|
||||
const cancelledPlan = group.hasActiveGroupPlan() && !group.hasNotCancelled();
|
||||
const groupQueryString = JSON.stringify({
|
||||
id: group._id,
|
||||
inviter: inviter._id,
|
||||
|
||||
Reference in New Issue
Block a user