Skip to content
Open
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
116 changes: 116 additions & 0 deletions splitio_web/lib/splitio_web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -596,4 +596,120 @@ class SplitioWeb extends SplitioPlatform {

return result.toDart;
}

@override
Future<Map<String, dynamic>> getAllAttributes(
{required String matchingKey, required String? bucketingKey}) async {
final client = await _getClient(
matchingKey: matchingKey,
bucketingKey: bucketingKey,
);

final result = client.getAttributes.callAsFunction(null) as JSObject;

return jsObjectToMap(result);
}

@override
Future<bool> setAttribute(
{required String matchingKey,
required String? bucketingKey,
required String attributeName,
required dynamic value}) async {
final client = await _getClient(
matchingKey: matchingKey,
bucketingKey: bucketingKey,
);

final result = client.setAttribute.callAsFunction(
null, attributeName.toJS, _convertValue(value, true)) as JSBoolean;

return result.toDart;
}

@override
Future<bool> setAttributes(
{required String matchingKey,
required String? bucketingKey,
required Map<String, dynamic> attributes}) async {
final client = await _getClient(
matchingKey: matchingKey,
bucketingKey: bucketingKey,
);

final result = client.setAttributes
.callAsFunction(null, _convertMap(attributes, true)) as JSBoolean;

return result.toDart;
}

@override
Future<dynamic> getAttribute(
{required String matchingKey,
required String? bucketingKey,
required String attributeName}) async {
final client = await _getClient(
matchingKey: matchingKey,
bucketingKey: bucketingKey,
);

final result = client.getAttribute.callAsFunction(null, attributeName.toJS);

return jsAnyToDart(result);
}

@override
Future<bool> removeAttribute(
{required String matchingKey,
required String? bucketingKey,
required String attributeName}) async {
final client = await _getClient(
matchingKey: matchingKey,
bucketingKey: bucketingKey,
);

final result = client.removeAttribute
.callAsFunction(null, attributeName.toJS) as JSBoolean;

return result.toDart;
}

@override
Future<bool> clearAttributes(
{required String matchingKey, required String? bucketingKey}) async {
final client = await _getClient(
matchingKey: matchingKey,
bucketingKey: bucketingKey,
);

final result = client.clearAttributes.callAsFunction(null) as JSBoolean;

return result.toDart;
}

@override
Future<void> flush(
{required String matchingKey, required String? bucketingKey}) async {
final client = await _getClient(
matchingKey: matchingKey,
bucketingKey: bucketingKey,
);

final result = client.flush.callAsFunction(null) as JSPromise<Null>;

return result.toDart;
}

@override
Future<void> destroy(
{required String matchingKey, required String? bucketingKey}) async {
final client = await _getClient(
matchingKey: matchingKey,
bucketingKey: bucketingKey,
);

final result = client.destroy.callAsFunction(null) as JSPromise<Null>;

return result.toDart;
}
}
8 changes: 8 additions & 0 deletions splitio_web/lib/src/js_interop.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ extension type JS_IBrowserClient._(JSObject _) implements JSObject {
external JSFunction getTreatmentsWithConfigByFlagSet;
external JSFunction getTreatmentsWithConfigByFlagSets;
external JSFunction track;
external JSFunction setAttribute;
external JSFunction getAttribute;
external JSFunction removeAttribute;
external JSFunction setAttributes;
external JSFunction getAttributes;
external JSFunction clearAttributes;
external JSFunction flush;
external JSFunction destroy;
}

@JS()
Expand Down
124 changes: 117 additions & 7 deletions splitio_web/test/splitio_web_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ extension on web.Window {
}

void main() {

SplitioWeb _platform = SplitioWeb();
final mock = SplitioMock();

Expand All @@ -40,7 +39,8 @@ void main() {

expect(result, 'on');
expect(mock.calls.last.methodName, 'getTreatment');
expect(mock.calls.last.methodArguments.map(jsAnyToDart), ['split', {}, {}]);
expect(
mock.calls.last.methodArguments.map(jsAnyToDart), ['split', {}, {}]);
});

