Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat:(service-worker): new service worker compatible SDK with samples #668

Merged
merged 8 commits into from
Oct 17, 2022
Merged
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ publish.sh
dist/*
!dist/rudder-sdk-js/*
dist/rudder-sdk-js/index.js
dist/rudder-sdk-js/service-worker/
coverage/**
__tests__/prodsdk.js
.eslintcache
.idea
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ The JavaScript SDK lets you track customer event data from your website and send
- [**The `ready` API**](https://github.com/rudderlabs/rudder-sdk-js/blob/master/README.md#the-ready-api)
- [**Self-hosted control plane**](https://github.com/rudderlabs/rudder-sdk-js/blob/master/README.md#self-hosted-control-plane)
- [**Adding your own integrations**](https://github.com/rudderlabs/rudder-sdk-js/blob/master/README.md#adding-your-own-integrations)
- [**Usage in Chrome Extensions**](https://github.com/rudderlabs/rudder-sdk-js/blob/master/README.md#usage-in-chrome-extensions)

| **IMPORTANT**: We have deprecated the Autotrack feature for the RudderStack JavaScript SDK and it will soon be removed. If you still wish to use it for your project, refer to [**this repository**](https://github.com/rudderlabs/rudder-sdk-js-autotrack#autotrack). |
| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
Expand Down Expand Up @@ -297,6 +298,13 @@ You can start adding integrations of your choice for sending the data through th
- For adding or removing integrations, modify the imports in `index.js` under the `integrations` folder.

### Usage in Chrome Extensions

RudderStack JS SDK can be used in Chrome Extensions with manifest v3, both as a content script or as a background script
service worker.

For examples and specific details look into [Chrome Extensions Usage](https://github.com/rudderlabs/rudder-sdk-js/blob/master/tests/chrome-extension/USAGE.md)

## Contribute

We would love to see you contribute to this project. Get more information on how to contribute [**here**](./CONTRIBUTING.md).
Expand Down
79 changes: 79 additions & 0 deletions __tests__/service-worker/__mocks__/fixtures.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
const identifyRequestPayload = {
userId: '123456',
traits: {
name: 'Name Username',
email: 'name@website.com',
plan: 'Free',
friends: 21,
},
};

const trackRequestPayload = {
userId: '123456',
event: 'Item Viewed',
properties: {
revenue: 19.95,
shippingMethod: 'Premium',
},
};

const pageRequestPayload = {
userId: '12345',
category: 'Food',
name: 'Pizza',
properties: {
url: 'https://dominos.com',
title: 'Pizza',
referrer: 'https://google.com',
},
};

const screenRequestPayload = {
userId: '12345',
category: 'Food',
name: 'Pizza',
properties: {
screenSize: 10,
title: 'Pizza',
referrer: 'https://google.com',
},
};

const groupRequestPayload = {
userId: '12345',
groupId: '1',
traits: {
name: 'Company',
description: 'Google',
},
};

const aliasRequestPayload = {
previousId: 'old_id',
userId: 'new_id',
};

const dummyWriteKey = 'dummyWriteKey';

const dummyDataplaneHost = 'https://dummy.dataplane.host.com';

const dummyInitOptions = {
timeout: false,
flushAt: 1,
flushInterval: 200000,
maxInternalQueueSize: 1,
logLevel: 'off',
enable: true,
};

export {
identifyRequestPayload,
trackRequestPayload,
pageRequestPayload,
screenRequestPayload,
groupRequestPayload,
aliasRequestPayload,
dummyWriteKey,
dummyInitOptions,
dummyDataplaneHost,
};
12 changes: 12 additions & 0 deletions __tests__/service-worker/__mocks__/msw.server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { setupServer } from 'msw/node';
import { dummyDataplaneHost } from './fixtures';

const server = setupServer({
url: `${dummyDataplaneHost}/v1/batch`,
response: () => null,
status: 200,
method: 'post',
responseHeaders: { Environment: 'local' },
});

export { server };
109 changes: 109 additions & 0 deletions __tests__/service-worker/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { advanceTo } from 'jest-date-mock';
import { Analytics } from '../../service-worker';
import { server } from './__mocks__/msw.server';
import {
aliasRequestPayload,
dummyDataplaneHost,
dummyInitOptions,
dummyWriteKey,
groupRequestPayload,
identifyRequestPayload,
pageRequestPayload,
screenRequestPayload,
trackRequestPayload,
} from './__mocks__/fixtures';

jest.mock('uuid', () => ({ v4: () => '123456789' }));

describe('JS SDK Service Worker', () => {
let rudderAnalyticsClient = null;
let requestBody;

beforeAll(() => {
advanceTo(new Date(2022, 1, 21, 0, 0, 0));
server.listen();
});

beforeEach(() => {
rudderAnalyticsClient = new Analytics(dummyWriteKey, dummyDataplaneHost, dummyInitOptions);
server.events.on('request:start', (req) => {
requestBody = req.body;
});
});

afterEach(() => {
rudderAnalyticsClient = null;
server.resetHandlers();
server.events.removeAllListeners();
requestBody = null;
});

afterAll(() => {
server.close();
});

it('Should initialise with correct values', () => {
expect(rudderAnalyticsClient.writeKey).toBe(dummyWriteKey);
expect(rudderAnalyticsClient.host).toBe(dummyDataplaneHost);
expect(rudderAnalyticsClient.timeout).toBe(dummyInitOptions.timeout);
expect(rudderAnalyticsClient.flushAt).toBe(dummyInitOptions.flushAt);
expect(rudderAnalyticsClient.flushInterval).toBe(dummyInitOptions.flushInterval);
expect(rudderAnalyticsClient.maxInternalQueueSize).toBe(dummyInitOptions.maxInternalQueueSize);
expect(rudderAnalyticsClient.logLevel).toBe(dummyInitOptions.logLevel);
expect(rudderAnalyticsClient.enable).toBe(dummyInitOptions.enable);
});

it('Should record identify', async () => {
rudderAnalyticsClient.identify(identifyRequestPayload);
rudderAnalyticsClient.flush();

await new Promise((r) => setTimeout(r, 1));

expect(requestBody.batch[0]).toEqual(expect.objectContaining(identifyRequestPayload));
});

it('Should record track', async () => {
rudderAnalyticsClient.track(trackRequestPayload);
rudderAnalyticsClient.flush();

await new Promise((r) => setTimeout(r, 1));

expect(requestBody.batch[0]).toEqual(expect.objectContaining(trackRequestPayload));
});

it('Should record page', async () => {
rudderAnalyticsClient.page(pageRequestPayload);
rudderAnalyticsClient.flush();

await new Promise((r) => setTimeout(r, 1));

expect(requestBody.batch[0]).toEqual(expect.objectContaining(pageRequestPayload));
});

it('Should record screen', async () => {
rudderAnalyticsClient.screen(screenRequestPayload);
rudderAnalyticsClient.flush();

await new Promise((r) => setTimeout(r, 1));

expect(requestBody.batch[0]).toEqual(expect.objectContaining(screenRequestPayload));
});

it('Should record group', async () => {
rudderAnalyticsClient.group(groupRequestPayload);
rudderAnalyticsClient.flush();

await new Promise((r) => setTimeout(r, 1));

expect(requestBody.batch[0]).toEqual(expect.objectContaining(groupRequestPayload));
});

it('Should record alias', async () => {
rudderAnalyticsClient.alias(aliasRequestPayload);
rudderAnalyticsClient.flush();

await new Promise((r) => setTimeout(r, 1));

expect(requestBody.batch[0]).toEqual(expect.objectContaining(aliasRequestPayload));
});
});
6 changes: 6 additions & 0 deletions dist/rudder-sdk-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
"version": "2.16.0",
"description": "RudderStack Javascript SDK",
"main": "index.js",
"module": "index.es.js",
"exports": {
".": {
"service-worker": "./service-worker/index.es.js"
}
},
"types": "index.d.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
Expand Down
19 changes: 14 additions & 5 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// https://jestjs.io/docs/en/configuration.html

module.exports = {
prettierPath: 'prettier',
// All imported modules in your tests should be mocked automatically
// automock: false,

Expand All @@ -24,7 +25,14 @@ module.exports = {
coverageDirectory: 'coverage',

// An array of regexp pattern strings used to skip coverage collection
coveragePathIgnorePatterns: ['/node_modules/', '/dist/', '/tests/', '/scripts/', '__tests__'],
coveragePathIgnorePatterns: [
'/node_modules/',
'/dist/',
'/tests/',
'/scripts/',
'__tests__',
'__mocks__',
],

// A list of reporter names that Jest uses when writing coverage reports
coverageReporters: ['json', 'text', 'lcov', 'clover'],
Expand Down Expand Up @@ -152,12 +160,12 @@ module.exports = {
// timers: "real",

// A map from regular expressions to paths to transformers
// transform: undefined,
transform: {
'^.+\\.(js|)?$': 'esbuild-jest',
},

// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
// transformIgnorePatterns: [
// "/node_modules/"
// ],
transformIgnorePatterns: ['<rootDir>/.github/', '<rootDir>/.husky/', '<rootDir>/dist/'],

// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
Expand All @@ -170,4 +178,5 @@ module.exports = {

// Whether to use watchman for file crawling
// watchman: true,
setupFiles: ['jest-date-mock', '<rootDir>/jest/jest.polyfills.js'],
};
1 change: 1 addition & 0 deletions jest/jest.polyfills.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require('isomorphic-fetch');
Loading