Skip to content

Commit b1ec8c1

Browse files
authored
[FSSDK-9605] add sendBeacon event dispatcher and test (#873)
1 parent 2c4865a commit b1ec8c1

File tree

5 files changed

+177
-1
lines changed

5 files changed

+177
-1
lines changed
Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
module.exports = {
22
"transform": {
3-
"^.+\\.tsx?$": "ts-jest"
3+
"^.+\\.(ts|tsx|js|jsx)$": "ts-jest",
44
},
55
"testRegex": "(/tests/.*|(\\.|/)(test|spec))\\.tsx?$",
6+
moduleNameMapper: {
7+
// Force module uuid to resolve with the CJS entry point, because Jest does not support package.json.exports. See https://github.com/uuidjs/uuid/issues/451
8+
"uuid": require.resolve('uuid'),
9+
},
10+
"testPathIgnorePatterns" : [
11+
"tests/testUtils.ts",
12+
"dist"
13+
],
614
"moduleFileExtensions": [
715
"ts",
816
"tsx",
@@ -11,4 +19,6 @@ module.exports = {
1119
"json",
1220
"node"
1321
],
22+
"resetMocks": false,
23+
testEnvironment: "jsdom"
1424
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Copyright 2023, Optimizely
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { EventDispatcher } from '../../shared_types';
18+
19+
export type Event = {
20+
url: string;
21+
httpVerb: 'POST';
22+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
23+
params: any;
24+
}
25+
26+
/**
27+
* Sample event dispatcher implementation for tracking impression and conversions
28+
* Users of the SDK can provide their own implementation
29+
* @param {Event} eventObj
30+
* @param {Function} callback
31+
*/
32+
export const dispatchEvent = function(
33+
eventObj: Event,
34+
callback: (response: { statusCode: number; }) => void
35+
): void {
36+
const { params, url } = eventObj;
37+
const blob = new Blob([JSON.stringify(params)], {
38+
type: "application/json",
39+
});
40+
41+
const success = navigator.sendBeacon(url, blob);
42+
callback({
43+
statusCode: success ? 200 : 500,
44+
});
45+
}
46+
47+
const eventDispatcher : EventDispatcher = {
48+
dispatchEvent,
49+
}
50+
51+
export default eventDispatcher;

packages/optimizely-sdk/package-lock.json

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/optimizely-sdk/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
"sinon": "^2.3.1",
8181
"ts-jest": "^23.10.5",
8282
"ts-loader": "^7.0.5",
83+
"ts-mockito": "^2.6.1",
8384
"ts-node": "^8.10.2",
8485
"typescript": "^4.0.3",
8586
"webpack": "^4.42.1"
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/**
2+
* Copyright 2023, Optimizely
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import sendBeaconDispatcher, { Event } from '../lib/plugins/event_dispatcher/send_beacon_dispatcher';
17+
import { anyString, anything, capture, instance, mock, reset, when } from 'ts-mockito';
18+
19+
describe('dispatchEvent', function() {
20+
const mockNavigator = mock<Navigator>();
21+
22+
afterEach(function() {
23+
reset(mockNavigator);
24+
});
25+
26+
it('should call sendBeacon with correct url, data and type', async () => {
27+
var eventParams = { testParam: 'testParamValue' };
28+
var eventObj: Event = {
29+
url: 'https://cdn.com/event',
30+
httpVerb: 'POST',
31+
params: eventParams,
32+
};
33+
34+
when(mockNavigator.sendBeacon(anyString(), anything())).thenReturn(true);
35+
const navigator = instance(mockNavigator);
36+
if (!global.navigator) {
37+
global.navigator = navigator;
38+
}
39+
40+
global.navigator.sendBeacon = navigator.sendBeacon;
41+
42+
sendBeaconDispatcher.dispatchEvent(eventObj, () => {});
43+
44+
const [url, data] = capture(mockNavigator.sendBeacon).last();
45+
const blob = data as Blob;
46+
47+
const reader = new FileReader();
48+
reader.readAsBinaryString(blob);
49+
50+
const sentParams = await new Promise((resolve) => {
51+
reader.onload = () => {``
52+
resolve(reader.result);
53+
};
54+
});
55+
56+
57+
expect(url).toEqual(eventObj.url);
58+
expect(blob.type).toEqual('application/json');
59+
expect(sentParams).toEqual(JSON.stringify(eventObj.params));
60+
});
61+
62+
it('should call call callback with status 200 on sendBeacon success', (done) => {
63+
var eventParams = { testParam: 'testParamValue' };
64+
var eventObj: Event = {
65+
url: 'https://cdn.com/event',
66+
httpVerb: 'POST',
67+
params: eventParams,
68+
};
69+
70+
when(mockNavigator.sendBeacon(anyString(), anything())).thenReturn(true);
71+
const navigator = instance(mockNavigator);
72+
global.navigator.sendBeacon = navigator.sendBeacon;
73+
74+
sendBeaconDispatcher.dispatchEvent(eventObj, (res: { statusCode: number }) => {
75+
try {
76+
expect(res.statusCode).toEqual(200);
77+
done();
78+
} catch(err) {
79+
done(err);
80+
}
81+
});
82+
});
83+
84+
it('should call call callback with status 200 on sendBeacon failure', (done) => {
85+
var eventParams = { testParam: 'testParamValue' };
86+
var eventObj: Event = {
87+
url: 'https://cdn.com/event',
88+
httpVerb: 'POST',
89+
params: eventParams,
90+
};
91+
92+
when(mockNavigator.sendBeacon(anyString(), anything())).thenReturn(false);
93+
const navigator = instance(mockNavigator);
94+
global.navigator.sendBeacon = navigator.sendBeacon;
95+
96+
sendBeaconDispatcher.dispatchEvent(eventObj, (res: { statusCode: number }) => {
97+
try {
98+
expect(res.statusCode).toEqual(500);
99+
done();
100+
} catch(err) {
101+
done(err);
102+
}
103+
});
104+
});
105+
});

0 commit comments

Comments
 (0)