Skip to content

Commit bad1b41

Browse files
Merge pull request #139 from splitio/development
Release v1.1.0
2 parents 721a80a + 883f021 commit bad1b41

17 files changed

+176
-94
lines changed

.github/workflows/ci-cd.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ jobs:
4848

4949
- name: Store assets
5050
if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/development' || github.ref == 'refs/heads/main') }}
51-
uses: actions/upload-artifact@v3
51+
uses: actions/upload-artifact@v4
5252
with:
5353
name: assets
5454
path: umd/
@@ -74,7 +74,7 @@ jobs:
7474

7575
steps:
7676
- name: Download assets
77-
uses: actions/download-artifact@v3
77+
uses: actions/download-artifact@v4
7878
with:
7979
name: assets
8080
path: umd
@@ -84,7 +84,7 @@ jobs:
8484
working-directory: umd
8585

8686
- name: Configure AWS credentials
87-
uses: aws-actions/configure-aws-credentials@v1-node16
87+
uses: aws-actions/configure-aws-credentials@v4
8888
with:
8989
role-to-assume: arn:aws:iam::${{ matrix.account_id }}:role/gha-public-assets-role
9090
aws-region: us-east-1
@@ -117,7 +117,7 @@ jobs:
117117

118118
steps:
119119
- name: Download assets
120-
uses: actions/download-artifact@v3
120+
uses: actions/download-artifact@v4
121121
with:
122122
name: assets
123123
path: umd
@@ -127,7 +127,7 @@ jobs:
127127
working-directory: umd
128128

129129
- name: Configure AWS credentials
130-
uses: aws-actions/configure-aws-credentials@v1-node16
130+
uses: aws-actions/configure-aws-credentials@v4
131131
with:
132132
role-to-assume: arn:aws:iam::${{ matrix.account_id }}:role/gha-public-assets-role
133133
aws-region: us-east-1

CHANGES.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
1.1.0 (January 17, 2025)
2+
- Added support for the new impressions tracking toggle available on feature flags, both respecting the setting and including the new field being returned on `SplitView` type objects. Read more in our docs.
3+
- Bugfixing - Updated @splitsoftware/splitio-commons package to version 2.1.0, which properly handles rejected promises when using targeting rules with segment matchers in consumer modes (e.g., Redis and Pluggable storages).
4+
15
1.0.1 (November 11, 2024)
26
- Bugfixing - Revert removal of TypeScript `SplitIO` namespace at `/types/splitio.d.ts` to allow explicit imports of types from the Browser SDK package. E.g., `import type { IClientSideSettings } from '@splitsoftware/splitio-browserjs/types/splitio';`.
37

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright © 2024 Split Software, Inc.
1+
Copyright © 2025 Split Software, Inc.
22

33
Licensed under the Apache License, Version 2.0 (the "License");
44
you may not use this file except in compliance with the License.

package-lock.json

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

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@splitsoftware/splitio-browserjs",
3-
"version": "1.0.1",
3+
"version": "1.1.0",
44
"description": "Split SDK for JavaScript on Browser",
55
"main": "cjs/index.js",
66
"module": "esm/index.js",
@@ -59,7 +59,7 @@
5959
"bugs": "https://github.com/splitio/javascript-browser-client/issues",
6060
"homepage": "https://github.com/splitio/javascript-browser-client#readme",
6161
"dependencies": {
62-
"@splitsoftware/splitio-commons": "2.0.0",
62+
"@splitsoftware/splitio-commons": "2.1.0",
6363
"tslib": "^2.3.1",
6464
"unfetch": "^4.2.0"
6565
},

src/__tests__/browserSuites/impressions-listener.spec.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,21 @@ export default function (assert) {
5757
keyName: 'marcio@split.io',
5858
treatment: 'no',
5959
bucketingKey: 'impr_bucketing_2',
60-
label: 'default rule'
60+
label: 'default rule',
61+
pt: undefined
6162
};
6263