test('getTreatment with attributes', () async {
Expand Down Expand Up @@ -196,7 +196,8 @@ void main() {

expect(result.toString(), SplitResult('on', 'some-config').toString());
expect(mock.calls.last.methodName, 'getTreatmentWithConfig');
expect(mock.calls.last.methodArguments.map(jsAnyToDart), ['split1', {}, {}]);
expect(
mock.calls.last.methodArguments.map(jsAnyToDart), ['split1', {}, {}]);
});

test('getTreatmentsWithConfig without attributes', () async {
Expand Down Expand Up @@ -250,7 +251,8 @@ void main() {

expect(result, {'split1': 'on', 'split2': 'on'});
expect(mock.calls.last.methodName, 'getTreatmentsByFlagSet');
expect(mock.calls.last.methodArguments.map(jsAnyToDart), ['set_1', {}, {}]);
expect(
mock.calls.last.methodArguments.map(jsAnyToDart), ['set_1', {}, {}]);
});

test('getTreatmentsByFlagSet with attributes', () async {
Expand Down Expand Up @@ -314,7 +316,8 @@ void main() {
SplitResult('on', 'some-config').toString();
}));
expect(mock.calls.last.methodName, 'getTreatmentsWithConfigByFlagSet');
expect(mock.calls.last.methodArguments.map(jsAnyToDart), ['set_1', {}, {}]);
expect(
mock.calls.last.methodArguments.map(jsAnyToDart), ['set_1', {}, {}]);
});

test('getTreatmentsWithConfigByFlagSet with attributes', () async {
Expand Down Expand Up @@ -451,6 +454,111 @@ void main() {
});
});

group('other client methods: attributes, destroy, flush', () {
test('get single attribute', () async {
final result = await _platform.getAttribute(
matchingKey: 'matching-key',
bucketingKey: 'bucketing-key',
attributeName: 'attribute-name');

expect(result, 'attr-value');
expect(mock.calls.last.methodName, 'getAttribute');
expect(
mock.calls.last.methodArguments.map(jsAnyToDart), ['attribute-name']);
});

test('get all attributes', () async {
final result = await _platform.getAllAttributes(
matchingKey: 'matching-key', bucketingKey: 'bucketing-key');

expect(
result,
equals({
'attrBool': true,
'attrString': 'value',
'attrInt': 1,
'attrDouble': 1.1,
'attrList': ['value1', 100, false],
}));
expect(mock.calls.last.methodName, 'getAttributes');
expect(mock.calls.last.methodArguments, []);
});

test('set attribute', () async {
final result = await _platform.setAttribute(
matchingKey: 'matching-key',
bucketingKey: 'bucketing-key',
attributeName: 'my_attr',
value: 'attr_value');

expect(result, true);
expect(mock.calls.last.methodName, 'setAttribute');
expect(mock.calls.last.methodArguments.map(jsAnyToDart),
['my_attr', 'attr_value']);
});

test('set multiple attributes', () async {
final result = await _platform.setAttributes(
matchingKey: 'matching-key',
bucketingKey: 'bucketing-key',
attributes: {
'bool_attr': true,
'number_attr': 25.56,
'string_attr': 'attr-value',
'list_attr': ['one', true],
'attrNull': null, // not valid. ignored
'attrMap': {'value5': true} // not valid. ignored
});

expect(result, true);
expect(mock.calls.last.methodName, 'setAttributes');
expect(mock.calls.last.methodArguments.map(jsAnyToDart), [
{
'bool_attr': true,
'number_attr': 25.56,
'string_attr': 'attr-value',
'list_attr': ['one', true],
}
]);
});

test('remove attribute', () async {
final result = await _platform.removeAttribute(
matchingKey: 'matching-key',
bucketingKey: 'bucketing-key',
attributeName: 'attr-name');

expect(result, true);
expect(mock.calls.last.methodName, 'removeAttribute');
expect(mock.calls.last.methodArguments.map(jsAnyToDart), ['attr-name']);
});

test('clear attributes', () async {
final result = await _platform.clearAttributes(
matchingKey: 'matching-key', bucketingKey: 'bucketing-key');

expect(result, true);
expect(mock.calls.last.methodName, 'clearAttributes');
expect(mock.calls.last.methodArguments, []);
});

test('flush', () async {
await _platform.flush(
matchingKey: 'matching-key', bucketingKey: 'bucketing-key');

expect(mock.calls.last.methodName, 'flush');
expect(mock.calls.last.methodArguments, []);
});

test('destroy', () async {
await _platform.destroy(
matchingKey: 'matching-key', bucketingKey: 'bucketing-key');

expect(mock.calls.last.methodName, 'destroy');
expect(mock.calls.last.methodArguments, []);
});
});

