Skip to content

Commit 06a2743

Browse files
authored
test: Resolve flakey tests (#1098)
1 parent 801717d commit 06a2743

37 files changed

+1876
-3843
lines changed

test/integrations/requirejs/test-requirejs.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const waitForCondition = function async(
3535
};
3636

3737
describe('Require.JS Pages', function() {
38-
it('loads mParticle properly', function() {
38+
it('loads mParticle properly', async () => {
3939
fetchMockSuccess('https://identity.mparticle.com/v1/identify', {
4040
mpid: 'testMPID', is_logged_in: false
4141
});
@@ -54,7 +54,7 @@ describe('Require.JS Pages', function() {
5454
};
5555

5656
mParticle.init('test_key', window.mParticle.config);
57-
waitForCondition(hasIdentifyReturned).then(() => {
57+
await waitForCondition(hasIdentifyReturned);
5858
fetchMock.resetHistory();
5959

6060
mParticle.logEvent('Test Event1');
@@ -63,6 +63,5 @@ describe('Require.JS Pages', function() {
6363
const testEventName = JSON.parse(testEvent[1].body).events[0].data.event_name;
6464

6565
testEventName.should.equal('Test Event1');
66-
})
6766
});
6867
});

test/src/config/setup.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,33 @@ declare global {
1010
let userApi = null;
1111

1212
window.mParticle._isTestEnv = true;
13+
type MParticleSDK = { _forwardingStatsTimer?: number | null };
14+
1315

1416
beforeEach(function() {
17+
const mpInstance = window.mParticle.getInstance();
18+
const store = mpInstance?._Store;
19+
const sessionTimer = store?.globalTimer;
20+
21+
if (typeof sessionTimer === 'number') {
22+
clearTimeout(sessionTimer);
23+
store.globalTimer = 0;
24+
}
25+
26+
const mParticleSDK = (window as Window & { mParticle?: MParticleSDK }).mParticle;
27+
const forwardingStatsTimer = mParticleSDK?._forwardingStatsTimer;
28+
29+
if (typeof forwardingStatsTimer === 'number') {
30+
clearInterval(forwardingStatsTimer);
31+
mParticleSDK._forwardingStatsTimer = 0;
32+
}
33+
1534
// mocha can't clean up after itself, so this lets
1635
// tests mock the current user and restores in between runs.
1736
if (!userApi) {
18-
userApi = window.mParticle.getInstance().Identity.getCurrentUser;
37+
userApi = mpInstance.Identity.getCurrentUser;
1938
} else {
20-
window.mParticle.getInstance().Identity.getCurrentUser = userApi;
39+
mpInstance.Identity.getCurrentUser = userApi;
2140
}
2241

2342
window.mParticle.config = {
@@ -28,9 +47,11 @@ beforeEach(function() {
2847
isDevelopmentMode: false,
2948
flags: {
3049
eventBatchingIntervalMillis: 0,
50+
astBackgroundEvents: 'False',
51+
offlineStorage: '0',
3152
}
3253
};
33-
54+
3455
// This is to tell the resetPersistence method that we are in a test environment
3556
// It should probably be refactored to be included as an argument
3657
window.mParticle._resetForTests(MPConfig);

test/src/config/utils.js

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
das,
1111
} from './constants';
1212
import fetchMock from 'fetch-mock/esm/client';
13+
import sinon from 'sinon';
14+
import { expect } from 'chai';
1315

1416
var pluses = /\+/g,
1517
decoded = function decoded(s) {
@@ -624,7 +626,50 @@ var pluses = /\+/g,
624626
return window.mParticle.Identity.getCurrentUser()?.getMPID() === _mpid;
625627
},
626628
hasIdentityCallInflightReturned = () => !mParticle.getInstance()?._Store?.identityCallInFlight,
627-
hasConfigurationReturned = () => !!mParticle.getInstance()?._Store?.configurationLoaded;
629+
hasConfigurationReturned = () => !!mParticle.getInstance()?._Store?.configurationLoaded,
630+
setupLoggerSpy = () => {
631+
const loggerSpy = {
632+
verbose: sinon.spy(),
633+
warning: sinon.spy(),
634+
error: sinon.spy(),
635+
};
636+
window.mParticle.config.logger = loggerSpy;
637+
window.mParticle.config.logLevel = 'verbose';
638+
return loggerSpy;
639+
},
640+
hasIdentityResponseParsed = (loggerSpy) => {
641+
return () => loggerSpy?.verbose?.getCalls()?.some(call =>
642+
call.args[0] === 'Successfully parsed Identity Response'
643+
);
644+
},
645+
getBeaconBatch = async function(beaconSpy, callIndex = 0) {
646+
const beaconCall = beaconSpy.getCall(callIndex);
647+
expect(beaconCall, 'Expected beacon call to exist').to.exist;
648+
649+
const blob = beaconCall.args[1];
650+
expect(blob).to.be.instanceof(Blob);
651+
652+
const reader = new FileReader();
653+
const blobContent = await new Promise((resolve) => {
654+
reader.onload = () => resolve(reader.result);
655+
reader.readAsText(blob);
656+
});
657+
658+
return JSON.parse(blobContent);
659+
},
660+
setupFakeTimers = function(now) {
661+
return sinon.useFakeTimers({
662+
now: now || Date.now(),
663+
shouldAdvanceTime: true
664+
});
665+
},
666+
triggerVisibilityHidden = function() {
667+
Object.defineProperty(document, 'visibilityState', {
668+
configurable: true,
669+
get: () => 'hidden'
670+
});
671+
document.dispatchEvent(new Event('visibilitychange'));
672+
};
628673

629674
var TestsCore = {
630675
findCookie: findCookie,
@@ -651,8 +696,13 @@ var TestsCore = {
651696
waitForCondition: waitForCondition,
652697
fetchMockSuccess: fetchMockSuccess,
653698
hasIdentifyReturned: hasIdentifyReturned,
699+
getBeaconBatch: getBeaconBatch,
700+
setupFakeTimers: setupFakeTimers,
701+
triggerVisibilityHidden: triggerVisibilityHidden,
654702
hasIdentityCallInflightReturned,
655703
hasConfigurationReturned,
704+
setupLoggerSpy,
705+
hasIdentityResponseParsed,
656706
};
657707

658708
export default TestsCore;

test/src/tests-apiClient.ts

Lines changed: 35 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,11 @@ const mParticle = window.mParticle;
1414

1515
describe('Api Client', () => {
1616
beforeEach(() => {
17-
mParticle.init(apiKey, mParticle.config);
18-
});
19-
20-
afterEach(() => {
2117
mParticle._resetForTests(MPConfig);
18+
mParticle.init(apiKey, mParticle.config);
2219
});
2320

24-
it('should update queued events with latest user info', done => {
21+
it('should update queued events with latest user info', () => {
2522
const event = {
2623
messageType: Types.MessageType.PageEvent,
2724
name: 'foo page',
@@ -30,89 +27,66 @@ describe('Api Client', () => {
3027
customFlags: { 'foo-flag': 'foo-flag-val' },
3128
};
3229

33-
expect(mParticle.getInstance()._Store).to.be.ok;
34-
let sdkEvent1 = mParticle
35-
.getInstance()
36-
._ServerModel.createEventObject(event);
30+
const mpInstance = mParticle.getInstance();
31+
expect(mpInstance._Store).to.be.ok;
32+
33+
const sdkEvent1 = mpInstance._ServerModel.createEventObject(event);
34+
const sdkEvent2 = mpInstance._ServerModel.createEventObject(event);
3735

3836
expect(sdkEvent1).to.be.ok;
3937
expect(sdkEvent1.MPID).equal(null);
4038
expect(sdkEvent1.UserAttributes).equal(null);
4139
expect(sdkEvent1.UserIdentities).equal(null);
4240
expect(sdkEvent1.ConsentState).equal(null);
4341

44-
let sdkEvent2 = mParticle
45-
.getInstance()
46-
._ServerModel.createEventObject(event);
47-
4842
expect(sdkEvent2).to.be.ok;
4943
expect(sdkEvent2.MPID).equal(null);
5044
expect(sdkEvent2.UserAttributes).equal(null);
5145
expect(sdkEvent2.UserIdentities).equal(null);
5246
expect(sdkEvent2.ConsentState).equal(null);
5347

54-
const consentState = mParticle
55-
.getInstance()
56-
.Consent.createConsentState();
48+
const consentState = mpInstance.Consent.createConsentState();
5749
consentState.addGDPRConsentState(
5850
'foo',
59-
mParticle
60-
.getInstance()
61-
.Consent.createGDPRConsent(
62-
true,
63-
10,
64-
'foo document',
65-
'foo location',
66-
'foo hardware id'
67-
)
51+
mpInstance.Consent.createGDPRConsent(
52+
true,
53+
10,
54+
'foo document',
55+
'foo location',
56+
'foo hardware id'
57+
)
6858
);
6959

70-
mParticle.getInstance().Identity.getCurrentUser = () => {
71-
return {
72-
getUserIdentities: () => {
73-
return {
74-
userIdentities: {
75-
customerid: '1234567',
76-
email: 'foo-email',
77-
other: 'foo-other',
78-
other2: 'foo-other2',
79-
other3: 'foo-other3',
80-
other4: 'foo-other4',
81-
},
82-
};
83-
},
84-
getAllUserAttributes: () => {
85-
return {
86-
'foo-user-attr': 'foo-attr-value',
87-
'foo-user-attr-list': ['item1', 'item2'],
88-
};
60+
const mockUser = {
61+
getUserIdentities: () => ({
62+
userIdentities: {
63+
customerid: '1234567',
64+
email: 'foo-email',
65+
other: 'foo-other',
66+
other2: 'foo-other2',
67+
other3: 'foo-other3',
68+
other4: 'foo-other4',
8969
},
90-
getMPID: () => {
91-
return '98765';
92-
},
93-
getConsentState: () => {
94-
return consentState;
95-
},
96-
} as IMParticleUser;
97-
};
70+
}),
71+
getAllUserAttributes: () => ({
72+
'foo-user-attr': 'foo-attr-value',
73+
'foo-user-attr-list': ['item1', 'item2'],
74+
}),
75+
getMPID: () => '98765',
76+
getConsentState: () => consentState,
77+
} as IMParticleUser;
9878

99-
mParticle
100-
.getInstance()
101-
._APIClient.appendUserInfoToEvents(
102-
mParticle.Identity.getCurrentUser(),
103-
[sdkEvent1, sdkEvent2]
104-
);
79+
80+
mpInstance._APIClient.appendUserInfoToEvents(mockUser, [sdkEvent1, sdkEvent2]);
10581

10682
expect(sdkEvent1.UserIdentities.length).to.equal(6);
107-
expect(Object.keys(sdkEvent2.UserAttributes).length).to.equal(2);
83+
expect(Object.keys(sdkEvent1.UserAttributes).length).to.equal(2);
10884
expect(sdkEvent1.MPID).to.equal('98765');
10985
expect(sdkEvent1.ConsentState).to.not.equal(null);
11086

11187
expect(sdkEvent2.UserIdentities.length).to.equal(6);
11288
expect(Object.keys(sdkEvent2.UserAttributes).length).to.equal(2);
11389
expect(sdkEvent2.MPID).to.equal('98765');
11490
expect(sdkEvent2.ConsentState).to.not.equal(null);
115-
116-
done();
11791
});
11892
});

test/src/tests-audience-manager.ts

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import sinon from 'sinon';
22
import fetchMock from 'fetch-mock/esm/client';
33
import { expect } from 'chai';
4-
import { urls, apiKey, testMPID } from './config/constants';
4+
import { urls, apiKey, testMPID, MPConfig } from './config/constants';
55
import Constants from '../../src/constants';
66
import { IMParticleInstanceManager, SDKLoggerApi } from '../../src/sdkRuntimeModels';
77
import AudienceManager, {
@@ -21,21 +21,11 @@ declare global {
2121
const userAudienceUrl = `https://${Constants.DefaultBaseUrls.userAudienceUrl}${apiKey}/audience`;
2222

2323
describe('AudienceManager', () => {
24-
before(function() {
25-
fetchMock.restore();
26-
sinon.restore();
27-
});
28-
29-
beforeEach(function() {
30-
fetchMock.restore();
31-
24+
beforeEach(() => {
25+
window.mParticle._resetForTests(MPConfig);
3226
fetchMockSuccess(urls.identify, {
3327
mpid: testMPID, is_logged_in: false
3428
});
35-
36-
window.mParticle.config.flags = {
37-
eventBatchingIntervalMillis: 1000,
38-
};
3929
});
4030

4131
afterEach(() => {
@@ -59,12 +49,16 @@ describe('AudienceManager', () => {
5949
});
6050

6151
describe('#sendGetUserAudienceRequest', () => {
62-
const newLogger = new Logger(window.mParticle.config);
63-
const audienceManager = new AudienceManager(
64-
Constants.DefaultBaseUrls.userAudienceUrl,
65-
apiKey,
66-
newLogger
67-
);
52+
let newLogger: SDKLoggerApi;
53+
let audienceManager: AudienceManager;
54+
beforeEach(() => {
55+
newLogger = new Logger(window.mParticle.config);
56+
audienceManager = new AudienceManager(
57+
Constants.DefaultBaseUrls.userAudienceUrl,
58+
apiKey,
59+
newLogger
60+
);
61+
});
6862

6963
const audienceMembershipServerResponse: IAudienceMembershipsServerResponse = {
7064
ct: 1710441407914,
@@ -135,15 +129,15 @@ describe('AudienceManager', () => {
135129
};
136130

137131
const expectedAudienceMembership2: IAudienceMemberships = {
138-
currentAudienceMemberships: [
139-
{
140-
audience_id: 9876,
141-
},
142-
{
143-
audience_id: 5432,
144-
},
145-
]
146-
};
132+
currentAudienceMemberships: [
133+
{
134+
audience_id: 9876,
135+
},
136+
{
137+
audience_id: 5432,
138+
},
139+
]
140+
};
147141

148142
fetchMock.get(newMPIDAudienceEndpoint, {
149143
status: 200,

0 commit comments

Comments
 (0)