diff --git a/website/client/src/components/shops/quests/buyQuestModal.vue b/website/client/src/components/shops/quests/buyQuestModal.vue index 2e9a86761e..6da0fc8a60 100644 --- a/website/client/src/components/shops/quests/buyQuestModal.vue +++ b/website/client/src/components/shops/quests/buyQuestModal.vue @@ -28,8 +28,8 @@
{{ $t('howManyToBuy') }} diff --git a/website/client/src/components/static/home.vue b/website/client/src/components/static/home.vue index 2f1603519d..0288f7fd63 100644 --- a/website/client/src/components/static/home.vue +++ b/website/client/src/components/static/home.vue @@ -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({ @@ -968,7 +961,7 @@ export default { } else { try { await hello(network).logout(); - } catch (e) {} // eslint-disable-line + } catch (e) {} // eslint-disable-line const redirectUrl = `${window.location.protocol}//${window.location.host}`; const auth = await hello(network).login({ diff --git a/website/client/src/components/ui/pinBadge.vue b/website/client/src/components/ui/pinBadge.vue index c6c14c18c6..ccd05d1ee5 100644 --- a/website/client/src/components/ui/pinBadge.vue +++ b/website/client/src/components/ui/pinBadge.vue @@ -5,7 +5,8 @@ >
+ v-html="icons.pin" + >
diff --git a/website/client/src/libs/store/helpers/public.js b/website/client/src/libs/store/helpers/public.js index c907b7c5ca..c7eedcd047 100644 --- a/website/client/src/libs/store/helpers/public.js +++ b/website/client/src/libs/store/helpers/public.js @@ -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 }; }); diff --git a/website/client/src/libs/store/index.js b/website/client/src/libs/store/index.js index d0af6f9ae8..de9279ff38 100644 --- a/website/client/src/libs/store/index.js +++ b/website/client/src/libs/store/index.js @@ -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.`); 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..bc85ee9bd1 --- /dev/null +++ b/website/client/tests/unit/components/home.spec.js @@ -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); + }); + }); +}); diff --git a/website/client/tests/unit/libs/store.spec.js b/website/client/tests/unit/libs/store.spec.js index 16410d1d53..9fa413a07e 100644 --- a/website/client/tests/unit/libs/store.spec.js +++ b/website/client/tests/unit/libs/store.spec.js @@ -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: { diff --git a/website/client/tests/unit/mixins/groupsUtilities.spec.js b/website/client/tests/unit/mixins/groupsUtilities.spec.js index 0a7ae6ecbb..3c34035429 100644 --- a/website/client/tests/unit/mixins/groupsUtilities.spec.js +++ b/website/client/tests/unit/mixins/groupsUtilities.spec.js @@ -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], }); diff --git a/website/server/libs/email.js b/website/server/libs/email.js index 23a5c17480..2442f8c979 100644 --- a/website/server/libs/email.js +++ b/website/server/libs/email.js @@ -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 diff --git a/website/server/libs/invites/index.js b/website/server/libs/invites/index.js index 1679b1bf73..e7f5055e2b 100644 --- a/website/server/libs/invites/index.js +++ b/website/server/libs/invites/index.js @@ -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,