Fix vue test stacktraces (#12612)

* 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

* fix(vuejs-unit-tests): Fewer stacktraces in avatar.spec.js

* No more stacktraces in avatar.spec.js

* Register dummy directive in chatCard.spec.js

* Resolve stacktraces in column.spec.js

* Resolve stacktrace in notifications.spec.js

* Resolve warnings in user.spec.js

* Resolve asynchronous stacktrace from home.spec.js

* Remove unnecessary mount call.

* Clear up some let clutter in column.spec.js
This commit is contained in:
Bart Enkelaar
2020-10-01 19:08:34 +02:00
committed by GitHub
parent 73c5764d63
commit 32ac00b417
7 changed files with 191 additions and 107 deletions

View File

@@ -227,7 +227,7 @@ export default {
return this.member.preferences.costume ? 'costume' : 'equipped';
},
specialMountClass () {
if (!this.avatarOnly && this.member.items.currentMount && this.member.items.currentMount.indexOf('Kangaroo') !== -1) {
if (!this.avatarOnly && this.member.items.currentMount && this.member.items.currentMount.includes('Kangaroo')) {
return 'offset-kangaroo';
}

View File

@@ -1,4 +1,6 @@
import Vue from 'vue';
import merge from 'lodash/merge';
import Avatar from '@/components/avatar';
import generateStore from '@/store';
@@ -6,14 +8,10 @@ context('avatar.vue', () => {
let Constructr;
let vm;
beforeEach(() => {
Constructr = Vue.extend(Avatar);
vm = new Constructr({
propsData: {
member: {
const baseMember = {
stats: {
buffs: {},
class: 'warrior',
},
preferences: {
hair: {},
@@ -23,9 +21,14 @@ context('avatar.vue', () => {
equipped: {},
},
},
},
},
}).$mount();
};
beforeEach(() => {
Constructr = Vue.extend(Avatar);
vm = new Constructr({
propsData: { member: baseMember },
});
vm.$store = generateStore();
});
@@ -36,11 +39,11 @@ context('avatar.vue', () => {
describe('hasClass', () => {
beforeEach(() => {
vm.member = {
vm.member = merge({
stats: { lvl: 17 },
preferences: { disableClasses: true },
flags: { classSelected: false },
};
}, baseMember);
});
it('accurately reports class status', () => {
@@ -54,14 +57,6 @@ context('avatar.vue', () => {
});
describe('isBuffed', () => {
beforeEach(() => {
vm.member = {
stats: {
buffs: {},
},
};
});
it('accurately reports if buffed', () => {
expect(vm.isBuffed).to.equal(undefined);
@@ -72,29 +67,23 @@ context('avatar.vue', () => {
});
describe('paddingTop', () => {
beforeEach(() => {
vm.member = {
items: {},
};
});
xit('defaults to 27px', () => {
vm.avatarOnly = true;
expect(vm.paddingTop).to.equal('27px');
});
it('is 24px if user has a pet', () => {
vm.member.items = {
vm.member.items = merge({
currentPet: { name: 'Foo' },
};
}, baseMember.items);
expect(vm.paddingTop).to.equal('24px');
});
it('is 0px if user has a mount', () => {
vm.member.items = {
currentMount: { name: 'Bar' },
};
vm.member.items = merge({
currentMount: 'Bar',
}, baseMember.items);
expect(vm.paddingTop).to.equal('0px');
});
@@ -106,28 +95,25 @@ context('avatar.vue', () => {
});
describe('costumeClass', () => {
beforeEach(() => {
vm.member = {
preferences: {},
};
});
it('returns if showing equipped gear', () => {
expect(vm.costumeClass).to.equal('equipped');
});
it('returns if wearing a costume', () => {
vm.member.preferences = { costume: true };
vm.member.preferences = { costume: true, hair: {} };
vm.member.items.gear.costume = {};
expect(vm.costumeClass).to.equal('costume');
});
});
describe('visualBuffs', () => {
it('returns an array of buffs', () => {
vm.member = {
vm.member = merge({
stats: {
class: 'warrior',
},
};
}, baseMember);
expect(vm.visualBuffs).to.include({ snowball: 'avatar_snowball_warrior' });
expect(vm.visualBuffs).to.include({ spookySparkles: 'ghost' });
@@ -138,7 +124,10 @@ context('avatar.vue', () => {
describe('backgroundClass', () => {
beforeEach(() => {
vm.member.preferences = { background: 'pony' };
vm.member.preferences = {
hair: {},
background: 'pony',
};
});
it('shows the background', () => {
@@ -161,17 +150,11 @@ context('avatar.vue', () => {
describe('specialMountClass', () => {
it('checks if riding a Kangaroo', () => {
vm.member = {
stats: {
class: 'None',
},
items: {},
};
expect(vm.specialMountClass).to.equal(null);
vm.member.items = {
currentMount: ['Kangaroo'],
currentMount: 'Kangaroo',
gear: { equipped: {} },
};
expect(vm.specialMountClass).to.equal('offset-kangaroo');
@@ -180,24 +163,22 @@ context('avatar.vue', () => {
describe('skinClass', () => {
it('returns current skin color', () => {
vm.member = {
stats: {},
vm.member = merge({
preferences: {
skin: 'blue',
},
};
}, baseMember);
expect(vm.skinClass).to.equal('skin_blue');
});
it('returns if sleep or not', () => {
vm.member = {
stats: {},
vm.member = merge({
preferences: {
skin: 'blue',
sleep: false,
},
};
}, baseMember);
expect(vm.skinClass).to.equal('skin_blue');
@@ -210,14 +191,14 @@ context('avatar.vue', () => {
context('methods', () => {
describe('getGearClass', () => {
beforeEach(() => {
vm.member = {
vm.member = merge({
items: {
gear: {
equipped: { Hat: 'Fancy Tophat' },
},
},
preferences: { costume: false },
};
}, baseMember);
});
it('returns undefined if no match', () => {
@@ -242,7 +223,7 @@ context('avatar.vue', () => {
});
beforeEach(() => {
vm.member = {
vm.member = merge({
items: {
gear: {
equipped: {
@@ -254,13 +235,13 @@ context('avatar.vue', () => {
},
},
preferences: { costume: false },
};
}, baseMember);
});
});
describe('show avatar', () => {
beforeEach(() => {
vm.member = {
vm.member = merge({
stats: {
buffs: {
snowball: false,
@@ -269,7 +250,7 @@ context('avatar.vue', () => {
shinySeed: false,
},
},
};
}, baseMember);
});
it('does if not showing visual buffs', () => {
expect(vm.showAvatar()).to.equal(true);

View File

@@ -1,3 +1,4 @@
import Vue from 'vue';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import ChatCard from '@/components/chat/chatCard.vue';
@@ -5,6 +6,7 @@ import Store from '@/libs/store';
const localVue = createLocalVue();
localVue.use(Store);
localVue.use(Vue.directive('b-tooltip', {}));
describe('ChatCard', () => {
function createMessage (text) {

View File

@@ -0,0 +1,109 @@
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,
'auth:verifyUsername': () => Promise.resolve({}),
},
});
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);
});
});
});

View File

@@ -20,7 +20,7 @@ describe('Notifications', () => {
lvl: 0,
},
flags: {},
preferences: {},
preferences: { suppressModals: {} },
party: {
quest: {
},
@@ -55,6 +55,7 @@ describe('Notifications', () => {
expect(wrapper.vm.userHasClass).to.be.true;
});
describe('user exp notifcation', () => {
it('notifies when user gets more exp', () => {
const expSpy = sinon.spy(wrapper.vm, 'exp');

View File

@@ -1,4 +1,4 @@
import { mount, createLocalVue } from '@vue/test-utils';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import TaskColumn from '@/components/tasks/column.vue';
import Store from '@/libs/store';
@@ -7,10 +7,6 @@ localVue.use(Store);
describe('Task Column', () => {
let wrapper;
let store; let
getters;
let habits; let taskListOverride; let
tasks;
function makeWrapper (additionalSetup = {}) {
const type = 'habit';
@@ -19,9 +15,12 @@ describe('Task Column', () => {
};
const stubs = ['b-modal']; // <b-modal> is a custom component and not tested here
return mount(TaskColumn, {
propsData: {
type,
return shallowMount(TaskColumn, {
propsData: { type },
store: {
getters: {
'tasks:getFilteredTaskList': () => [],
},
},
mocks,
stubs,
@@ -56,6 +55,9 @@ describe('Task Column', () => {
});
describe('Computed Properties', () => {
let taskListOverride;
let habits;
beforeEach(() => {
habits = [
{ id: 1 },
@@ -67,14 +69,14 @@ describe('Task Column', () => {
{ id: 4 },
];
getters = {
const getters = {
// (...) => { ... } will return a value
// (...) => (...) => { ... } will return a function
// Task Column expects a function
'tasks:getFilteredTaskList': () => () => habits,
};
store = new Store({ getters });
const store = new Store({ getters });
wrapper = makeWrapper({ store });
});
@@ -103,6 +105,8 @@ describe('Task Column', () => {
});
describe('Methods', () => {
let tasks;
describe('Filter By Tags', () => {
beforeEach(() => {
tasks = [

View File

@@ -6,6 +6,19 @@ const localVue = createLocalVue();
localVue.use(Store);
describe('Tasks User', () => {
function createWrapper (challengeTag) {
const store = new Store({
state: { user: { data: { tags: [challengeTag] } } },
getters: {},
});
return shallowMount(User, {
store,
localVue,
mocks: { $t: s => s },
stubs: ['b-tooltip'],
});
}
describe('Computed Properties', () => {
it('should render a challenge tag under challenge header in tag filter popup when the challenge is active', () => {
const activeChallengeTag = {
@@ -13,20 +26,7 @@ describe('Tasks User', () => {
name: 'Challenge1',
challenge: true,
};
const state = {
user: {
data: {
tags: [activeChallengeTag],
},
},
};
const getters = {};
const store = new Store({ state, getters });
const wrapper = shallowMount(User, {
store,
localVue,
});
const wrapper = createWrapper(activeChallengeTag);
const computedTagsByType = wrapper.vm.tagsByType;
expect(computedTagsByType.challenges.tags.length).to.equal(1);
@@ -40,20 +40,7 @@ describe('Tasks User', () => {
name: 'Challenge1',
challenge: false,
};
const state = {
user: {
data: {
tags: [inactiveChallengeTag],
},
},
};
const getters = {};
const store = new Store({ state, getters });
const wrapper = shallowMount(User, {
store,
localVue,
});
const wrapper = createWrapper(inactiveChallengeTag);
const computedTagsByType = wrapper.vm.tagsByType;
expect(computedTagsByType.challenges.tags.length).to.equal(0);