@dotcms/experiments 0.0.1-alpha.39 → 0.0.1-alpha.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.esm.d.ts +1 -0
- package/index.esm.js +7174 -0
- package/package.json +10 -6
- package/src/lib/components/{DotExperimentHandlingComponent.tsx → DotExperimentHandlingComponent.d.ts} +3 -20
- package/src/lib/components/{DotExperimentsProvider.tsx → DotExperimentsProvider.d.ts} +3 -41
- package/src/lib/components/withExperiments.d.ts +20 -0
- package/src/lib/contexts/{DotExperimentsContext.tsx → DotExperimentsContext.d.ts} +2 -5
- package/src/lib/dot-experiments.d.ts +289 -0
- package/src/lib/hooks/useExperimentVariant.d.ts +21 -0
- package/src/lib/hooks/useExperiments.d.ts +14 -0
- package/src/lib/shared/{constants.ts → constants.d.ts} +18 -35
- package/src/lib/shared/mocks/mock.d.ts +43 -0
- package/src/lib/shared/{models.ts → models.d.ts} +2 -35
- package/src/lib/shared/parser/parser.d.ts +54 -0
- package/src/lib/shared/persistence/index-db-database-handler.d.ts +87 -0
- package/src/lib/shared/utils/DotLogger.d.ts +15 -0
- package/src/lib/shared/utils/memoize.d.ts +7 -0
- package/src/lib/shared/utils/utils.d.ts +73 -0
- package/src/lib/standalone.d.ts +7 -0
- package/.babelrc +0 -12
- package/.eslintrc.json +0 -26
- package/jest.config.ts +0 -11
- package/project.json +0 -55
- package/src/lib/components/DotExperimentsProvider.spec.tsx +0 -62
- package/src/lib/components/withExperiments.tsx +0 -52
- package/src/lib/contexts/DotExperimentsContext.spec.tsx +0 -42
- package/src/lib/dot-experiments.spec.ts +0 -285
- package/src/lib/dot-experiments.ts +0 -716
- package/src/lib/hooks/useExperimentVariant.spec.tsx +0 -111
- package/src/lib/hooks/useExperimentVariant.ts +0 -55
- package/src/lib/hooks/useExperiments.ts +0 -90
- package/src/lib/shared/mocks/mock.ts +0 -209
- package/src/lib/shared/parser/parse.spec.ts +0 -187
- package/src/lib/shared/parser/parser.ts +0 -171
- package/src/lib/shared/persistence/index-db-database-handler.spec.ts +0 -100
- package/src/lib/shared/persistence/index-db-database-handler.ts +0 -218
- package/src/lib/shared/utils/DotLogger.ts +0 -57
- package/src/lib/shared/utils/memoize.spec.ts +0 -49
- package/src/lib/shared/utils/memoize.ts +0 -49
- package/src/lib/shared/utils/utils.spec.ts +0 -142
- package/src/lib/shared/utils/utils.ts +0 -203
- package/src/lib/standalone.spec.ts +0 -36
- package/src/lib/standalone.ts +0 -28
- package/tsconfig.json +0 -20
- package/tsconfig.lib.json +0 -20
- package/tsconfig.spec.json +0 -9
- package/vite.config.ts +0 -41
- /package/src/{index.ts → index.d.ts} +0 -0
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { renderHook } from '@testing-library/react-hooks';
|
|
2
|
-
import { ReactNode, useContext } from 'react';
|
|
3
|
-
|
|
4
|
-
import DotExperimentsContext from './DotExperimentsContext';
|
|
5
|
-
|
|
6
|
-
import { DotExperiments } from '../dot-experiments';
|
|
7
|
-
|
|
8
|
-
jest.mock('../dot-experiments', () => {
|
|
9
|
-
return jest.fn().mockImplementation(() => {
|
|
10
|
-
return {};
|
|
11
|
-
});
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
describe('useDotExperimentsContext', () => {
|
|
15
|
-
it('returns the context value null', () => {
|
|
16
|
-
const mockContextValue = null;
|
|
17
|
-
|
|
18
|
-
const { result } = renderHook(() => useContext(DotExperimentsContext), {
|
|
19
|
-
wrapper: ({ children }: { children: ReactNode }) => (
|
|
20
|
-
<DotExperimentsContext.Provider value={mockContextValue}>
|
|
21
|
-
{children}
|
|
22
|
-
</DotExperimentsContext.Provider>
|
|
23
|
-
)
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
expect(result.current).toEqual(mockContextValue);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('returns the context value DotExperiment', () => {
|
|
30
|
-
const mockContextValue = {} as DotExperiments;
|
|
31
|
-
|
|
32
|
-
const { result } = renderHook(() => useContext(DotExperimentsContext), {
|
|
33
|
-
wrapper: ({ children }: { children: ReactNode }) => (
|
|
34
|
-
<DotExperimentsContext.Provider value={mockContextValue}>
|
|
35
|
-
{children}
|
|
36
|
-
</DotExperimentsContext.Provider>
|
|
37
|
-
)
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
expect(result.current).toEqual(mockContextValue);
|
|
41
|
-
});
|
|
42
|
-
});
|
|
@@ -1,285 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
2
|
-
import fakeIndexedDB from 'fake-indexeddb';
|
|
3
|
-
import fetchMock from 'fetch-mock';
|
|
4
|
-
|
|
5
|
-
import { DotExperiments } from './dot-experiments';
|
|
6
|
-
import { API_EXPERIMENTS_URL, EXPERIMENT_QUERY_PARAM_KEY } from './shared/constants';
|
|
7
|
-
import {
|
|
8
|
-
After15DaysIsUserIncludedResponse,
|
|
9
|
-
IsUserIncludedResponse,
|
|
10
|
-
LocationMock,
|
|
11
|
-
MOCK_CURRENT_TIMESTAMP,
|
|
12
|
-
MockDataStoredIndexDB,
|
|
13
|
-
MockDataStoredIndexDBWithNew,
|
|
14
|
-
MockDataStoredIndexDBWithNew15DaysLater,
|
|
15
|
-
NewIsUserIncludedResponse,
|
|
16
|
-
NoExperimentsIsUserIncludedResponse,
|
|
17
|
-
sessionStorageMock,
|
|
18
|
-
TIME_15_DAYS_MILLISECONDS,
|
|
19
|
-
TIME_5_DAYS_MILLISECONDS
|
|
20
|
-
} from './shared/mocks/mock';
|
|
21
|
-
import { DotExperimentConfig } from './shared/models';
|
|
22
|
-
|
|
23
|
-
jest.spyOn(Date, 'now').mockImplementation(() => MOCK_CURRENT_TIMESTAMP);
|
|
24
|
-
|
|
25
|
-
// Jitsu SDK Mock
|
|
26
|
-
jest.mock('@jitsu/sdk-js', () => ({
|
|
27
|
-
jitsuClient: jest.fn(() => ({
|
|
28
|
-
set: jest.fn(),
|
|
29
|
-
track: jest.fn().mockResolvedValue(true)
|
|
30
|
-
}))
|
|
31
|
-
}));
|
|
32
|
-
|
|
33
|
-
// SessionStorage mock
|
|
34
|
-
global.sessionStorage = sessionStorageMock;
|
|
35
|
-
|
|
36
|
-
// IndexDB Mock
|
|
37
|
-
Object.defineProperty(window, 'indexedDB', {
|
|
38
|
-
writable: true,
|
|
39
|
-
value: fakeIndexedDB
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
if (!globalThis.structuredClone) {
|
|
43
|
-
globalThis.structuredClone = function (obj) {
|
|
44
|
-
return JSON.parse(JSON.stringify(obj));
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Windows Mock
|
|
49
|
-
global.window = Object.create(window);
|
|
50
|
-
Object.defineProperty(window, 'location', {
|
|
51
|
-
value: {
|
|
52
|
-
href: 'http://localhost:8080/',
|
|
53
|
-
origin: 'http://localhost:8080'
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
describe('DotExperiments', () => {
|
|
58
|
-
const configMock: DotExperimentConfig = {
|
|
59
|
-
apiKey: 'yourApiKey',
|
|
60
|
-
server: 'http://localhost:8080/',
|
|
61
|
-
debug: false,
|
|
62
|
-
trackPageView: true
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
describe('DotExperiments Instance and Initialization', () => {
|
|
66
|
-
beforeEach(() => {
|
|
67
|
-
// destroy the instance of the singleton
|
|
68
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
69
|
-
(DotExperiments as any).instance = null;
|
|
70
|
-
});
|
|
71
|
-
it('should throw an error if config is not provided', () => {
|
|
72
|
-
expect(() => DotExperiments.getInstance()).toThrow(
|
|
73
|
-
'Configuration is required to create a new instance.'
|
|
74
|
-
);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('should instantiate the class when getInstance is called with config', () => {
|
|
78
|
-
const dotExperimentsInstance = DotExperiments.getInstance(configMock);
|
|
79
|
-
|
|
80
|
-
expect(dotExperimentsInstance).toBeInstanceOf(DotExperiments);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('should throw an error if server is not provided in config', () => {
|
|
84
|
-
expect(() =>
|
|
85
|
-
// @ts-ignore
|
|
86
|
-
DotExperiments.getInstance({ 'api-key': 'api-key-test', debug: true })
|
|
87
|
-
).toThrow('`server` must be provided and should not be empty!');
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('should throw an error if api-key is not provided in config', () => {
|
|
91
|
-
expect(() =>
|
|
92
|
-
// @ts-ignore
|
|
93
|
-
DotExperiments.getInstance({ server: 'http://server-test.com', debug: true })
|
|
94
|
-
).toThrow('`apiKey` must be provided and should not be empty!');
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('should return false if the debug inactive', async () => {
|
|
98
|
-
const instance = DotExperiments.getInstance(configMock);
|
|
99
|
-
|
|
100
|
-
expect(instance.getIsDebugActive()).toBe(false);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should not call to trackPageView if you send the flag', async () => {
|
|
104
|
-
const config: DotExperimentConfig = { ...configMock, trackPageView: false };
|
|
105
|
-
|
|
106
|
-
fetchMock.post(`${configMock.server}/${API_EXPERIMENTS_URL}`, {
|
|
107
|
-
status: 200,
|
|
108
|
-
body: IsUserIncludedResponse,
|
|
109
|
-
headers: {
|
|
110
|
-
Accept: 'application/json',
|
|
111
|
-
'Content-Type': 'application/json'
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
const instance = DotExperiments.getInstance(config);
|
|
116
|
-
|
|
117
|
-
const spyTrackPageView = jest.spyOn(instance, 'trackPageView');
|
|
118
|
-
|
|
119
|
-
expect(spyTrackPageView).not.toHaveBeenCalled();
|
|
120
|
-
|
|
121
|
-
await instance.locationChanged(LocationMock).then(() => {
|
|
122
|
-
expect(spyTrackPageView).not.toHaveBeenCalled();
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('should not call to trackPageView if you dont have experiment to track', async () => {
|
|
127
|
-
const config: DotExperimentConfig = { ...configMock, trackPageView: false };
|
|
128
|
-
|
|
129
|
-
fetchMock.post(
|
|
130
|
-
`${configMock.server}/${API_EXPERIMENTS_URL}`,
|
|
131
|
-
{
|
|
132
|
-
status: 200,
|
|
133
|
-
body: NoExperimentsIsUserIncludedResponse,
|
|
134
|
-
headers: {
|
|
135
|
-
Accept: 'application/json',
|
|
136
|
-
'Content-Type': 'application/json'
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
{ overwriteRoutes: true }
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
const instance = DotExperiments.getInstance(config);
|
|
143
|
-
|
|
144
|
-
const spyTrackPageView = jest.spyOn(instance, 'trackPageView');
|
|
145
|
-
|
|
146
|
-
expect(spyTrackPageView).not.toHaveBeenCalled();
|
|
147
|
-
|
|
148
|
-
await instance.locationChanged(LocationMock).then(() => {
|
|
149
|
-
expect(spyTrackPageView).not.toHaveBeenCalled();
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('should return a a string with the query params of variant by the url given', async () => {
|
|
154
|
-
const config: DotExperimentConfig = { ...configMock, trackPageView: false };
|
|
155
|
-
|
|
156
|
-
fetchMock.post(
|
|
157
|
-
`${configMock.server}/${API_EXPERIMENTS_URL}`,
|
|
158
|
-
{
|
|
159
|
-
status: 200,
|
|
160
|
-
body: IsUserIncludedResponse,
|
|
161
|
-
headers: {
|
|
162
|
-
Accept: 'application/json',
|
|
163
|
-
'Content-Type': 'application/json'
|
|
164
|
-
}
|
|
165
|
-
},
|
|
166
|
-
{ overwriteRoutes: true }
|
|
167
|
-
);
|
|
168
|
-
|
|
169
|
-
const instance = DotExperiments.getInstance(config);
|
|
170
|
-
|
|
171
|
-
await instance.ready();
|
|
172
|
-
|
|
173
|
-
const EMPTY_URL = '';
|
|
174
|
-
|
|
175
|
-
const expected1 = new URLSearchParams({});
|
|
176
|
-
|
|
177
|
-
expect(instance.getVariantAsQueryParam(EMPTY_URL)).toStrictEqual(expected1);
|
|
178
|
-
|
|
179
|
-
const URL_WITH_EXPERIMENT = '/blog';
|
|
180
|
-
|
|
181
|
-
const expected2 = new URLSearchParams({
|
|
182
|
-
[EXPERIMENT_QUERY_PARAM_KEY]:
|
|
183
|
-
IsUserIncludedResponse.entity.experiments[0].variant.name
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
expect(instance.getVariantAsQueryParam(URL_WITH_EXPERIMENT)).toStrictEqual(expected2);
|
|
187
|
-
|
|
188
|
-
const URL_NO_EXPERIMENT = '/destinations';
|
|
189
|
-
|
|
190
|
-
const expected3 = new URLSearchParams({});
|
|
191
|
-
|
|
192
|
-
expect(instance.getVariantAsQueryParam(URL_NO_EXPERIMENT)).toStrictEqual(expected3);
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
describe('Class interactions', () => {
|
|
197
|
-
beforeEach(() => {
|
|
198
|
-
fetchMock.restore();
|
|
199
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
200
|
-
(DotExperiments as any).instance = null;
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it('should simulate the changes of the data in first run, after 5 days and 15 days.', async () => {
|
|
204
|
-
// First time the user enter to the page
|
|
205
|
-
|
|
206
|
-
fetchMock.post(`${configMock.server}/${API_EXPERIMENTS_URL}`, {
|
|
207
|
-
status: 200,
|
|
208
|
-
body: IsUserIncludedResponse,
|
|
209
|
-
headers: {
|
|
210
|
-
Accept: 'application/json',
|
|
211
|
-
'Content-Type': 'application/json'
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
const instance = DotExperiments.getInstance(configMock);
|
|
216
|
-
|
|
217
|
-
const spyTrackPageView = jest.spyOn(instance, 'trackPageView');
|
|
218
|
-
|
|
219
|
-
await instance.ready().then(() => {
|
|
220
|
-
const experiments = instance.experiments;
|
|
221
|
-
|
|
222
|
-
expect(experiments.length).toBe(1);
|
|
223
|
-
expect(experiments).toEqual(MockDataStoredIndexDB);
|
|
224
|
-
|
|
225
|
-
expect(spyTrackPageView).toBeCalledTimes(1);
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
// Second time the user enter to the page
|
|
229
|
-
// change the time 5 days later
|
|
230
|
-
jest.spyOn(Date, 'now').mockImplementation(
|
|
231
|
-
() => MOCK_CURRENT_TIMESTAMP + TIME_5_DAYS_MILLISECONDS
|
|
232
|
-
);
|
|
233
|
-
|
|
234
|
-
fetchMock.post(
|
|
235
|
-
`${configMock.server}/${API_EXPERIMENTS_URL}`,
|
|
236
|
-
{
|
|
237
|
-
status: 200,
|
|
238
|
-
body: NewIsUserIncludedResponse,
|
|
239
|
-
headers: {
|
|
240
|
-
Accept: 'application/json',
|
|
241
|
-
'Content-Type': 'application/json'
|
|
242
|
-
}
|
|
243
|
-
},
|
|
244
|
-
{ overwriteRoutes: true }
|
|
245
|
-
);
|
|
246
|
-
|
|
247
|
-
await instance.locationChanged(LocationMock).then(() => {
|
|
248
|
-
// get the experiments stored in the indexDB
|
|
249
|
-
const experiments = instance.experiments;
|
|
250
|
-
|
|
251
|
-
expect(experiments.length).toBe(1);
|
|
252
|
-
expect(experiments).toEqual(MockDataStoredIndexDBWithNew);
|
|
253
|
-
expect(spyTrackPageView).toBeCalledTimes(2);
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
fetchMock.post(
|
|
257
|
-
`${configMock.server}/${API_EXPERIMENTS_URL}`,
|
|
258
|
-
{
|
|
259
|
-
status: 200,
|
|
260
|
-
body: After15DaysIsUserIncludedResponse,
|
|
261
|
-
headers: {
|
|
262
|
-
Accept: 'application/json',
|
|
263
|
-
'Content-Type': 'application/json'
|
|
264
|
-
}
|
|
265
|
-
},
|
|
266
|
-
{ overwriteRoutes: true }
|
|
267
|
-
);
|
|
268
|
-
|
|
269
|
-
// Third try, after 15 days
|
|
270
|
-
const location = { ...LocationMock, href: 'http://localhost/destinations' };
|
|
271
|
-
|
|
272
|
-
jest.spyOn(Date, 'now').mockImplementation(
|
|
273
|
-
() => MOCK_CURRENT_TIMESTAMP + TIME_15_DAYS_MILLISECONDS
|
|
274
|
-
);
|
|
275
|
-
await instance.locationChanged(location).then(() => {
|
|
276
|
-
// get the experiments stored in the indexDB
|
|
277
|
-
const experiments = instance.experiments;
|
|
278
|
-
|
|
279
|
-
expect(experiments.length).toBe(1);
|
|
280
|
-
expect(experiments).toEqual(MockDataStoredIndexDBWithNew15DaysLater);
|
|
281
|
-
expect(spyTrackPageView).toBeCalledTimes(3);
|
|
282
|
-
});
|
|
283
|
-
});
|
|
284
|
-
});
|
|
285
|
-
});
|