6364
assert.equal(listener.logImpression.callCount, 4, 'Impression listener logImpression method should be called after we call client.getTreatment, once per each impression generated.');
64-
assert.true(listener.logImpression.getCall(0).calledWithMatch({
65+
assert.true(listener.logImpression.getCall(0).calledWithExactly({
6566
impression: {
6667
feature: 'hierarchical_splits_test',
6768
keyName: 'nicolas@split.io',
6869
treatment: 'on',
70+
time: listener.logImpression.getCall(0).args[0].impression.time,
6971
bucketingKey: undefined,
7072
label: 'expected label',
73+
changeNumber: 2828282828,
74+
pt: undefined
7175
},
7276
attributes: undefined,
7377
...metaData

src/__tests__/browserSuites/impressions.debug.spec.js

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import splitChangesMock1 from '../mocks/splitchanges.since.-1.json';
44
import splitChangesMock2 from '../mocks/splitchanges.since.1457552620999.json';
55
import membershipsFacundo from '../mocks/memberships.facundo@split.io.json';
66
import { DEBUG } from '@splitsoftware/splitio-commons/src/utils/constants';
7+
import { truncateTimeFrame } from '@splitsoftware/splitio-commons/src/utils/time';
78
import { url } from '../testUtils';
89

910
const baseUrls = {
@@ -19,6 +20,8 @@ const settings = settingsFactory({
1920
streamingEnabled: false
2021
});
2122

23+
let truncatedTimeFrame;
24+
2225
export default function (fetchMock, assert) {
2326
// Mocking this specific route to make sure we only get the items we want to test from the handlers.
2427
fetchMock.getOnce(url(settings, '/splitChanges?s=1.2&since=-1'), { status: 200, body: splitChangesMock1 });
@@ -47,41 +50,21 @@ export default function (fetchMock, assert) {
4750
});
4851

4952
const client = splitio.client();
50-
const assertPayload = req => {
51-
const resp = JSON.parse(req.body);
52-
53-
assert.equal(resp.length, 1, 'We performed three evaluations so we should have 1 impressions type');
54-
55-
const alwaysOnWithConfigImpr = resp.filter(e => e.f === 'split_with_config')[0];
56-
57-
assert.equal(alwaysOnWithConfigImpr.i.length, 3);
58-
59-
function validateImpressionData(output, expected) {
60-
assert.equal(output.k, expected.keyName, 'Present impressions should have the correct key.');
61-
assert.equal(output.b, expected.bucketingKey, 'Present impressions should have the correct bucketingKey.');
62-
assert.equal(output.t, expected.treatment, 'Present impressions should have the correct treatment.');
63-
assert.equal(output.r, expected.label, 'Present impressions should have the correct label.');
64-
assert.equal(output.c, expected.changeNumber, 'Present impressions should have the correct changeNumber.');
65-
assert.equal(output.pt, expected.pt, 'Present impressions should have the correct previousTime.');
66-
}
67-
68-
validateImpressionData(alwaysOnWithConfigImpr.i[0], {
69-
keyName: 'facundo@split.io', label: 'another expected label', treatment: 'o.n',
70-
bucketingKey: undefined, changeNumber: 828282828282, pt: undefined
71-
});
72-
validateImpressionData(alwaysOnWithConfigImpr.i[1], {
73-
keyName: 'facundo@split.io', label: 'another expected label', treatment: 'o.n',
74-
bucketingKey: undefined, changeNumber: 828282828282, pt: alwaysOnWithConfigImpr.i[0].m
75-
});
76-
validateImpressionData(alwaysOnWithConfigImpr.i[2], {
77-
keyName: 'facundo@split.io', label: 'another expected label', treatment: 'o.n',
78-
bucketingKey: undefined, changeNumber: 828282828282, pt: alwaysOnWithConfigImpr.i[1].m
79-
});
80-
};
8153

8254
fetchMock.postOnce(url(settings, '/testImpressions/bulk'), (url, req) => {
8355
assert.equal(req.headers.SplitSDKImpressionsMode, DEBUG);
84-
assertPayload(req);
56+
const data = JSON.parse(req.body);
57+
58+
assert.deepEqual(data, [{
59+
f: 'split_with_config',
60+
i: [{
61+
k: 'facundo@split.io', t: 'o.n', m: data[0].i[0].m, c: 828282828282, r: 'another expected label'
62+
}, {
63+
k: 'facundo@split.io', t: 'o.n', m: data[0].i[1].m, c: 828282828282, r: 'another expected label', pt: data[0].i[0].m,
64+
}, {
65+
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
66+
}]
67+
}]);
8568

8669
client.destroy().then(() => {
8770
assert.end();
@@ -90,9 +73,28 @@ export default function (fetchMock, assert) {
9073
return 200;
9174
});
9275

76+
fetchMock.postOnce(url(settings, '/testImpressions/count'), (url, opts) => {
77+
assert.deepEqual(JSON.parse(opts.body), {
78+
pf: [{ f: 'always_on_track_impressions_false', m: truncatedTimeFrame, rc: 1 }]
79+
}, 'We should generate impression count for the feature with track impressions disabled.');
80+
81+
return 200;
82+
});
83+
84+
fetchMock.postOnce(url(settings, '/v1/keys/cs'), (url, opts) => {
85+
assert.deepEqual(JSON.parse(opts.body), {
86+
keys: [{ fs: ['always_on_track_impressions_false'], k: 'facundo@split.io' }]
87+
}, 'We should track unique keys for the feature with track impressions disabled.');
88+
89+
return 200;
90+
});
91+
9392
client.ready().then(() => {
93+
truncatedTimeFrame = truncateTimeFrame(Date.now());
94+
9495
client.getTreatment('split_with_config');
9596
client.getTreatment('split_with_config');
9697
client.getTreatment('split_with_config');
98+
assert.equal(client.getTreatment('always_on_track_impressions_false'), 'on');
9799
});
98100
}

src/__tests__/browserSuites/impressions.spec.js

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,19 @@ export default function (fetchMock, assert) {
4949
const assertPayload = req => {
5050
const resp = JSON.parse(req.body);
5151

52-
assert.equal(resp.length, 2, 'We performed three evaluations so we should have 2 impressions type');
52+
assert.equal(resp.length, 2, 'We performed evaluations for 3 features, but one with `impressionsDisabled` true, so we should have 2 items total');
5353

5454
const dependencyChildImpr = resp.filter(e => e.f === 'hierarchical_splits_test')[0];
55-
const alwaysOnWithConfigImpr = resp.filter(e => e.f === 'split_with_config')[0];
55+
const splitWithConfigImpr = resp.filter(e => e.f === 'split_with_config')[0];
56+
const alwaysOnWithTrackImpressionsFalse = resp.filter(e => e.f === 'always_on_track_impressions_false');
5657

5758
assert.true(dependencyChildImpr, 'Split we wanted to evaluate should be present on the impressions.');
5859
assert.false(resp.some(e => e.f === 'hierarchical_dep_always_on'), 'Parent split evaluations should not result in impressions.');
5960
assert.false(resp.some(e => e.f === 'hierarchical_dep_hierarchical'), 'No matter how deep is the chain.');
60-
assert.true(alwaysOnWithConfigImpr, 'Split evaluated with config should have generated an impression too.');
61-
assert.false(Object.prototype.hasOwnProperty.call(alwaysOnWithConfigImpr.i[0], 'configuration'), 'Impressions do not change with configuration evaluations.');
62-
assert.false(Object.prototype.hasOwnProperty.call(alwaysOnWithConfigImpr.i[0], 'config'), 'Impressions do not change with configuration evaluations.');
61+
assert.true(splitWithConfigImpr, 'Split evaluated with config should have generated an impression too.');
62+
assert.false(Object.prototype.hasOwnProperty.call(splitWithConfigImpr.i[0], 'configuration'), 'Impressions do not change with configuration evaluations.');
63+
assert.false(Object.prototype.hasOwnProperty.call(splitWithConfigImpr.i[0], 'config'), 'Impressions do not change with configuration evaluations.');
64+
assert.equal(alwaysOnWithTrackImpressionsFalse.length, 0);
6365

6466
const {
6567
k,
@@ -94,18 +96,26 @@ export default function (fetchMock, assert) {
9496
fetchMock.postOnce(url(settings, '/testImpressions/count'), (url, opts) => {
9597
const data = JSON.parse(opts.body);
9698

97-
assert.equal(data.pf.length, 1, 'We should generate impressions count for one feature.');
99+
assert.equal(data.pf.length, 2, 'We should generate impressions count for 2 features.');
98100

99101
// finding these validate the feature names collection too
100-
const dependencyChildImpr = data.pf.filter(e => e.f === 'hierarchical_splits_test')[0];
101-
const alwaysOnWithConfigImpr = data.pf.filter(e => e.f === 'split_with_config')[0];
102+
const splitWithConfigImpr = data.pf.filter(e => e.f === 'split_with_config')[0];
103+
const alwaysOnWithTrackImpressionsFalse = data.pf.filter(e => e.f === 'always_on_track_impressions_false')[0];
102104

103-
assert.equal(dependencyChildImpr.rc, 1);
104-
assert.equal(typeof dependencyChildImpr.m, 'number');
105-
assert.equal(dependencyChildImpr.m, truncatedTimeFrame);
106-
assert.equal(alwaysOnWithConfigImpr.rc, 3);
107-
assert.equal(typeof alwaysOnWithConfigImpr.m, 'number');
108-
assert.equal(alwaysOnWithConfigImpr.m, truncatedTimeFrame);
105+
assert.equal(splitWithConfigImpr.rc, 2);
106+
assert.equal(typeof splitWithConfigImpr.m, 'number');
107+
assert.equal(splitWithConfigImpr.m, truncatedTimeFrame);
108+
assert.equal(alwaysOnWithTrackImpressionsFalse.rc, 1);
109+
assert.equal(typeof alwaysOnWithTrackImpressionsFalse.m, 'number');
110+
assert.equal(alwaysOnWithTrackImpressionsFalse.m, truncatedTimeFrame);
111+
112+
return 200;
113+
});
114+
115+
fetchMock.postOnce(url(settings, '/v1/keys/cs'), (url, opts) => {
116+
assert.deepEqual(JSON.parse(opts.body), {
117+
keys: [{ fs: [ 'always_on_track_impressions_false' ], k: 'facundo@split.io' }]
118+
}, 'We should only track unique keys for features flags with track impressions disabled.');
109119

110120
return 200;
111121
});
@@ -120,5 +130,8 @@ export default function (fetchMock, assert) {
120130
}, 'We should get an evaluation as always.');
121131
client.getTreatmentWithConfig('split_with_config');
122132
client.getTreatmentWithConfig('split_with_config');
133+
134+
// Impression should not be tracked
135+
assert.equal(client.getTreatment('always_on_track_impressions_false'), 'on');
123136
});
124137
}

src/__tests__/browserSuites/manager.spec.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ export default async function (settings, fetchMock, assert) {
4141
'treatments': map(mockSplits.splits[index].conditions[0].partitions, partition => partition.treatment),
4242
'configs': mockSplits.splits[index].configurations || {},
4343
'sets': mockSplits.splits[index].sets,
44-
'defaultTreatment': mockSplits.splits[index].defaultTreatment
44+
'defaultTreatment': mockSplits.splits[index].defaultTreatment,
45+
'impressionsDisabled': false
4546
});
4647

4748
assert.equal(manager.split('non_existent'), null, 'Trying to get a manager.split() of a Split that does not exist returns null.');

src/__tests__/browserSuites/telemetry.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export default async function telemetryBrowserSuite(fetchMock, assert) {
7171

7272
// @TODO check if iDe value is correct
7373
assert.deepEqual(data, {
74-
mE: {}, hE: { sp: { 500: 1 }, ms: { 500: 1 } }, tR: 0, aR: 0, iQ: 4, iDe: 1, iDr: 0, spC: 32, seC: 1, skC: 1, eQ: 1, eD: 0, sE: [], t: [], ufs: {}
74+
mE: {}, hE: { sp: { 500: 1 }, ms: { 500: 1 } }, tR: 0, aR: 0, iQ: 4, iDe: 1, iDr: 0, spC: 33, seC: 1, skC: 1, eQ: 1, eD: 0, sE: [], t: [], ufs: {}
7575
}, 'metrics/usage JSON payload should be the expected');
7676

7777
finish.next();
@@ -91,7 +91,7 @@ export default async function telemetryBrowserSuite(fetchMock, assert) {
9191
// @TODO check if iDe value is correct
9292
assert.deepEqual(data, {
9393
mL: {}, mE: {}, hE: {}, hL: {}, // errors and latencies were popped
94-
tR: 0, aR: 0, iQ: 4, iDe: 1, iDr: 0, spC: 32, seC: 1, skC: 1, eQ: 1, eD: 0, sE: [], t: [], ufs: {}
94+
tR: 0, aR: 0, iQ: 4, iDe: 1, iDr: 0, spC: 33, seC: 1, skC: 1, eQ: 1, eD: 0, sE: [], t: [], ufs: {}
9595
}, '2nd metrics/usage JSON payload should be the expected');
9696
return 200;
9797
});

0 commit comments

Comments
 (0)