fix: Resolve infinite loop with campaign API call (#2290)

Co-authored-by: Muhsin <muhsinkeramam@gmail.com>
This commit is contained in:
Pranav Raj S 2021-05-18 12:15:23 +05:30 committed by GitHub
parent bc9db36d62
commit 20a0d381a7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 65 additions and 33 deletions

View file

@ -36,7 +36,7 @@ export default {
...mapGetters({ ...mapGetters({
hasFetched: 'agent/getHasFetched', hasFetched: 'agent/getHasFetched',
unreadMessageCount: 'conversation/getUnreadMessageCount', unreadMessageCount: 'conversation/getUnreadMessageCount',
campaigns: 'campaign/fetchCampaigns', campaigns: 'campaign/getCampaigns',
}), }),
isLeftAligned() { isLeftAligned() {
const isLeft = this.widgetPosition === 'left'; const isLeft = this.widgetPosition === 'left';

View file

@ -3,8 +3,8 @@ export const stripTrailingSlash = ({ URL }) => {
}; };
// Format all campaigns // Format all campaigns
export const formatCampaigns = ({ campagins }) => { export const formatCampaigns = ({ campaigns }) => {
return campagins.map(item => { return campaigns.map(item => {
return { return {
id: item.id, id: item.id,
timeOnPage: item?.trigger_rules?.time_on_page, timeOnPage: item?.trigger_rules?.time_on_page,
@ -14,8 +14,8 @@ export const formatCampaigns = ({ campagins }) => {
}; };
// Find all campaigns that matches the current URL // Find all campaigns that matches the current URL
export const filterCampaigns = ({ campagins, currentURL }) => { export const filterCampaigns = ({ campaigns, currentURL }) => {
return campagins.filter( return campaigns.filter(
item => item =>
stripTrailingSlash({ URL: item.url }) === stripTrailingSlash({ URL: item.url }) ===
stripTrailingSlash({ URL: currentURL }) stripTrailingSlash({ URL: currentURL })

View file

@ -5,9 +5,9 @@ class CampaignTimer {
this.campaignTimers = []; this.campaignTimers = [];
} }
initTimers = ({ campagins }) => { initTimers = ({ campaigns }) => {
this.clearTimers(); this.clearTimers();
campagins.forEach(campaign => { campaigns.forEach(campaign => {
const { timeOnPage, id: campaignId } = campaign; const { timeOnPage, id: campaignId } = campaign;
this.campaignTimers[campaignId] = setTimeout(() => { this.campaignTimers[campaignId] = setTimeout(() => {
triggerCampaign({ campaignId }); triggerCampaign({ campaignId });

View file

@ -15,7 +15,7 @@ describe('#Campagin Helper', () => {
describe('formatCampaigns', () => { describe('formatCampaigns', () => {
it('should return formated campaigns if camapgins are passed', () => { it('should return formated campaigns if camapgins are passed', () => {
expect(formatCampaigns({ campagins: campaigns })).toStrictEqual([ expect(formatCampaigns({ campaigns })).toStrictEqual([
{ {
id: 1, id: 1,
timeOnPage: 3, timeOnPage: 3,
@ -33,7 +33,7 @@ describe('#Campagin Helper', () => {
it('should return filtered campaigns if formatted camapgins are passed', () => { it('should return filtered campaigns if formatted camapgins are passed', () => {
expect( expect(
filterCampaigns({ filterCampaigns({
campagins: [ campaigns: [
{ {
id: 1, id: 1,
timeOnPage: 3, timeOnPage: 3,

View file

@ -13,43 +13,42 @@ const state = {
}, },
}; };
const resetCampaignTimers = (campaigns, currentURL) => {
const formattedCampaigns = formatCampaigns({ campaigns });
// Find all campaigns that matches the current URL
const filteredCampaigns = filterCampaigns({
campaigns: formattedCampaigns,
currentURL,
});
campaignTimer.initTimers({ campaigns: filteredCampaigns });
};
export const getters = { export const getters = {
getHasFetched: $state => $state.uiFlags.hasFetched, getHasFetched: $state => $state.uiFlags.hasFetched,
fetchCampaigns: $state => $state.records, getCampaigns: $state => $state.records,
}; };
export const actions = { export const actions = {
fetchCampaigns: async ( fetchCampaigns: async ({ commit }, { websiteToken, currentURL }) => {
{ commit, dispatch },
{ websiteToken, currentURL }
) => {
try { try {
const { data } = await getCampaigns(websiteToken); const { data: campaigns } = await getCampaigns(websiteToken);
commit('setCampaigns', data); commit('setCampaigns', campaigns);
dispatch('startCampaigns', { currentURL, websiteToken });
commit('setError', false); commit('setError', false);
commit('setHasFetched', true); commit('setHasFetched', true);
resetCampaignTimers(campaigns, currentURL);
} catch (error) { } catch (error) {
commit('setError', true); commit('setError', true);
commit('setHasFetched', true); commit('setHasFetched', true);
} }
}, },
startCampaigns: async ( startCampaigns: async (
{ getters: { fetchCampaigns: campagins }, dispatch }, { getters: { getCampaigns: campaigns }, dispatch },
{ currentURL, websiteToken } { currentURL, websiteToken }
) => { ) => {
if (!campagins.length) { if (!campaigns.length) {
dispatch('fetchCampaigns', { websiteToken, currentURL }); dispatch('fetchCampaigns', { websiteToken, currentURL });
} else { } else {
const formattedCampaigns = formatCampaigns({ resetCampaignTimers(campaigns, currentURL);
campagins,
});
// Find all campaigns that matches the current URL
const filteredCampaigns = filterCampaigns({
campagins: formattedCampaigns,
currentURL,
});
campaignTimer.initTimers({ campagins: filteredCampaigns });
} }
}, },
}; };

View file

@ -3,21 +3,28 @@ import { actions } from '../../campaign';
import { campaigns } from './data'; import { campaigns } from './data';
const commit = jest.fn(); const commit = jest.fn();
const dispatch = jest.fn();
jest.mock('widget/helpers/axios'); jest.mock('widget/helpers/axios');
import campaignTimer from 'widget/helpers/campaignTimer';
jest.mock('widget/helpers/campaignTimer');
describe('#actions', () => { describe('#actions', () => {
describe('#fetchCampaigns', () => { describe('#fetchCampaigns', () => {
it('sends correct actions if API is success', async () => { it('sends correct actions if API is success', async () => {
API.get.mockResolvedValue({ data: campaigns }); API.get.mockResolvedValue({ data: campaigns });
await actions.fetchCampaigns( await actions.fetchCampaigns(
{ commit }, { commit },
{ websiteToken: 'XDsafmADasd', currentURL: 'https://www.chatwoot.com' } { websiteToken: 'XDsafmADasd', currentURL: 'https://chatwoot.com' }
); );
expect(commit.mock.calls).not.toEqual([ expect(commit.mock.calls).toEqual([
['setCampaigns', campaigns], ['setCampaigns', campaigns],
['setError', false], ['setError', false],
['setHasFetched', true], ['setHasFetched', true],
]); ]);
expect(campaignTimer.initTimers).toHaveBeenCalledWith({
campaigns: [{ id: 11, timeOnPage: '20', url: 'https://chatwoot.com' }],
});
}); });
it('sends correct actions if API is error', async () => { it('sends correct actions if API is error', async () => {
API.get.mockRejectedValue({ message: 'Authentication required' }); API.get.mockRejectedValue({ message: 'Authentication required' });
@ -31,4 +38,30 @@ describe('#actions', () => {
]); ]);
}); });
}); });
describe('#startCampaigns', () => {
const actionParams = {
websiteToken: 'XDsafmADasd',
currentURL: 'https://chatwoot.com',
};
it('sends correct actions if campaigns are empty', async () => {
await actions.startCampaigns(
{ dispatch, getters: { getCampaigns: [] } },
actionParams
);
expect(dispatch.mock.calls).toEqual([['fetchCampaigns', actionParams]]);
expect(campaignTimer.initTimers).not.toHaveBeenCalled();
});
it('resets time if campaigns are available', async () => {
await actions.startCampaigns(
{ dispatch, getters: { getCampaigns: campaigns } },
actionParams
);
expect(dispatch.mock.calls).toEqual([]);
expect(campaignTimer.initTimers).toHaveBeenCalledWith({
campaigns: [{ id: 11, timeOnPage: '20', url: 'https://chatwoot.com' }],
});
});
});
}); });

View file

@ -2,11 +2,11 @@ import { getters } from '../../campaign';
import { campaigns } from './data'; import { campaigns } from './data';
describe('#getters', () => { describe('#getters', () => {
it('fetchCampaigns', () => { it('getCampaigns', () => {
const state = { const state = {
records: campaigns, records: campaigns,
}; };
expect(getters.fetchCampaigns(state)).toEqual([ expect(getters.getCampaigns(state)).toEqual([
{ {
id: 1, id: 1,
title: 'Welcome', title: 'Welcome',

View file

@ -2,7 +2,7 @@ import { mutations } from '../../campaign';
import { campaigns } from './data'; import { campaigns } from './data';
describe('#mutations', () => { describe('#mutations', () => {
describe('#setCampagins', () => { describe('#setCampaigns', () => {
it('set campaign records', () => { it('set campaign records', () => {
const state = { records: [] }; const state = { records: [] };
mutations.setCampaigns(state, campaigns); mutations.setCampaigns(state, campaigns);