Skip to content
Merged
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
105 changes: 105 additions & 0 deletions UnitTests/MPRoktTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,24 @@
#import "MPKitContainer.h"
#import "MPForwardQueueParameters.h"

@interface MPRokt ()
- (NSArray<NSDictionary<NSString *, NSString *> *> *)getRoktPlacementAttributes;
@end

@interface MPRokt (Testing)
@end

@interface MPRoktTests : XCTestCase
@property (nonatomic, strong) MPRokt *rokt;
@property (nonatomic, strong) id mockRokt;
@end

@implementation MPRoktTests

- (void)setUp {
[super setUp];
self.rokt = [[MPRokt alloc] init];
self.mockRokt = OCMPartialMock(self.rokt);
}

- (void)tearDown {
Expand All @@ -24,6 +30,7 @@ - (void)tearDown {
}

- (void)testSelectPlacementsSimpleWithValidParameters {
[[[self.mockRokt stub] andReturn:@[]] getRoktPlacementAttributes];
id mockInstance = OCMClassMock([MParticle class]);
id mockContainer = OCMClassMock([MPKitContainer_PRIVATE class]);
[[[mockInstance stub] andReturn:mockContainer] kitContainer_PRIVATE];
Expand Down Expand Up @@ -66,6 +73,7 @@ - (void)testSelectPlacementsSimpleWithValidParameters {
}

- (void)testSelectPlacementsExpandedWithValidParameters {
[[[self.mockRokt stub] andReturn:@[]] getRoktPlacementAttributes];
id mockInstance = OCMClassMock([MParticle class]);
id mockContainer = OCMClassMock([MPKitContainer_PRIVATE class]);
[[[mockInstance stub] andReturn:mockContainer] kitContainer_PRIVATE];
Expand Down Expand Up @@ -120,6 +128,7 @@ - (void)testSelectPlacementsExpandedWithValidParameters {
}

- (void)testSelectPlacementsExpandedWithNilParameters {
[[[self.mockRokt stub] andReturn:@[]] getRoktPlacementAttributes];
id mockInstance = OCMClassMock([MParticle class]);
id mockContainer = OCMClassMock([MPKitContainer_PRIVATE class]);
[[[mockInstance stub] andReturn:mockContainer] kitContainer_PRIVATE];
Expand Down Expand Up @@ -155,4 +164,100 @@ - (void)testSelectPlacementsExpandedWithNilParameters {
OCMVerifyAll(mockContainer);
}

- (void)testSelectPlacementsSimpleWithMapping {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test stubs the getRoktPlacementAttributes call.

Can you add a test for getRoktPlacementAttributes itself, using a config like the one you put in the chat. passing in "[{"jsmap":null,"map":"f.name","maptype":"UserAttributeClass.Name","value":"firstname"},{"jsmap":null,"map":"zip","maptype":"UserAttributeClass.Name","value":"billingzipcode"},{"jsmap":null,"map":"l.name","maptype":"UserAttributeClass.Name","value":"lastname"}]"

and expecting what you have in line 168 back?

(note that i copied and pasted what you had in #temp-rokt-client but updated the mapping from f.name --> firstname instead of email

[[[self.mockRokt stub] andReturn:@[@{@"map": @"f.name", @"maptype": @"UserAttributeClass.Name", @"value": @"firstname"}, @{@"map": @"zip", @"maptype": @"UserAttributeClass.Name", @"value": @"billingzipcode"}, @{@"map": @"l.name", @"maptype": @"UserAttributeClass.Name", @"value": @"lastname"}]] getRoktPlacementAttributes];
id mockInstance = OCMClassMock([MParticle class]);
id mockContainer = OCMClassMock([MPKitContainer_PRIVATE class]);
[[[mockInstance stub] andReturn:mockContainer] kitContainer_PRIVATE];
[[[mockInstance stub] andReturn:mockInstance] sharedInstance];

// Set up test parameters
NSString *viewName = @"testView";
NSDictionary *attributes = @{@"f.name": @"Brandon"};
NSDictionary *mappedAttributes = @{@"firstname": @"Brandon"};

// Set up expectations for kit container
XCTestExpectation *expectation = [self expectationWithDescription:@"Wait for async operation"];
SEL roktSelector = @selector(executeWithViewName:attributes:placements:onLoad:onUnLoad:onShouldShowLoadingIndicator:onShouldHideLoadingIndicator:onEmbeddedSizeChange:filteredUser:);
OCMExpect([mockContainer forwardSDKCall:roktSelector
event:nil
parameters:[OCMArg checkWithBlock:^BOOL(MPForwardQueueParameters *params) {
XCTAssertEqualObjects(params[0], viewName);
XCTAssertEqualObjects(params[1], mappedAttributes);
XCTAssertNil(params[2]);
XCTAssertNil(params[3]);
XCTAssertNil(params[4]);
XCTAssertNil(params[5]);
XCTAssertNil(params[6]);
XCTAssertNil(params[7]);
return true;
}]
messageType:MPMessageTypeEvent
userInfo:nil]).andDo(^(NSInvocation *invocation) {
[expectation fulfill];
});

// Execute method
[self.rokt selectPlacements:viewName
attributes:attributes];

// Wait for async operation
[self waitForExpectationsWithTimeout:1.0 handler:nil];

// Verify
OCMVerifyAll(mockContainer);
}

- (void)testSelectPlacementsSimpleWithNilMapping {
[[[self.mockRokt stub] andReturn:nil] getRoktPlacementAttributes];
id mockInstance = OCMClassMock([MParticle class]);
id mockContainer = OCMClassMock([MPKitContainer_PRIVATE class]);
[[[mockInstance stub] andReturn:mockContainer] kitContainer_PRIVATE];
[[[mockInstance stub] andReturn:mockInstance] sharedInstance];

SEL roktSelector = @selector(executeWithViewName:attributes:placements:onLoad:onUnLoad:onShouldShowLoadingIndicator:onShouldHideLoadingIndicator:onEmbeddedSizeChange:filteredUser:);
OCMReject([mockContainer forwardSDKCall:roktSelector
event:[OCMArg any]
parameters:[OCMArg any]
messageType:MPMessageTypeEvent
userInfo:[OCMArg any]]);

// Set up test parameters
NSString *viewName = @"testView";
NSDictionary *attributes = @{@"f.name": @"Brandon"};

// Execute method
[self.rokt selectPlacements:viewName
attributes:attributes];

// Verify
OCMVerifyAll((id)mockContainer);
}

- (void)testGetRoktPlacementAttributes {
id mockInstance = OCMClassMock([MParticle class]);
id mockContainer = OCMClassMock([MPKitContainer_PRIVATE class]);
NSArray *kitConfig = @[@{
@"AllowJavaScriptResponse": @"True",
@"accountId": @12345,
@"onboardingExpProvider": @"None",
@"placementAttributes": @"[{\"jsmap\":null,\"map\":\"f.name\",\"maptype\":\"UserAttributeClass.Name\",\"value\":\"firstname\"},{\"jsmap\":null,\"map\":\"zip\",\"maptype\":\"UserAttributeClass.Name\",\"value\":\"billingzipcode\"},{\"jsmap\":null,\"map\":\"l.name\",\"maptype\":\"UserAttributeClass.Name\",\"value\":\"lastname\"}]",
@"sandboxMode": @"True",
@"eau": @0,
@"hs": @{
@"pur": @{},
@"reg": @{}
},
@"id": @181
}];
[[[mockContainer stub] andReturn:kitConfig] originalConfig];
[[[mockInstance stub] andReturn:mockContainer] kitContainer_PRIVATE];
[[[mockInstance stub] andReturn:mockInstance] sharedInstance];

NSArray<NSDictionary<NSString *, NSString *> *> *testResult = [self.rokt getRoktPlacementAttributes];
NSArray<NSDictionary<NSString *, NSString *> *> *expectedResult = @[@{@"map": @"f.name", @"maptype": @"UserAttributeClass.Name", @"value": @"firstname", @"jsmap": [NSNull null]}, @{@"map": @"zip", @"maptype": @"UserAttributeClass.Name", @"value": @"billingzipcode", @"jsmap": [NSNull null]}, @{@"map": @"l.name", @"maptype": @"UserAttributeClass.Name", @"value": @"lastname", @"jsmap": [NSNull null]}];

XCTAssertEqualObjects(testResult, expectedResult, @"Mapping does not match .");
}

@end
165 changes: 121 additions & 44 deletions mParticle-Apple-SDK/mParticle.m
Original file line number Diff line number Diff line change
Expand Up @@ -163,30 +163,47 @@ @implementation MPRokt

- (void)selectPlacements:(NSString *)identifier
attributes:(NSDictionary<NSString *, NSString *> * _Nullable)attributes {
for (NSString *key in attributes) {
[[MParticle sharedInstance].identity.currentUser setUserAttribute:key value:attributes[key]];
}

dispatch_async(dispatch_get_main_queue(), ^{
// Forwarding call to kits
MPForwardQueueParameters *queueParameters = [[MPForwardQueueParameters alloc] init];
[queueParameters addParameter:identifier];
[queueParameters addParameter:attributes];
[queueParameters addParameter:nil];
[queueParameters addParameter:nil];
[queueParameters addParameter:nil];
[queueParameters addParameter:nil];
[queueParameters addParameter:nil];
[queueParameters addParameter:nil];
NSArray<NSDictionary<NSString *, NSString *> *> *attributeMap = [self getRoktPlacementAttributes];

// If attributeMap is nil the kit hasn't been initialized
if (attributeMap) {
NSMutableDictionary *mappedAttributes = attributes.mutableCopy;
for (NSDictionary<NSString *, NSString *> *map in attributeMap) {
NSString *mapFrom = map[@"map"];
NSString *mapTo = map[@"value"];
if (mappedAttributes[mapFrom]) {
NSString * value = mappedAttributes[mapFrom];
[mappedAttributes removeObjectForKey:mapFrom];
mappedAttributes[mapTo] = value;
}
}
for (NSString *key in mappedAttributes) {
[[MParticle sharedInstance].identity.currentUser setUserAttribute:key value:mappedAttributes[key]];
}
Comment on lines +170 to +182
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this be DRYed up into a separate function so that it's called from both selectPlacements methods?


SEL roktSelector = @selector(executeWithViewName:attributes:placements:onLoad:onUnLoad:onShouldShowLoadingIndicator:onShouldHideLoadingIndicator:onEmbeddedSizeChange:filteredUser:);
[[MParticle sharedInstance].kitContainer_PRIVATE forwardSDKCall:roktSelector
event:nil
parameters:queueParameters
messageType:MPMessageTypeEvent
userInfo:nil
];
});
dispatch_async(dispatch_get_main_queue(), ^{
// Forwarding call to kits
MPForwardQueueParameters *queueParameters = [[MPForwardQueueParameters alloc] init];
[queueParameters addParameter:identifier];
[queueParameters addParameter:mappedAttributes];
[queueParameters addParameter:nil];
[queueParameters addParameter:nil];
[queueParameters addParameter:nil];
[queueParameters addParameter:nil];
[queueParameters addParameter:nil];
[queueParameters addParameter:nil];

SEL roktSelector = @selector(executeWithViewName:attributes:placements:onLoad:onUnLoad:onShouldShowLoadingIndicator:onShouldHideLoadingIndicator:onEmbeddedSizeChange:filteredUser:);
[[MParticle sharedInstance].kitContainer_PRIVATE forwardSDKCall:roktSelector
event:nil
parameters:queueParameters
messageType:MPMessageTypeEvent
userInfo:nil
];
});
} else {
MPILogVerbose(@"[MParticle.Rokt selectPlacements:attributes:] not performed due to kit not being configured");
}
Comment on lines +184 to +206
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can also be DRYed up with the method below using all arguments and just pass either 2, or all arguments to it.

}

- (void)selectPlacements:(NSString *)identifier
Expand All @@ -197,30 +214,90 @@ - (void)selectPlacements:(NSString *)identifier
onShouldShowLoadingIndicator:(void (^ _Nullable)(void))onShouldShowLoadingIndicator
onShouldHideLoadingIndicator:(void (^ _Nullable)(void))onShouldHideLoadingIndicator
onEmbeddedSizeChange:(void (^ _Nullable)(NSString * _Nonnull, CGFloat))onEmbeddedSizeChange {
for (NSString *key in attributes) {
[[MParticle sharedInstance].identity.currentUser setUserAttribute:key value:attributes[key]];
NSArray<NSDictionary<NSString *, NSString *> *> *attributeMap = [self getRoktPlacementAttributes];

// If attributeMap is nil the kit hasn't been initialized
if (attributeMap) {
NSMutableDictionary *mappedAttributes = attributes.mutableCopy;
for (NSDictionary<NSString *, NSString *> *map in attributeMap) {
NSString *mapFrom = map[@"map"];
NSString *mapTo = map[@"value"];
if (mappedAttributes[mapFrom]) {
NSString * value = mappedAttributes[mapFrom];
[mappedAttributes removeObjectForKey:mapFrom];
mappedAttributes[mapTo] = value;
}
}
for (NSString *key in mappedAttributes) {
[[MParticle sharedInstance].identity.currentUser setUserAttribute:key value:mappedAttributes[key]];
}

dispatch_async(dispatch_get_main_queue(), ^{
// Forwarding call to kits
MPForwardQueueParameters *queueParameters = [[MPForwardQueueParameters alloc] init];
[queueParameters addParameter:identifier];
[queueParameters addParameter:mappedAttributes];
[queueParameters addParameter:placements];
[queueParameters addParameter:onLoad];
[queueParameters addParameter:onUnLoad];
[queueParameters addParameter:onShouldShowLoadingIndicator];
[queueParameters addParameter:onShouldHideLoadingIndicator];
[queueParameters addParameter:onEmbeddedSizeChange];

SEL roktSelector = @selector(executeWithViewName:attributes:placements:onLoad:onUnLoad:onShouldShowLoadingIndicator:onShouldHideLoadingIndicator:onEmbeddedSizeChange:filteredUser:);
[[MParticle sharedInstance].kitContainer_PRIVATE forwardSDKCall:roktSelector
event:nil
parameters:queueParameters
messageType:MPMessageTypeEvent
userInfo:nil
];
});
} else {
MPILogVerbose(@"[MParticle.Rokt selectPlacements: not performed since Kit not configured");
}
}

- (NSArray<NSDictionary<NSString *, NSString *> *> *)getRoktPlacementAttributes {
NSArray<NSDictionary<NSString *, NSString *> *> *attributeMap = nil;

dispatch_async(dispatch_get_main_queue(), ^{
// Forwarding call to kits
MPForwardQueueParameters *queueParameters = [[MPForwardQueueParameters alloc] init];
[queueParameters addParameter:identifier];
[queueParameters addParameter:attributes];
[queueParameters addParameter:placements];
[queueParameters addParameter:onLoad];
[queueParameters addParameter:onUnLoad];
[queueParameters addParameter:onShouldShowLoadingIndicator];
[queueParameters addParameter:onShouldHideLoadingIndicator];
[queueParameters addParameter:onEmbeddedSizeChange];
// Get the kit configuration
NSArray<NSDictionary *> *kitConfigs = [MParticle sharedInstance].kitContainer_PRIVATE.originalConfig.copy;
NSDictionary *roktKitConfig;
for (NSDictionary *kitConfig in kitConfigs) {
if (kitConfig[@"id"] != nil && [kitConfig[@"id"] integerValue] == 181) {
roktKitConfig = kitConfig;
}
}

// Get the placement attributes map
NSString *strAttributeMap;
NSData *dataAttributeMap;
if (roktKitConfig != nil) {
// Rokt Kit is available though there may not be an attribute map
attributeMap = @[];
if (roktKitConfig[@"placementAttributes"] != [NSNull null]) {
strAttributeMap = [roktKitConfig[@"placementAttributes"] stringByRemovingPercentEncoding];
dataAttributeMap = [strAttributeMap dataUsingEncoding:NSUTF8StringEncoding];
}
}

if (dataAttributeMap != nil) {
// Convert it to an array of dictionaries
NSError *error = nil;

SEL roktSelector = @selector(executeWithViewName:attributes:placements:onLoad:onUnLoad:onShouldShowLoadingIndicator:onShouldHideLoadingIndicator:onEmbeddedSizeChange:filteredUser:);
[[MParticle sharedInstance].kitContainer_PRIVATE forwardSDKCall:roktSelector
event:nil
parameters:queueParameters
messageType:MPMessageTypeEvent
userInfo:nil
];
});
@try {
attributeMap = [NSJSONSerialization JSONObjectWithData:dataAttributeMap options:kNilOptions error:&error];
} @catch (NSException *exception) {
}

if (attributeMap && !error) {
NSLog(@"%@", attributeMap);
} else {
NSLog(@"%@", error);
}
}

return attributeMap;
}

@end
Expand Down
Loading