mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-18 15:17:25 +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">
|
<div class="inner-content">
|
||||||
<questDialogContent :item="item" />
|
<questDialogContent :item="item" />
|
||||||
<div
|
<div
|
||||||
class="purchase-amount"
|
|
||||||
v-if="!item.locked"
|
v-if="!item.locked"
|
||||||
|
class="purchase-amount"
|
||||||
>
|
>
|
||||||
<div class="how-many-to-buy">
|
<div class="how-many-to-buy">
|
||||||
<strong>{{ $t('howManyToBuy') }}</strong>
|
<strong>{{ $t('howManyToBuy') }}</strong>
|
||||||
|
|||||||
@@ -913,6 +913,7 @@ export default {
|
|||||||
if (username.length < 1) {
|
if (username.length < 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$store.dispatch('auth:verifyUsername', {
|
this.$store.dispatch('auth:verifyUsername', {
|
||||||
username: this.username,
|
username: this.username,
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
@@ -942,15 +943,7 @@ export default {
|
|||||||
groupInvite,
|
groupInvite,
|
||||||
});
|
});
|
||||||
|
|
||||||
let redirectTo;
|
window.location.href = this.$route.query.redirectTo || '/';
|
||||||
|
|
||||||
if (this.$route.query.redirectTo) {
|
|
||||||
redirectTo = this.$route.query.redirectTo;
|
|
||||||
} else {
|
|
||||||
redirectTo = '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
window.location.href = redirectTo;
|
|
||||||
},
|
},
|
||||||
playButtonClick () {
|
playButtonClick () {
|
||||||
Analytics.track({
|
Analytics.track({
|
||||||
@@ -968,7 +961,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
await hello(network).logout();
|
await hello(network).logout();
|
||||||
} catch (e) {} // eslint-disable-line
|
} catch (e) {} // eslint-disable-line
|
||||||
|
|
||||||
const redirectUrl = `${window.location.protocol}//${window.location.host}`;
|
const redirectUrl = `${window.location.protocol}//${window.location.host}`;
|
||||||
const auth = await hello(network).login({
|
const auth = await hello(network).login({
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="svg-icon color"
|
class="svg-icon color"
|
||||||
v-html="icons.pin">
|
v-html="icons.pin"
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export function mapActions (actions) {
|
|||||||
const res = {};
|
const res = {};
|
||||||
|
|
||||||
normalizeMap(actions).forEach(({ key, val }) => {
|
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
|
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)
|
// Actions should be called using store.dispatch(ACTION_NAME, ...ARGS)
|
||||||
// They get passed the store instance and any additional argument passed to dispatch()
|
// 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];
|
const action = this._actions[type];
|
||||||
|
|
||||||
if (!action) throw new Error(`Action "${type}" not found.`);
|
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', () => {
|
describe('actions', () => {
|
||||||
it('can dispatch an action', () => {
|
it('can dispatch an action', async () => {
|
||||||
expect(store.dispatch('getName', 1, 2, 3)).to.deep.equal(['test', 1, 2, 3]);
|
expect(await store.dispatch('getName', 1, 2, 3)).to.deep.equal(['test', 1, 2, 3]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can dispatch a nested action', () => {
|
it('can dispatch a nested action', async () => {
|
||||||
expect(store.dispatch('nested:getName', 1, 2, 3)).to.deep.equal(['test', 1, 2, 3]);
|
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;
|
expect(() => store.dispatched('wrong')).to.throw;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -140,9 +140,9 @@ describe('Store', () => {
|
|||||||
data: {
|
data: {
|
||||||
title: 'internal',
|
title: 'internal',
|
||||||
},
|
},
|
||||||
created () {
|
async created () {
|
||||||
expect(this.getName('123')).to.deep.equal(['test', '123']);
|
expect(await this.getName('123')).to.deep.equal(['test', '123']);
|
||||||
expect(this.getNameRenamed('123')).to.deep.equal(['test', '123']);
|
expect(await this.getNameRenamed('123')).to.deep.equal(['test', '123']);
|
||||||
done();
|
done();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
import Vue from 'vue';
|
import { createLocalVue } from '@vue/test-utils';
|
||||||
import groupsUtilities from '@/mixins/groupsUtilities';
|
import groupsUtilities from '@/mixins/groupsUtilities';
|
||||||
import { TAVERN_ID } from '@/../../common/script/constants';
|
import { TAVERN_ID } from '@/../../common/script/constants';
|
||||||
import generateStore from '@/store';
|
import generateStore from '@/store';
|
||||||
|
import Store from '@/libs/store';
|
||||||
|
|
||||||
|
const LocalVue = createLocalVue();
|
||||||
|
LocalVue.use(Store);
|
||||||
|
|
||||||
describe('Groups Utilities Mixin', () => {
|
describe('Groups Utilities Mixin', () => {
|
||||||
let instance; let
|
let instance;
|
||||||
user;
|
let user;
|
||||||
|
|
||||||
before(() => {
|
before(() => {
|
||||||
instance = new Vue({
|
instance = new LocalVue({
|
||||||
store: generateStore(),
|
store: generateStore(),
|
||||||
mixins: [groupsUtilities],
|
mixins: [groupsUtilities],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -69,13 +69,13 @@ export function getGroupUrl (group) {
|
|||||||
export async function sendTxn (mailingInfoArray, emailType, variables, personalVariables) {
|
export async function sendTxn (mailingInfoArray, emailType, variables, personalVariables) {
|
||||||
mailingInfoArray = Array.isArray(mailingInfoArray) ? mailingInfoArray : [mailingInfoArray]; // eslint-disable-line no-param-reassign, max-len
|
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 },
|
{ name: 'BASE_URL', content: BASE_URL },
|
||||||
].concat(variables || []);
|
].concat(variables || []);
|
||||||
|
|
||||||
// It's important to pass at least a user with its `preferences`
|
// It's important to pass at least a user with its `preferences`
|
||||||
// as we need to check if he unsubscribed
|
// 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))
|
.map(mailingInfo => (mailingInfo._id ? getUserInfo(mailingInfo, ['_id', 'email', 'name', 'canSend']) : mailingInfo))
|
||||||
// Always send reset-password emails
|
// Always send reset-password emails
|
||||||
// Don't check canSend for non registered users as already checked before
|
// 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 {
|
} else {
|
||||||
userReturnInfo = invite.email;
|
userReturnInfo = invite.email;
|
||||||
|
|
||||||
let cancelledPlan = false;
|
const cancelledPlan = group.hasActiveGroupPlan() && !group.hasNotCancelled();
|
||||||
if (group.hasActiveGroupPlan() && !group.hasNotCancelled()) cancelledPlan = true;
|
|
||||||
|
|
||||||
const groupQueryString = JSON.stringify({
|
const groupQueryString = JSON.stringify({
|
||||||
id: group._id,
|
id: group._id,
|
||||||
inviter: inviter._id,
|
inviter: inviter._id,
|
||||||
|
|||||||
Reference in New Issue
Block a user