diff --git a/website/client/src/components/avatar.vue b/website/client/src/components/avatar.vue index 3ca2483d3d..9e70cd3ea4 100644 --- a/website/client/src/components/avatar.vue +++ b/website/client/src/components/avatar.vue @@ -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'; } diff --git a/website/client/tests/unit/components/avatar.spec.js b/website/client/tests/unit/components/avatar.spec.js index c1f49322df..908b452a0c 100644 --- a/website/client/tests/unit/components/avatar.spec.js +++ b/website/client/tests/unit/components/avatar.spec.js @@ -1,4 +1,6 @@ import Vue from 'vue'; +import merge from 'lodash/merge'; + import Avatar from '@/components/avatar'; import generateStore from '@/store'; @@ -6,26 +8,27 @@ context('avatar.vue', () => { let Constructr; let vm; + const baseMember = { + stats: { + buffs: {}, + class: 'warrior', + }, + preferences: { + hair: {}, + }, + items: { + gear: { + equipped: {}, + }, + }, + }; + beforeEach(() => { Constructr = Vue.extend(Avatar); vm = new Constructr({ - propsData: { - member: { - stats: { - buffs: {}, - }, - preferences: { - hair: {}, - }, - items: { - gear: { - equipped: {}, - }, - }, - }, - }, - }).$mount(); + 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); diff --git a/website/client/tests/unit/components/chat/chatCard.spec.js b/website/client/tests/unit/components/chat/chatCard.spec.js index 473a48696c..090521cfae 100644 --- a/website/client/tests/unit/components/chat/chatCard.spec.js +++ b/website/client/tests/unit/components/chat/chatCard.spec.js @@ -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) { diff --git a/website/client/tests/unit/components/home.spec.js b/website/client/tests/unit/components/home.spec.js new file mode 100644 index 0000000000..c98305b369 --- /dev/null +++ b/website/client/tests/unit/components/home.spec.js @@ -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); + }); + }); +}); diff --git a/website/client/tests/unit/components/notifications.spec.js b/website/client/tests/unit/components/notifications.spec.js index 24dc2a35f6..f4e9c905d9 100644 --- a/website/client/tests/unit/components/notifications.spec.js +++ b/website/client/tests/unit/components/notifications.spec.js @@ -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'); diff --git a/website/client/tests/unit/components/tasks/column.spec.js b/website/client/tests/unit/components/tasks/column.spec.js index 6c2c8a8f68..2fbea418ef 100644 --- a/website/client/tests/unit/components/tasks/column.spec.js +++ b/website/client/tests/unit/components/tasks/column.spec.js @@ -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']; // 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 = [ diff --git a/website/client/tests/unit/components/tasks/user.spec.js b/website/client/tests/unit/components/tasks/user.spec.js index 2bf0097eb6..aeb91192a7 100644 --- a/website/client/tests/unit/components/tasks/user.spec.js +++ b/website/client/tests/unit/components/tasks/user.spec.js @@ -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);