Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"bugs": "https://github.com/splitio/javascript-browser-client/issues",
"homepage": "https://github.com/splitio/javascript-browser-client#readme",
"dependencies": {
"@splitsoftware/splitio-commons": "2.8.0",
"@splitsoftware/splitio-commons": "2.8.1-rc.1",
"tslib": "^2.3.1",
"unfetch": "^4.2.0"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { SplitFactory } from '../..';
import { settingsFactory } from '../../settings';
import splitChangesMock1 from '../mocks/splitchanges.since.-1.json';
import { url } from '../testUtils';

const baseUrls = {
sdk: 'https://sdk.baseurl/evaluationsImpressionsDisabledSuite',
events: 'https://events.baseurl/evaluationsImpressionsDisabledSuite',
telemetry: 'https://telemetry.baseurl/evaluationsImpressionsDisabledSuite'
};

const settings = settingsFactory({
core: {
key: '<fake id>'
},
urls: baseUrls,
streamingEnabled: false
});

export default async function (configInMemory, configInLocalStorage, fetchMock, assert) {

assert.test('Evaluations / impressionsDisabled option', async t => {
// Mocking split changes
fetchMock.getOnce(url(settings, '/splitChanges?s=1.3&since=-1&rbSince=-1'), { status: 200, body: splitChangesMock1 });
fetchMock.get(new RegExp(`${url(settings, '/segmentChanges/')}.*`), { status: 200, body: { since: 10, till: 10, name: 'segmentName', added: [], removed: [] } });
fetchMock.post(url(settings, '/v1/keys/ss'), 200);
fetchMock.post(url(settings, '/v1/metrics/usage'), 200);
fetchMock.post(url(settings, '/v1/metrics/config'), 200);
// Mock default telemetry URLs as fallback
fetchMock.post('https://telemetry.split.io/api/v1/keys/ss', 200);
fetchMock.post('https://telemetry.split.io/api/v1/metrics/usage', 200);
fetchMock.post('https://telemetry.split.io/api/v1/metrics/usage', 200);
fetchMock.post('https://telemetry.split.io/api/v1/metrics/config', 200);

fetchMock.post(url(settings, '/testImpressions/bulk'), 200);
fetchMock.post(url(settings, '/testImpressions/count'), 200);

const splitio = SplitFactory(configInMemory);
const client = splitio.client();

await client.ready();

// getTreatment
t.equal(client.getTreatment('split_with_config', { impressionsDisabled: true }), 'o.n', 'getTreatment with impressionsDisabled: true returns correct treatment');
t.equal(client.getTreatment('split_with_config', { impressionsDisabled: false }), 'o.n', 'getTreatment with impressionsDisabled: false returns correct treatment');

// getTreatments
t.deepEqual(client.getTreatments(['split_with_config', 'whitelist'], { impressionsDisabled: true }), {
split_with_config: 'o.n',
whitelist: 'allowed'
}, 'getTreatments with impressionsDisabled: true returns correct treatments');

// getTreatmentWithConfig
const expectedConfig = '{"color":"brown","dimensions":{"height":12,"width":14},"text":{"inner":"click me"}}';
t.deepEqual(client.getTreatmentWithConfig('split_with_config', { impressionsDisabled: true }), {
treatment: 'o.n',
config: expectedConfig
}, 'getTreatmentWithConfig with impressionsDisabled: true returns correct treatment and config');

// getTreatmentsWithConfig
t.deepEqual(client.getTreatmentsWithConfig(['split_with_config', 'whitelist'], { impressionsDisabled: true }), {
split_with_config: { treatment: 'o.n', config: expectedConfig },
whitelist: { treatment: 'allowed', config: null }
}, 'getTreatmentsWithConfig with impressionsDisabled: true returns correct treatments and configs');

await client.destroy();
t.end();
});

}
14 changes: 13 additions & 1 deletion src/__tests__/browserSuites/impressions-listener.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export default function (assert) {
client2.getTreatment('qc_team');
client2.getTreatmentWithConfig('qc_team'); // Validate that the impression is the same.
client3.getTreatment('qc_team', testAttrs);
client.getTreatment('whitelist', testAttrs, { impressionsDisabled: true });

setTimeout(() => {
const secondImpression = {
Expand All @@ -62,7 +63,7 @@ export default function (assert) {
properties: undefined
};

assert.equal(listener.logImpression.callCount, 4, 'Impression listener logImpression method should be called after we call client.getTreatment, once per each impression generated.');
assert.equal(listener.logImpression.callCount, 5, 'Impression listener logImpression method should be called after we call client.getTreatment, once per each impression generated.');
assert.true(listener.logImpression.getCall(0).calledWithExactly({
impression: {
feature: 'hierarchical_splits_test',
Expand Down Expand Up @@ -98,6 +99,17 @@ export default function (assert) {
attributes: testAttrs,
...metaData
}));
assert.true(listener.logImpression.getCall(4).calledWithMatch({
impression: {
feature: 'whitelist',
keyName: 'nicolas@split.io',
treatment: 'not_allowed',
bucketingKey: undefined,
label: 'default rule',
},
attributes: testAttrs,
...metaData
}));

client3.destroy();
client2.destroy();
Expand Down
28 changes: 26 additions & 2 deletions src/__tests__/browserSuites/impressions.debug.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ export default function (fetchMock, assert) {
}, {
k: 'facundo@split.io', t: 'o.n', m: data[0].i[2].m, c: 828282828282, r: 'another expected label', pt: data[0].i[1].m
}]
}, {
f: 'whitelist',
i: [{
k: 'facundo@split.io', t: 'allowed', m: data[1].i[0].m, r: 'default rule', properties: '{"prop1":"value2"}'
}]
}]);

client.destroy().then(() => {
Expand All @@ -73,15 +78,20 @@ export default function (fetchMock, assert) {

fetchMock.postOnce(url(settings, '/testImpressions/count'), (url, opts) => {
assert.deepEqual(JSON.parse(opts.body), {
pf: [{ f: 'always_on_impressions_disabled_true', m: truncatedTimeFrame, rc: 1 }]
pf: [
{ f: 'always_on_impressions_disabled_true', m: truncatedTimeFrame, rc: 3 },
{ f: 'whitelist', m: truncatedTimeFrame, rc: 3 }
]
}, 'We should generate impression count for the feature with track impressions disabled.');

return 200;
});

fetchMock.postOnce(url(settings, '/v1/keys/cs'), (url, opts) => {
assert.deepEqual(JSON.parse(opts.body), {
keys: [{ fs: ['always_on_impressions_disabled_true'], k: 'facundo@split.io' }]
keys: [
{ k: 'facundo@split.io', fs: ['always_on_impressions_disabled_true', 'whitelist'] }
]
}, 'We should track unique keys for the feature with track impressions disabled.');

return 200;
Expand All @@ -94,5 +104,19 @@ export default function (fetchMock, assert) {
client.getTreatment('split_with_config');
client.getTreatment('split_with_config');
assert.equal(client.getTreatment('always_on_impressions_disabled_true'), 'on');

// impressions disabled
// Flags with impression enabled should generate:
// - 1 impression for whitelist
// - 3 impressions count for whitelist
// - 2 impressions unique keys for whitelist
assert.equal(client.getTreatment('whitelist', undefined, { impressionsDisabled: true, properties: { prop1: 'value1' } }), 'allowed');
assert.equal(client.getTreatments(['whitelist'], undefined, { properties: { prop1: 'value2' } }).whitelist, 'allowed');
assert.equal(client.getTreatmentWithConfig('whitelist', undefined, { impressionsDisabled: true, properties: { prop1: 'value3' } }).treatment, 'allowed');
assert.equal(client.getTreatmentsWithConfig(['whitelist'], undefined, { impressionsDisabled: true, properties: { prop1: 'value4' } }).whitelist.treatment, 'allowed');

// Flags with impression disabled should only generate impressions count and unique keys
assert.equal(client.getTreatment('always_on_impressions_disabled_true', undefined, { impressionsDisabled: true }), 'on');
assert.equal(client.getTreatment('always_on_impressions_disabled_true', undefined, { impressionsDisabled: false }), 'on');
});
}
13 changes: 10 additions & 3 deletions src/__tests__/browserSuites/impressions.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,12 @@ export default function (fetchMock, assert) {
const assertPayload = req => {
const resp = JSON.parse(req.body);

assert.equal(resp.length, 2, 'We performed evaluations for 3 features, but one with `impressionsDisabled` true, so we should have 2 items total');
assert.equal(resp.length, 2, 'We performed evaluations for 4 features, but two with `impressionsDisabled` true, so we should have 2 items total');

const dependencyChildImpr = resp.filter(e => e.f === 'hierarchical_splits_test')[0];
const splitWithConfigImpr = resp.filter(e => e.f === 'split_with_config')[0];
const alwaysOnWithImpressionsDisabledTrue = resp.filter(e => e.f === 'always_on_impressions_disabled_true');
const whitelist = resp.filter(e => e.f === 'whitelist');

assert.true(dependencyChildImpr, 'Split we wanted to evaluate should be present on the impressions.');
assert.false(resp.some(e => e.f === 'hierarchical_dep_always_on'), 'Parent split evaluations should not result in impressions.');
Expand All @@ -62,6 +63,7 @@ export default function (fetchMock, assert) {
assert.false(Object.prototype.hasOwnProperty.call(splitWithConfigImpr.i[0], 'configuration'), 'Impressions do not change with configuration evaluations.');
assert.false(Object.prototype.hasOwnProperty.call(splitWithConfigImpr.i[0], 'config'), 'Impressions do not change with configuration evaluations.');
assert.equal(alwaysOnWithImpressionsDisabledTrue.length, 0);
assert.equal(whitelist.length, 0);

const {
k,
Expand Down Expand Up @@ -96,25 +98,29 @@ export default function (fetchMock, assert) {
fetchMock.postOnce(url(settings, '/testImpressions/count'), (url, opts) => {
const data = JSON.parse(opts.body);

assert.equal(data.pf.length, 2, 'We should generate impressions count for 2 features.');
assert.equal(data.pf.length, 3, 'We should generate impressions count for 3 features.');

// finding these validate the feature names collection too
const splitWithConfigImpr = data.pf.filter(e => e.f === 'split_with_config')[0];
const alwaysOnWithImpressionsDisabledTrue = data.pf.filter(e => e.f === 'always_on_impressions_disabled_true')[0];
const whitelist = data.pf.filter(e => e.f === 'whitelist')[0];

assert.equal(splitWithConfigImpr.rc, 2);
assert.equal(typeof splitWithConfigImpr.m, 'number');
assert.equal(splitWithConfigImpr.m, truncatedTimeFrame);
assert.equal(alwaysOnWithImpressionsDisabledTrue.rc, 1);
assert.equal(typeof alwaysOnWithImpressionsDisabledTrue.m, 'number');
assert.equal(alwaysOnWithImpressionsDisabledTrue.m, truncatedTimeFrame);
assert.equal(whitelist.rc, 1);
assert.equal(typeof whitelist.m, 'number');
assert.equal(whitelist.m, truncatedTimeFrame);

return 200;
});

fetchMock.postOnce(url(settings, '/v1/keys/cs'), (url, opts) => {
assert.deepEqual(JSON.parse(opts.body), {
keys: [{ fs: [ 'always_on_impressions_disabled_true' ], k: 'facundo@split.io' }]
keys: [ { k: 'facundo@split.io', fs: [ 'whitelist', 'always_on_impressions_disabled_true' ]}]
}, 'We should only track unique keys for features flags with track impressions disabled.');

return 200;
Expand All @@ -130,6 +136,7 @@ export default function (fetchMock, assert) {
}, 'We should get an evaluation as always.');
client.getTreatmentWithConfig('split_with_config');
client.getTreatmentWithConfig('split_with_config');
client.getTreatmentWithConfig('whitelist', undefined, { impressionsDisabled: true });

// Impression should not be tracked
assert.equal(client.getTreatment('always_on_impressions_disabled_true'), 'on');
Expand Down
19 changes: 16 additions & 3 deletions src/__tests__/consumer/browser_consumer_partial.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const expectedSplitView = { name: 'hierarchical_splits_testing_on', trafficType:

const wrapperPrefix = 'PLUGGABLE_STORAGE_UT';
const wrapperInstance = inMemoryWrapperFactory();
const TOTAL_RAW_IMPRESSIONS = 17;
const TOTAL_RAW_IMPRESSIONS = 21;
const TOTAL_EVENTS = 5;

/** @type SplitIO.IBrowserAsyncSettings */
Expand Down Expand Up @@ -48,7 +48,7 @@ tape('Browser Consumer Partial mode with pluggable storage', function (t) {
const resp = JSON.parse(req.body);
assert.equal(resp.reduce((prev, cur) => {
return prev + cur.i.length;
}, 0), TOTAL_RAW_IMPRESSIONS - 1, 'Impressions were deduped');
}, 0), TOTAL_RAW_IMPRESSIONS - 5, 'Impressions were deduped');
return 200;
});

Expand All @@ -58,7 +58,7 @@ tape('Browser Consumer Partial mode with pluggable storage', function (t) {
assert.deepEqual(data, {
keys: [{
k: 'UT_Segment_member',
fs: ['always-on-impressions-disabled-true']
fs: ['always-on-impressions-disabled-true', 'always-on']
}]
}, 'Unique keys for the evaluation with impressions disabled true.');

Expand Down Expand Up @@ -153,6 +153,12 @@ tape('Browser Consumer Partial mode with pluggable storage', function (t) {
assert.equal(await client.getTreatment('hierarchical_splits_testing_on_negated'), 'off', 'Evaluations using pluggable storage should be correct.');
assert.equal(await client.getTreatment('always-on-impressions-disabled-true'), 'on', 'Evaluations using pluggable storage should be correct.');

// Verify impressionsDisabled option
assert.deepEqual(await client.getTreatment('always-on', undefined, { impressionsDisabled: true }), 'on', 'Evaluations with impressionsDisabled: true should be correct.');
assert.deepEqual(await client.getTreatmentWithConfig('always-on', undefined, { impressionsDisabled: true }), { treatment: 'on', config: null }, 'Evaluations with impressionsDisabled: true should be correct.');
assert.deepEqual(await client.getTreatments(['always-on'], undefined, { impressionsDisabled: true }), { 'always-on': 'on' }, 'Evaluations with impressionsDisabled: true should be correct.');
assert.deepEqual(await client.getTreatmentsWithConfig(['always-on'], undefined, { impressionsDisabled: true }), { 'always-on': { treatment: 'on', config: null } }, 'Evaluations with impressionsDisabled: true should be correct.');

assert.equal(typeof client.track('user', 'test.event', 18).then, 'function', 'Track calls should always return a promise on Consumer mode.');
assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Consumer mode, even when parameters are incorrect.');

Expand Down Expand Up @@ -311,6 +317,13 @@ tape('Browser Consumer Partial mode with pluggable storage', function (t) {
assert.equal(await client.getTreatment('hierarchical_splits_testing_off'), 'off', 'Evaluations using pluggable storage should be correct.');
assert.equal(await client.getTreatment('hierarchical_splits_testing_on_negated'), 'off', 'Evaluations using pluggable storage should be correct.');


// Verify impressionsDisabled option
assert.deepEqual(await client.getTreatment('always-on', undefined, { impressionsDisabled: true }), 'on', 'Evaluations with impressionsDisabled: true should be correct.');
assert.deepEqual(await client.getTreatmentWithConfig('always-on', undefined, { impressionsDisabled: true }), { treatment: 'on', config: null }, 'Evaluations with impressionsDisabled: true should be correct.');
assert.deepEqual(await client.getTreatments(['always-on'], undefined, { impressionsDisabled: true }), { 'always-on': 'on' }, 'Evaluations with impressionsDisabled: true should be correct.');
assert.deepEqual(await client.getTreatmentsWithConfig(['always-on'], undefined, { impressionsDisabled: true }), { 'always-on': { treatment: 'on', config: null } }, 'Evaluations with impressionsDisabled: true should be correct.');

assert.equal(typeof client.track('user', 'test.event', 18).then, 'function', 'Track calls should always return a promise on Consumer mode.');
assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Consumer mode, even when parameters are incorrect.');

Expand Down
3 changes: 3 additions & 0 deletions src/__tests__/online/browser.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import membershipsMarcio from '../mocks/memberships.marcio@split.io.json';
import membershipsEmmanuel from '../mocks/memberships.emmanuel@split.io.json';
import { InLocalStorage } from '../../index';
import evaluationsFallbackSuite from '../browserSuites/evaluations-fallback.spec';
import evaluationsImpressionsDisabledSuite from '../browserSuites/evaluations-impressionsDisabled.spec';

const settings = settingsFactory({
core: {
Expand Down Expand Up @@ -106,6 +107,8 @@ tape('## E2E CI Tests ##', function (assert) {
assert.test('E2E / Impressions Debug Mode', impressionsSuiteDebug.bind(null, fetchMock));
/* Check impression listener */
assert.test('E2E / Impression listener', impressionsListenerSuite);
/* Check impressions disabled */
assert.test('E2E / Impressions Disabled', evaluationsImpressionsDisabledSuite.bind(null, configInMemory, configInLocalStorage, fetchMock));
/* Check telemetry */
assert.test('E2E / Telemetry', telemetrySuite.bind(null, fetchMock));
/* Check events */
Expand Down