Skip to content

Commit

Permalink
test: add unit tests for opf onsite messaging (#19308)
Browse files Browse the repository at this point in the history
CXSPA-8511
  • Loading branch information
FollowTheFlo authored Oct 7, 2024
1 parent 12c6c13 commit 85ddf62
Show file tree
Hide file tree
Showing 5 changed files with 409 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { CmsService, Page, Product, QueryState } from '@spartacus/core';
import {
ActiveConfiguration,
OpfBaseFacade,
OpfDynamicScript,
OpfPaymentProviderType,
OpfResourceLoaderService,
} from '@spartacus/opf/base/root';
Expand All @@ -29,6 +30,10 @@ const ctaScriptsRequestForPdpMock: CtaScriptsRequest = {
paymentAccountIds: [1],
scriptLocations: [CtaScriptsLocation.PDP_MESSAGING],
};
const ctaScriptsRequestForCartPageMock: CtaScriptsRequest = {
paymentAccountIds: [1],
scriptLocations: [CtaScriptsLocation.CART_MESSAGING],
};

describe('OpfCtaScriptsService', () => {
let service: OpfCtaScriptsService;
Expand Down Expand Up @@ -97,6 +102,9 @@ describe('OpfCtaScriptsService', () => {
opfDynamicCtaServiceMock.fillCtaRequestforProductPage.and.returnValue(
of(ctaScriptsRequestForPdpMock)
);
opfDynamicCtaServiceMock.fillCtaRequestforCartPage.and.returnValue(
of(ctaScriptsRequestForCartPageMock)
);
opfDynamicCtaServiceMock.initiateEvents.and.returnValue();
opfDynamicCtaServiceMock.stopEvents.and.returnValue();
opfDynamicCtaServiceMock.registerScriptReadyEvent.and.returnValue();
Expand All @@ -112,7 +120,7 @@ describe('OpfCtaScriptsService', () => {
opfBaseFacadeMock.getActiveConfigurationsState.and.returnValue(
of(activeConfigurationsMock)
);
opfCtaFacadeMock.getCtaScripts.and.returnValue(of(ctaScriptsresponseMock));
opfCtaFacadeMock.getCtaScripts.and.returnValue(of(ctaScriptsResponseMock));
});

it('should be created', () => {
Expand Down Expand Up @@ -163,6 +171,22 @@ describe('OpfCtaScriptsService', () => {
});
});

it('should call DynamicCtaService for CTA on Cart page', (done) => {
cmsServiceMock.getCurrentPage.and.returnValue(
of({ ...mockPage, pageId: 'cartPage' })
);

service.getCtaHtmlslList().subscribe((htmlsList) => {
expect(htmlsList[0].html).toContain(
'Thanks for purchasing our great products'
);
expect(
opfDynamicCtaServiceMock.fillCtaRequestforCartPage
).toHaveBeenCalled();
done();
});
});

it('should throw an error when empty CTA scripts response from OPF server', (done) => {
opfCtaFacadeMock.getCtaScripts.and.returnValue(of({ value: [] }));

Expand All @@ -175,20 +199,94 @@ describe('OpfCtaScriptsService', () => {
});
});

it('should throw an error when empty ScriptLocation is invalid', (done) => {
it('should throw an error when ScriptLocation is invalid', (done) => {
cmsServiceMock.getCurrentPage.and.returnValue(
of({ ...mockPage, pageId: 'testPage' })
);

service.getCtaHtmlslList().subscribe({
next: () => {
fail('Invalid script should fail');
done();
},
error: (error) => {
expect(error).toEqual('Invalid Script Location');
done();
},
});
});

const ctaScriptsresponseMock: CtaScriptsResponse = {
it('should throw an error when empty ScriptLocation', (done) => {
cmsServiceMock.getCurrentPage.and.returnValue(
of({ ...mockPage, pageId: undefined })
);

service.getCtaHtmlslList().subscribe({
next: () => {
fail('Empty script should fail');
done();
},
error: (error) => {
expect(error).toEqual('Invalid Script Location');
done();
},
});
});

it('should execute script from script response html', (done) => {
service.loadAndRunScript(dynamicScriptMock).then((scriptResponse) => {
expect(
opfResourceLoaderServiceMock.executeScriptFromHtml
).toHaveBeenCalled();
expect(scriptResponse?.html).toEqual(dynamicScriptMock.html);
done();
});
});

it('should not execute script when html from script response is empty', (done) => {
service
.loadAndRunScript({ ...dynamicScriptMock, html: '' })
.then((scriptResponse) => {
expect(
opfResourceLoaderServiceMock.executeScriptFromHtml
).not.toHaveBeenCalled();
expect(scriptResponse).toBeFalsy();
done();
});
});

it('should not execute script when resource loading failed', (done) => {
opfResourceLoaderServiceMock.loadProviderResources.and.returnValue(
Promise.reject()
);
service
.loadAndRunScript({ ...dynamicScriptMock, html: '' })
.then((scriptResponse) => {
expect(
opfResourceLoaderServiceMock.executeScriptFromHtml
).not.toHaveBeenCalled();
expect(scriptResponse).toBeFalsy();
done();
});
});

const dynamicScriptMock: OpfDynamicScript = {
html: '<div style="border-style: solid;text-align:center;border-radius:10px;align-content:center;background-color:yellow;color:black"><h2>Thanks for purchasing our great products</h2><h3>Please use promo code:<b>123abc</b> for your next purchase<h3></div><script>console.log(\'CTA Script #1 is running\')</script>',
cssUrls: [
{
url: 'https://checkoutshopper-test.adyen.com/checkoutshopper/sdk/4.2.1/adyen.css',
sri: '',
},
],
jsUrls: [
{
url: 'https://checkoutshopper-test.adyen.com/checkoutshopper/sdk/4.2.1/adyen.js',
sri: '',
},
],
};

const ctaScriptsResponseMock: CtaScriptsResponse = {
value: [
{
paymentAccountId: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,6 @@ export class OpfCtaScriptsService {
if (!scriptsLocation) {
return throwError(() => 'Invalid Script Location');
}
const toBeImplementedException = () =>
throwError(() => 'to be implemented');
const locationToFunctionMap: Record<
CtaScriptsLocation,
() => Observable<CtaScriptsRequest>
Expand All @@ -153,16 +151,9 @@ export class OpfCtaScriptsService {
scriptsLocation,
paymentAccountIds
),
[CtaScriptsLocation.CART_QUICK_BUY]: toBeImplementedException,
[CtaScriptsLocation.CHECKOUT_QUICK_BUY]: toBeImplementedException,
[CtaScriptsLocation.PDP_QUICK_BUY]: toBeImplementedException,
};

const selectedFunction = locationToFunctionMap[scriptsLocation];

return selectedFunction
? selectedFunction()
: throwError(() => 'Invalid Script Location');
return selectedFunction();
}

protected getScriptLocation(): Observable<CtaScriptsLocation | undefined> {
Expand Down
205 changes: 205 additions & 0 deletions integration-libs/opf/cta/core/services/opf-dynamic-cta.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import { TestBed } from '@angular/core/testing';
import { ActiveCartFacade, Cart } from '@spartacus/cart/base/root';
import {
CurrencyService,
EventService,
LanguageService,
Product,
WindowRef,
} from '@spartacus/core';
import { CtaScriptsLocation, OpfCtaFacade } from '@spartacus/opf/cta/root';
import { OpfGlobalFunctionsFacade } from '@spartacus/opf/global-functions/root';
import { CurrentProductService } from '@spartacus/storefront';
import { of } from 'rxjs';
import { OpfDynamicCtaService } from './opf-dynamic-cta.service';

describe('OpfDynamicCtaService', () => {
let service: OpfDynamicCtaService;
let globalFunctionsFacadeMock: jasmine.SpyObj<OpfGlobalFunctionsFacade>;
let eventServiceMock: jasmine.SpyObj<EventService>;
let currencyServiceMock: jasmine.SpyObj<CurrencyService>;
let languageServiceMock: jasmine.SpyObj<LanguageService>;
let activeCartFacadeMock: jasmine.SpyObj<ActiveCartFacade>;
let opfCtaFacadeMock: jasmine.SpyObj<OpfCtaFacade>;
let currentProductServiceMock: jasmine.SpyObj<CurrentProductService>;

beforeEach(() => {
// Prevent external link navigation
window.onbeforeunload = function () {
return '';
};
globalFunctionsFacadeMock = jasmine.createSpyObj('GlobalFunctionsFacade', [
'registerGlobalFunctions',
'removeGlobalFunctions',
]);

eventServiceMock = jasmine.createSpyObj('EventService', ['get']);

currencyServiceMock = jasmine.createSpyObj('CurrencyService', [
'getActive',
]);
languageServiceMock = jasmine.createSpyObj('LanguageService', [
'getActive',
]);
opfCtaFacadeMock = jasmine.createSpyObj('OpfCtaFacade', [
'listenScriptReadyEvent',
]);
currentProductServiceMock = jasmine.createSpyObj('CurrentProductService', [
'getProduct',
]);
activeCartFacadeMock = jasmine.createSpyObj('ActiveCartFacade', [
'takeActive',
]);

TestBed.configureTestingModule({
providers: [
WindowRef,
OpfDynamicCtaService,
{
provide: OpfGlobalFunctionsFacade,
useValue: globalFunctionsFacadeMock,
},
{ provide: EventService, useValue: eventServiceMock },
{ provide: CurrencyService, useValue: currencyServiceMock },
{ provide: ActiveCartFacade, useValue: activeCartFacadeMock },
{ provide: LanguageService, useValue: languageServiceMock },
{ provide: OpfCtaFacade, useValue: opfCtaFacadeMock },
{ provide: CurrentProductService, useValue: currentProductServiceMock },
],
});
service = TestBed.inject(OpfDynamicCtaService);
currencyServiceMock.getActive.and.returnValue(of(mockCurrency));
languageServiceMock.getActive.and.returnValue(of(mockLanguage));
eventServiceMock.get.and.returnValue(of(true));
opfCtaFacadeMock.listenScriptReadyEvent.and.returnValue(of(mockScriptId));
globalFunctionsFacadeMock.registerGlobalFunctions.and.returnValue();
globalFunctionsFacadeMock.removeGlobalFunctions.and.returnValue();
currentProductServiceMock.getProduct.and.returnValue(of(mockProduct));
activeCartFacadeMock.takeActive.and.returnValue(of(mockCart));
});

it('should be created', () => {
expect(service).toBeTruthy();
});

it('should call activeCart on fillCtaRequestforCartPage', (done) => {
service
.fillCtaRequestforCartPage(
CtaScriptsLocation.CART_MESSAGING,
mockAccountIds
)
.subscribe((ctaRequest) => {
expect(activeCartFacadeMock.takeActive).toHaveBeenCalled();
expect(languageServiceMock.getActive).toHaveBeenCalled();
expect(ctaRequest.scriptLocations).toEqual([
CtaScriptsLocation.CART_MESSAGING,
]);
done();
});
});

it('should call productService on fillCtaRequestforProductPage', (done) => {
service
.fillCtaRequestforProductPage(
CtaScriptsLocation.PDP_MESSAGING,
mockAccountIds
)
.subscribe((ctaRequest) => {
expect(currentProductServiceMock.getProduct).toHaveBeenCalled();
expect(languageServiceMock.getActive).toHaveBeenCalled();
expect(ctaRequest.scriptLocations).toEqual([
CtaScriptsLocation.PDP_MESSAGING,
]);
expect(
ctaRequest.additionalData?.find(
(keyVal) => keyVal.key === 'scriptIdentifier'
)?.value
).toEqual(mockScriptId);
done();
});
});

it('should start cartListener on cart page initiateEvents', (done) => {
service
.fillCtaRequestforCartPage(
CtaScriptsLocation.CART_MESSAGING,
mockAccountIds
)
.subscribe(() => {
service.initiateEvents();
expect(eventServiceMock.get).toHaveBeenCalled();
expect(
globalFunctionsFacadeMock.registerGlobalFunctions
).toHaveBeenCalled();
done();
});
});

it('should not start cartListener on pdp initiateEvents', (done) => {
service
.fillCtaRequestforProductPage(
CtaScriptsLocation.PDP_MESSAGING,
mockAccountIds
)
.subscribe(() => {
service.initiateEvents();
expect(eventServiceMock.get).not.toHaveBeenCalled();
expect(
globalFunctionsFacadeMock.registerGlobalFunctions
).toHaveBeenCalled();
done();
});
});

it('should remove global functions on stopEvents', (done) => {
service
.fillCtaRequestforProductPage(
CtaScriptsLocation.CART_MESSAGING,
mockAccountIds
)
.subscribe(() => {
service.initiateEvents();
service.stopEvents();
expect(
globalFunctionsFacadeMock.removeGlobalFunctions
).toHaveBeenCalled();

done();
});
});

const mockAccountIds = [51, 22];
const mockScriptId = '0001';

const mockLanguage = 'en';
const mockCurrency = 'UDS';

const mockProduct: Product = {
name: 'mockProduct',
code: 'code1',
stock: {
stockLevel: 333,
stockLevelStatus: 'inStock',
},
};

const mockCart: Cart = {
code: 'xxx',
guid: 'xxx',
totalItems: 0,
entries: [
{ entryNumber: 0, product: { code: '1234' } },
{ entryNumber: 1, product: { code: '01234' } },
{ entryNumber: 2, product: { code: '3234' } },
],
totalPrice: {
currencyIso: 'USD',
value: 100,
},
totalPriceWithTax: {
currencyIso: 'USD',
value: 0,
},
user: { uid: 'test' },
};
});
Loading

0 comments on commit 85ddf62

Please sign in to comment.