group('initialization', () {
test('init with matching key only', () async {
SplitioWeb _platform = SplitioWeb();
Expand Down Expand Up @@ -687,15 +795,17 @@ void main() {
matchingKey: 'matching-key', bucketingKey: null);

expect(mock.calls.last.methodName, 'client');
expect(mock.calls.last.methodArguments.map(jsAnyToDart), ['matching-key']);
expect(
mock.calls.last.methodArguments.map(jsAnyToDart), ['matching-key']);
});

test('get client with new matching key', () async {
await _platform.getClient(
matchingKey: 'new-matching-key', bucketingKey: null);

expect(mock.calls.last.methodName, 'client');
expect(mock.calls.last.methodArguments.map(jsAnyToDart), ['new-matching-key']);
expect(mock.calls.last.methodArguments.map(jsAnyToDart),
['new-matching-key']);
});

test('get client with new matching key and bucketing key', () async {
Expand Down
45 changes: 45 additions & 0 deletions splitio_web/test/utils/js_interop_test_utils.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import 'dart:js_interop';
import 'dart:js_interop_unsafe';

@JS('Promise.resolve')
external JSPromise<Null> _promiseResolve();

class SplitioMock {
final List<({String methodName, List<JSAny?> methodArguments})> calls = [];
final JSObject splitio = JSObject();
Expand Down Expand Up @@ -123,6 +126,48 @@ class SplitioMock {
));
return trafficType != null ? true.toJS : false.toJS;
}.toJS;
mockClient['setAttribute'] = (JSAny? attributeName, JSAny? attributeValue) {
calls.add((
methodName: 'setAttribute',
methodArguments: [attributeName, attributeValue]
));
return true.toJS;
}.toJS;
mockClient['getAttribute'] = (JSAny? attributeName) {
calls.add((methodName: 'getAttribute', methodArguments: [attributeName]));
return 'attr-value'.toJS;
}.toJS;
mockClient['removeAttribute'] = (JSAny? attributeName) {
calls.add(
(methodName: 'removeAttribute', methodArguments: [attributeName]));
return true.toJS;
}.toJS;
mockClient['setAttributes'] = (JSAny? attributes) {
calls.add((methodName: 'setAttributes', methodArguments: [attributes]));
return true.toJS;
}.toJS;
mockClient['getAttributes'] = () {
calls.add((methodName: 'getAttributes', methodArguments: []));
return {
'attrBool': true,
'attrString': 'value',
'attrInt': 1,
'attrDouble': 1.1,
'attrList': ['value1', 100, false],
}.jsify();
}.toJS;
mockClient['clearAttributes'] = () {
calls.add((methodName: 'clearAttributes', methodArguments: []));
return true.toJS;
}.toJS;
mockClient['flush'] = () {
calls.add((methodName: 'flush', methodArguments: []));
return _promiseResolve();
}.toJS;
mockClient['destroy'] = () {
calls.add((methodName: 'destroy', methodArguments: []));
return _promiseResolve();
}.toJS;

final mockLog = JSObject();
mockLog['warn'] = (JSAny? arg1) {
Expand Down