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
9 changes: 6 additions & 3 deletions src/roktManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,22 +319,25 @@ export default class RoktManager {
this.logger?.verbose(`RoktManager: Processing ${this.messageQueue.size} queued messages`);

this.messageQueue.forEach((message) => {
if(!(message.methodName in this.kit) || !isFunction(this.kit[message.methodName])) {
this.logger?.error(`RoktManager: Method ${message.methodName} not found in kit`);
if(!(message.methodName in this) || !isFunction(this[message.methodName])) {
this.logger?.error(`RoktManager: Method ${message.methodName} not found`);
return;
}

this.logger?.verbose(`RoktManager: Processing queued message: ${message.methodName} with payload: ${JSON.stringify(message.payload)}`);

try {
const result = (this.kit[message.methodName] as Function)(message.payload);
const result = (this[message.methodName] as Function)(message.payload);
this.completePendingPromise(message.messageId, result);
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.logger?.error(`RoktManager: Error processing message '${message.methodName}': ${errorMessage}`);
this.completePendingPromise(message.messageId, Promise.reject(error));
}
});

// Clear the queue after processing all messages
this.messageQueue.clear();
}

private queueMessage(message: IRoktMessage): void {
Expand Down
133 changes: 131 additions & 2 deletions test/jest/roktManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,8 +389,10 @@ describe('RoktManager', () => {
});

describe('#processMessageQueue', () => {
it('should process the message queue if a launcher and kit are attached', () => {
const kit: IRoktKit = {
let kit: IRoktKit;

beforeEach(() => {
kit = {
launcher: {
selectPlacements: jest.fn(),
hashAttributes: jest.fn(),
Expand All @@ -404,6 +406,9 @@ describe('RoktManager', () => {
setExtensionData: jest.fn(),
use: jest.fn(),
};
});

it('should process the message queue if a launcher and kit are attached', () => {


roktManager.selectPlacements({} as IRoktSelectPlacementsOptions);
Expand All @@ -420,6 +425,130 @@ describe('RoktManager', () => {
expect(roktManager['messageQueue'].size).toBe(0);
expect(kit.selectPlacements).toHaveBeenCalledTimes(3);
});

it('should call RoktManager methods (not kit methods directly) when processing queue', () => {
// Queue some calls before kit is ready (these will be deferred)
const selectOptions = { attributes: { test: 'value' } } as IRoktSelectPlacementsOptions;
const hashAttrs = { email: 'test@example.com' };
const extensionData = { 'test-ext': { config: true } };
const useName = 'TestExtension';

roktManager.selectPlacements(selectOptions);
roktManager.hashAttributes(hashAttrs);
roktManager.setExtensionData(extensionData);
roktManager.use(useName);

// Verify calls were queued
expect(roktManager['messageQueue'].size).toBe(4);
expect(kit.selectPlacements).not.toHaveBeenCalled(); // Kit methods not called yet
expect(kit.hashAttributes).not.toHaveBeenCalled(); // Kit methods not called yet
expect(kit.setExtensionData).not.toHaveBeenCalled(); // Kit methods not called yet
expect(kit.use).not.toHaveBeenCalled(); // Kit methods not called yet

// Spy on RoktManager methods AFTER initial calls to track queue processing
const selectPlacementsSpy = jest.spyOn(roktManager, 'selectPlacements');
const hashAttributesSpy = jest.spyOn(roktManager, 'hashAttributes');
const setExtensionDataSpy = jest.spyOn(roktManager, 'setExtensionData');
const useSpy = jest.spyOn(roktManager, 'use');

// Attach kit (triggers processMessageQueue)
roktManager.attachKit(kit);

// Verify RoktManager methods were called during queue processing
expect(selectPlacementsSpy).toHaveBeenCalledTimes(1);
expect(selectPlacementsSpy).toHaveBeenCalledWith(selectOptions);

expect(hashAttributesSpy).toHaveBeenCalledTimes(1);
expect(hashAttributesSpy).toHaveBeenCalledWith(hashAttrs);

expect(setExtensionDataSpy).toHaveBeenCalledTimes(1);
expect(setExtensionDataSpy).toHaveBeenCalledWith(extensionData);

expect(useSpy).toHaveBeenCalledTimes(1);
expect(useSpy).toHaveBeenCalledWith(useName);

// Verify queue was cleared
expect(roktManager['messageQueue'].size).toBe(0);

// Clean up spies
selectPlacementsSpy.mockRestore();
hashAttributesSpy.mockRestore();
setExtensionDataSpy.mockRestore();
useSpy.mockRestore();
});

it('should preserve RoktManager preprocessing logic when processing deferred selectPlacements calls', () => {
// Set up placement attributes mapping to test preprocessing
roktManager['placementAttributesMapping'] = [
{
jsmap: null,
map: 'original_key',
maptype: 'UserAttributeClass.Name',
value: 'mapped_key'
}
];

// Set up current user mock
roktManager['currentUser'] = {
setUserAttributes: jest.fn(),
getUserIdentities: jest.fn().mockReturnValue({
userIdentities: {}
})
} as unknown as IMParticleUser;

// Queue a selectPlacements call with attributes that need mapping
const originalOptions: IRoktSelectPlacementsOptions = {
attributes: {
original_key: 'test_value',
other_attr: 'other_value'
}
};

roktManager.selectPlacements(originalOptions);
expect(roktManager['messageQueue'].size).toBe(1);

// Attach kit (triggers processMessageQueue)
roktManager.attachKit(kit);

// Verify the kit method was called with MAPPED attributes (proving preprocessing occurred)
const expectedMappedOptions = {
attributes: {
mapped_key: 'test_value', // This key should be mapped
other_attr: 'other_value' // This key should remain unchanged
}
};

expect(kit.selectPlacements).toHaveBeenCalledWith(expectedMappedOptions);
expect(roktManager['currentUser'].setUserAttributes).toHaveBeenCalledWith({
mapped_key: 'test_value',
other_attr: 'other_value'
});
});


it('should skip processing if method does not exist on RoktManager', () => {
// Manually add a message with a non-existent method name
roktManager['queueMessage']({
messageId: 'test_123',
methodName: 'nonExistentMethod',
payload: { test: 'data' },
resolve: jest.fn(),
reject: jest.fn()
});

expect(roktManager['messageQueue'].size).toBe(1);

// Attach kit (triggers processMessageQueue)
roktManager.attachKit(kit);

// Verify error was logged for non-existent method
expect(mockMPInstance.Logger.error).toHaveBeenCalledWith(
'RoktManager: Method nonExistentMethod not found'
);

// Verify message was removed from queue even though method didn't exist
expect(roktManager['messageQueue'].size).toBe(0);
});
});

describe('#selectPlacements', () => {
Expand Down
Loading