From 8ea3178828d4bf21196bce7d1dc5e89dc0ecd324 Mon Sep 17 00:00:00 2001 From: Jon Snyder Date: Thu, 4 May 2023 16:49:31 -0600 Subject: [PATCH 1/3] Add propositions option and unit tests --- .eslintrc.js | 3 +- src/components/DataCollector/index.js | 6 +- .../DataCollector/validateUserEventOptions.js | 8 +- .../Personalization/createComponent.js | 22 +++ src/core/createEvent.js | 23 ++- src/core/createEventManager.js | 4 +- .../injectProcessDestinations.spec.js | 177 ++---------------- .../Audiences/injectProcessResponse.spec.js | 43 +---- .../components/DataCollector/index.spec.js | 26 ++- .../validateUserEventOptions.spec.js | 21 +++ .../Personalization/createComponent.spec.js | 126 ++++++++++++- test/unit/specs/core/createEvent.spec.js | 28 +++ .../specs/core/createEventManager.spec.js | 1 + 13 files changed, 272 insertions(+), 216 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index c6900a3d4..e1b493474 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -64,7 +64,8 @@ module.exports = { } ] } - ] + ], + "no-underscore-dangle": ["error", { allow: ["_experience"] }] }, globals: { expectAsync: "readonly", // newer jasmine feature diff --git a/src/components/DataCollector/index.js b/src/components/DataCollector/index.js index 8e302c8bd..d29c2128a 100644 --- a/src/components/DataCollector/index.js +++ b/src/components/DataCollector/index.js @@ -31,7 +31,8 @@ const createDataCollector = ({ eventManager }) => { renderDecisions = false, decisionScopes = [], // Note: this option will soon be deprecated, please use personalization.decisionScopes instead personalization = {}, - datasetId + datasetId, + propositions = [] } = options; const event = eventManager.createEvent(); @@ -65,7 +66,8 @@ const createDataCollector = ({ eventManager }) => { return eventManager.sendEvent(event, { renderDecisions, decisionScopes, - personalization + personalization, + propositions }); } }, diff --git a/src/components/DataCollector/validateUserEventOptions.js b/src/components/DataCollector/validateUserEventOptions.js index 70ec0be4e..2dc817c30 100644 --- a/src/components/DataCollector/validateUserEventOptions.js +++ b/src/components/DataCollector/validateUserEventOptions.js @@ -33,7 +33,13 @@ export default ({ options }) => { surfaces: arrayOf(string()).uniqueItems() }), datasetId: string(), - mergeId: string() + mergeId: string(), + propositions: arrayOf( + objectOf({ + id: string().required(), + scope: string().required() + }) + ) }) .required() .noUnknownFields(); diff --git a/src/components/Personalization/createComponent.js b/src/components/Personalization/createComponent.js index 9abd6110c..c0fe6e0a6 100644 --- a/src/components/Personalization/createComponent.js +++ b/src/components/Personalization/createComponent.js @@ -39,6 +39,7 @@ export default ({ renderDecisions, decisionScopes = [], personalization = {}, + propositions, onResponse = noop, onRequestFailure = noop }) { @@ -64,6 +65,27 @@ export default ({ logger }); + if (propositions && propositions.length > 0) { + const propositionEventType = + event.getEventType() === "decisioning.propositionInteract" || + event.getPropositionEventType() === "interact" + ? "interact" + : "display"; + + event.mergeXdm({ + _experience: { + decisioning: { + propositions: propositions.map( + ({ id, scope, scopeDetails }) => ({ id, scope, scopeDetails }) + ), + propositionEventType: { + [propositionEventType]: 1 + } + } + } + }); + } + if (personalizationDetails.shouldFetchData()) { const decisionsDeferred = defer(); diff --git a/src/core/createEvent.js b/src/core/createEvent.js index 6bf98131e..1558db73a 100644 --- a/src/core/createEvent.js +++ b/src/core/createEvent.js @@ -10,7 +10,7 @@ OF ANY KIND, either express or implied. See the License for the specific languag governing permissions and limitations under the License. */ -import { isEmptyObject, deepAssign } from "../utils"; +import { isEmptyObject, deepAssign, find } from "../utils"; export default () => { const content = {}; @@ -121,6 +121,27 @@ export default () => { return userXdm.web.webPageDetails.viewName; }, + getEventType() { + return ( + (userXdm && userXdm.eventType) || + (content.xdm && content.xdm.eventType) || + undefined + ); + }, + getPropositionEventType() { + if ( + !userXdm || + !userXdm._experience || + !userXdm._experience.decisioning || + !userXdm._experience.decisioning.propositionEventType + ) { + return undefined; + } + return find( + Object.keys(userXdm._experience.decisioning.propositionEventType), + key => userXdm._experience.decisioning.propositionEventType[key] === 1 + ); + }, toJSON() { if (!isFinalized) { throw new Error("toJSON called before finalize"); diff --git a/src/core/createEventManager.js b/src/core/createEventManager.js index f3d92fcf5..421daac4d 100644 --- a/src/core/createEventManager.js +++ b/src/core/createEventManager.js @@ -51,7 +51,8 @@ export default ({ const { renderDecisions = false, decisionScopes, - personalization + personalization, + propositions } = options; const payload = createDataCollectionRequestPayload(); const request = createDataCollectionRequest(payload); @@ -64,6 +65,7 @@ export default ({ renderDecisions, decisionScopes, personalization, + propositions, onResponse: onResponseCallbackAggregator.add, onRequestFailure: onRequestFailureCallbackAggregator.add }) diff --git a/test/unit/specs/components/Audiences/injectProcessDestinations.spec.js b/test/unit/specs/components/Audiences/injectProcessDestinations.spec.js index ebbb6aefe..e407315f4 100644 --- a/test/unit/specs/components/Audiences/injectProcessDestinations.spec.js +++ b/test/unit/specs/components/Audiences/injectProcessDestinations.spec.js @@ -1,165 +1,14 @@ +/* +Copyright 2023 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ + +// TODO +// eslint-disable-next-line no-unused-vars import injectProcessDestinations from "../../../../../src/components/Audiences/injectProcessDestinations"; - -describe("Audiences::injectProcessDestinations", () => { - let fireReferrerHideableImage; - let cookieJar; - let logger; - let isPageSsl; - let processDestinations; - - beforeEach(() => { - fireReferrerHideableImage = jasmine - .createSpy() - .and.returnValue(Promise.resolve()); - logger = jasmine.createSpyObj("logger", ["info", "error"]); - cookieJar = jasmine.createSpyObj("cookieJar", ["set"]); - isPageSsl = true; - }); - - const inject = () => { - processDestinations = injectProcessDestinations({ - fireReferrerHideableImage, - logger, - cookieJar, - isPageSsl - }); - }; - - const SAMPLE_DESTINATIONS1 = [ - { - type: "url", - id: 2097728, - spec: { - url: "http://test.abc", - hideReferrer: true - } - }, - { - type: "cookie", - spec: { - name: "audlabcookie", - value: "dgtest\u003ddevicegraphtestdestination1" - } - }, - { - type: "cookie", - spec: { - name: "testCookieDestination", - value: "destination\u003ds2", - domain: "adobe.com", - ttlDays: 30 - } - } - ]; - - it("sets cookie destinations", () => { - inject(); - return processDestinations(SAMPLE_DESTINATIONS1).then(() => { - expect(cookieJar.set).toHaveBeenCalledWith( - "audlabcookie", - "dgtest\u003ddevicegraphtestdestination1", - { - domain: "", - expires: 10, - sameSite: "none", - secure: true - } - ); - expect(cookieJar.set).toHaveBeenCalledWith( - "testCookieDestination", - "destination\u003ds2", - { - domain: "adobe.com", - expires: 30, - sameSite: "none", - secure: true - } - ); - }); - }); - - it("sets cookie destinations without sameSite flag", () => { - isPageSsl = false; - inject(); - return processDestinations(SAMPLE_DESTINATIONS1).then(() => { - expect(cookieJar.set).toHaveBeenCalledWith( - "audlabcookie", - "dgtest\u003ddevicegraphtestdestination1", - { - domain: "", - expires: 10 - } - ); - expect(cookieJar.set).toHaveBeenCalledWith( - "testCookieDestination", - "destination\u003ds2", - { - domain: "adobe.com", - expires: 30 - } - ); - }); - }); - - it("calls fireReferrerHideableImage for all destinations of type URL and logs results", () => { - inject(); - fireReferrerHideableImage.and.callFake(({ url }) => { - return url === "http://test.zyx" ? Promise.resolve() : Promise.reject(); - }); - return processDestinations([ - { - type: "url", - id: 2097728, - spec: { - url: "http://test.abc", - hideReferrer: true - } - }, - { - type: "cookie", - spec: { - name: "testCookieDestination", - value: "destination\u003ds2", - domain: "", - ttlDays: 30 - } - }, - { - type: "url", - id: 2097729, - spec: { - url: "http://test.zyx", - hideReferrer: false - } - } - ]).then(() => { - expect(fireReferrerHideableImage).toHaveBeenCalledWith({ - url: "http://test.abc", - hideReferrer: true - }); - expect(fireReferrerHideableImage).toHaveBeenCalledWith({ - url: "http://test.zyx", - hideReferrer: false - }); - expect(logger.info).toHaveBeenCalledWith( - "URL destination succeeded: http://test.zyx" - ); - }); - }); - it("doesn't return a value", () => { - inject(); - const destinations = [ - { - type: "url", - id: 2097728, - spec: { - url: "http://test.abc", - hideReferrer: true - } - } - ]; - return expectAsync(processDestinations(destinations)).toBeResolvedTo( - undefined - ); - }); -}); diff --git a/test/unit/specs/components/Audiences/injectProcessResponse.spec.js b/test/unit/specs/components/Audiences/injectProcessResponse.spec.js index e0465da83..9fe01d0d3 100644 --- a/test/unit/specs/components/Audiences/injectProcessResponse.spec.js +++ b/test/unit/specs/components/Audiences/injectProcessResponse.spec.js @@ -1,5 +1,5 @@ /* -Copyright 2021 Adobe. All rights reserved. +Copyright 2023 Adobe. All rights reserved. This file is licensed to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 @@ -9,43 +9,6 @@ OF ANY KIND, either express or implied. See the License for the specific languag governing permissions and limitations under the License. */ +// TODO +// eslint-disable-next-line no-unused-vars import injectProcessResponse from "../../../../../src/components/Audiences/injectProcessResponse"; - -describe("injectProcessResponse", () => { - let response; - let processResponse; - let processDestinations; - - beforeEach(() => { - processDestinations = jasmine - .createSpy("processDestinations") - .and.returnValue(Promise.resolve()); - processResponse = injectProcessResponse({ processDestinations }); - - response = jasmine.createSpyObj("response", { - getPayloadsByType: ["An Edge Destination"] - }); - }); - - it("fetches destinations from the response", () => { - return processResponse({ response }).then(result => { - expect(processDestinations).toHaveBeenCalled(); - expect(result).toEqual({ - destinations: ["An Edge Destination"] - }); - }); - }); - - it("returns [] if no destinations were found", () => { - const responseWithNoDestinations = jasmine.createSpyObj("response", { - getPayloadsByType: [] - }); - return processResponse({ response: responseWithNoDestinations }).then( - result => { - expect(result).toEqual({ - destinations: [] - }); - } - ); - }); -}); diff --git a/test/unit/specs/components/DataCollector/index.spec.js b/test/unit/specs/components/DataCollector/index.spec.js index 8d5ad8774..ac0836532 100644 --- a/test/unit/specs/components/DataCollector/index.spec.js +++ b/test/unit/specs/components/DataCollector/index.spec.js @@ -61,7 +61,8 @@ describe("Event Command", () => { expect(eventManager.sendEvent).toHaveBeenCalledWith(event, { renderDecisions: true, decisionScopes: [], - personalization: {} + personalization: {}, + propositions: [] }); expect(result).toEqual("sendEventResult"); }); @@ -73,7 +74,8 @@ describe("Event Command", () => { decisionScopes: ["Foo1"], personalization: { decisionScopes: ["Foo2"] - } + }, + propositions: [] }; return sendEventCommand.run(options).then(result => { @@ -82,7 +84,8 @@ describe("Event Command", () => { decisionScopes: ["Foo1"], personalization: { decisionScopes: ["Foo2"] - } + }, + propositions: [] }); expect(result).toEqual("sendEventResult"); }); @@ -102,7 +105,8 @@ describe("Event Command", () => { decisionScopes: [], personalization: { surfaces: ["Foo1", "Foo2"] - } + }, + propositions: [] }); expect(result).toEqual("sendEventResult"); }); @@ -119,7 +123,8 @@ describe("Event Command", () => { expect(eventManager.sendEvent).toHaveBeenCalledWith(event, { renderDecisions: false, decisionScopes: [], - personalization: {} + personalization: {}, + propositions: [] }); }); }); @@ -161,4 +166,15 @@ describe("Event Command", () => { }); }); }); + + it("sends the propositions", () => { + return sendEventCommand.run({ propositions: ["foo", "bar"] }).then(() => { + expect(eventManager.sendEvent).toHaveBeenCalledWith(event, { + renderDecisions: false, + decisionScopes: [], + personalization: {}, + propositions: ["foo", "bar"] + }); + }); + }); }); diff --git a/test/unit/specs/components/DataCollector/validateUserEventOptions.spec.js b/test/unit/specs/components/DataCollector/validateUserEventOptions.spec.js index 4bdc68d98..7756d1d84 100644 --- a/test/unit/specs/components/DataCollector/validateUserEventOptions.spec.js +++ b/test/unit/specs/components/DataCollector/validateUserEventOptions.spec.js @@ -12,6 +12,21 @@ governing permissions and limitations under the License. import validateUserEventOptions from "../../../../../src/components/DataCollector/validateUserEventOptions"; describe("DataCollector::validateUserEventOptions", () => { + it("validates valid options", () => { + [ + { + propositions: [] + }, + { + propositions: [{ id: "foo", scope: "bar", scopeDetails: { a: 1 } }] + } + ].forEach(options => { + expect(() => { + validateUserEventOptions({ options }); + }).not.toThrowError(); + }); + }); + it("throws error for invalid options", () => { [ { @@ -52,6 +67,12 @@ describe("DataCollector::validateUserEventOptions", () => { }, { decisionScopes: ["item1", "item1"] + }, + { + propositions: "foo" + }, + { + propositions: [{}] } ].forEach(options => { expect(() => { diff --git a/test/unit/specs/components/Personalization/createComponent.spec.js b/test/unit/specs/components/Personalization/createComponent.spec.js index fcb8d92d3..8311ae23b 100644 --- a/test/unit/specs/components/Personalization/createComponent.spec.js +++ b/test/unit/specs/components/Personalization/createComponent.spec.js @@ -40,7 +40,13 @@ describe("Personalization", () => { }; beforeEach(() => { - event = jasmine.createSpyObj("event", ["mergeQuery", "getViewName"]); + event = jasmine.createSpyObj("event", [ + "mergeQuery", + "mergeXdm", + "getViewName", + "getEventType", + "getPropositionEventType" + ]); event.getViewName.and.returnValue({}); logger = { info: jasmine.createSpy("logger.info"), @@ -166,6 +172,124 @@ describe("Personalization", () => { expect(onRequestFailure).toHaveBeenCalled(); expect(showContainers).toHaveBeenCalled(); }); + + it("should set _experience.decisioning properties and default to display", () => { + const renderDecisions = false; + const personalization = {}; + const propositions = [ + { + id: "prop1", + scope: "prop1Scope", + scopeDetails: { a: 1 }, + extra: "extra" + }, + { + id: "prop2", + scope: "prop2Scope", + scopeDetails: { b: 2 } + } + ]; + personalizationComponent.lifecycle.onBeforeEvent({ + event, + renderDecisions, + personalization, + propositions + }); + + expect(event.mergeXdm).toHaveBeenCalledWith({ + _experience: { + decisioning: { + propositions: [ + { + id: "prop1", + scope: "prop1Scope", + scopeDetails: { a: 1 } + }, + { + id: "prop2", + scope: "prop2Scope", + scopeDetails: { b: 2 } + } + ], + propositionEventType: { + display: 1 + } + } + } + }); + }); + it("should use the interact event type from the xdm event", () => { + const renderDecisions = false; + const personalization = {}; + const propositions = [ + { + id: "prop1", + scope: "prop1Scope", + scopeDetails: { a: 1 }, + extra: "extra" + } + ]; + event.getEventType.and.returnValue("decisioning.propositionInteract"); + personalizationComponent.lifecycle.onBeforeEvent({ + event, + renderDecisions, + personalization, + propositions + }); + + expect(event.mergeXdm).toHaveBeenCalledWith({ + _experience: { + decisioning: { + propositions: [ + { + id: "prop1", + scope: "prop1Scope", + scopeDetails: { a: 1 } + } + ], + propositionEventType: { + interact: 1 + } + } + } + }); + }); + it("should use the event type from _experience.decisioning.propositionEventType", () => { + const renderDecisions = false; + const personalization = {}; + const propositions = [ + { + id: "prop1", + scope: "prop1Scope", + scopeDetails: { a: 1 }, + extra: "extra" + } + ]; + event.getPropositionEventType.and.returnValue("interact"); + personalizationComponent.lifecycle.onBeforeEvent({ + event, + renderDecisions, + personalization, + propositions + }); + + expect(event.mergeXdm).toHaveBeenCalledWith({ + _experience: { + decisioning: { + propositions: [ + { + id: "prop1", + scope: "prop1Scope", + scopeDetails: { a: 1 } + } + ], + propositionEventType: { + interact: 1 + } + } + } + }); + }); }); describe("onBeforeRequest", () => { diff --git a/test/unit/specs/core/createEvent.spec.js b/test/unit/specs/core/createEvent.spec.js index 2ca57f2dc..46d48732c 100644 --- a/test/unit/specs/core/createEvent.spec.js +++ b/test/unit/specs/core/createEvent.spec.js @@ -228,6 +228,34 @@ describe("createEvent", () => { event.setUserXdm({ web: { webPageDetails: { viewName: "cart" } } }); expect(event.getViewName()).toBe("cart"); }); + it("returns undefined eventType", () => { + expect(event.getEventType()).toBeUndefined(); + }); + it("returns user XDM eventType", () => { + event.setUserXdm({ eventType: "myevent" }); + expect(event.getEventType()).toBe("myevent"); + }); + it("returns merged XDM eventType", () => { + event.mergeXdm({ eventType: "myevent" }); + expect(event.getEventType()).toBe("myevent"); + }); + it("returns an undefined proposition event type", () => { + expect(event.getPropositionEventType()).toBeUndefined(); + }); + it("returns a proposition event type", () => { + event.setUserXdm({ + _experience: { + decisioning: { + propositionEventType: { + otherField: "foo", + myevent: 1 + } + } + } + }); + expect(event.getPropositionEventType()).toBe("myevent"); + }); + describe("applyCallback", () => { it("can add fields to empty xdm", () => { const callback = ({ xdm, data }) => { diff --git a/test/unit/specs/core/createEventManager.spec.js b/test/unit/specs/core/createEventManager.spec.js index bb2ec31bd..99820ca21 100644 --- a/test/unit/specs/core/createEventManager.spec.js +++ b/test/unit/specs/core/createEventManager.spec.js @@ -117,6 +117,7 @@ describe("createEventManager", () => { renderDecisions: true, decisionScopes: undefined, personalization: undefined, + propositions: undefined, onResponse: jasmine.any(Function), onRequestFailure: jasmine.any(Function) }); From 38338e81211abd187cc718f06a2bdd1dd23604c6 Mon Sep 17 00:00:00 2001 From: Jon Snyder Date: Fri, 5 May 2023 14:17:16 -0600 Subject: [PATCH 2/3] Add Top/Bottom page to sandbox --- sandbox/src/App.js | 5 +++ sandbox/src/TopBottom.js | 47 +++++++++++++++++++++++++++++ sandbox/src/useSendPageViewEvent.js | 4 ++- 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 sandbox/src/TopBottom.js diff --git a/sandbox/src/App.js b/sandbox/src/App.js index 9c375f94d..12b4f6739 100755 --- a/sandbox/src/App.js +++ b/sandbox/src/App.js @@ -29,6 +29,7 @@ import RedirectedNewPage from "./RedirectedNewPage"; import PersonalizationAnalyticsClientSide from "./PersonalizationAnalyticsClientSide"; import PersonalizationFormBased from "./PersonalizationFormBased"; import Identity from "./Identity"; +import TopBottom from "./TopBottom"; import AlloyVersion from "./components/AlloyVersion"; function BasicExample() { @@ -92,6 +93,9 @@ function BasicExample() {
  • Identity
  • +
  • + Top/Bottom +

  • @@ -120,6 +124,7 @@ function BasicExample() { + diff --git a/sandbox/src/TopBottom.js b/sandbox/src/TopBottom.js new file mode 100644 index 000000000..8c6eba2e4 --- /dev/null +++ b/sandbox/src/TopBottom.js @@ -0,0 +1,47 @@ +import React, { useEffect } from "react"; +import ContentSecurityPolicy from "./components/ContentSecurityPolicy"; + +export default function TopBottom() { + useEffect(async () => { + let { propositions } = await window.alloy("sendEvent", { + renderDecisions: false, + personalization: { + decisionScopes: ["__view__"] + }, + xdm: { + eventType: "decisioning.propositionFetch" + } + }); + + ({ propositions } = await window.alloy("applyPropositions", { + propositions + })); + + await window.alloy("sendEvent", { + xdm: { + eventType: "page-view" + }, + propositions + }); + }, []); + + return ( +
    + +

    Top/Bottom Example

    +

    + This page tests rendering of activities using an AJO surface. If you + navigated here from another sandbox view, you will probably need to + refresh your browser because this is how to properly simulate a non-SPA + workflow. +

    +
    + This is the personalization placeholder. Personalized content has not + been loaded. +
    +
    + ); +} diff --git a/sandbox/src/useSendPageViewEvent.js b/sandbox/src/useSendPageViewEvent.js index 3a37550a1..fdf0b6c8d 100644 --- a/sandbox/src/useSendPageViewEvent.js +++ b/sandbox/src/useSendPageViewEvent.js @@ -23,7 +23,9 @@ export default ({ setPropositions } = {}) => { useEffect(() => { - xdm.eventType = "page-view"; + if (!xdm.eventType) { + xdm.eventType = "page-view"; + } if (viewName) { xdm.web = { From e3b2e7a8636351be573e4d5585012debe7bc18a9 Mon Sep 17 00:00:00 2001 From: Jon Snyder Date: Fri, 5 May 2023 15:03:21 -0600 Subject: [PATCH 3/3] Add functional test for sendEvent propositions option --- .../specs/Personalization/C11001566.js | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 test/functional/specs/Personalization/C11001566.js diff --git a/test/functional/specs/Personalization/C11001566.js b/test/functional/specs/Personalization/C11001566.js new file mode 100644 index 000000000..0f3dc1a0f --- /dev/null +++ b/test/functional/specs/Personalization/C11001566.js @@ -0,0 +1,63 @@ +import { t } from "testcafe"; +import createNetworkLogger from "../../helpers/networkLogger"; +import { responseStatus } from "../../helpers/assertions/index"; +import createFixture from "../../helpers/createFixture"; +import { + compose, + orgMainConfigMain, + debugEnabled +} from "../../helpers/constants/configParts"; +import { TEST_PAGE as TEST_PAGE_URL } from "../../helpers/constants/url"; +import createAlloyProxy from "../../helpers/createAlloyProxy"; + +const networkLogger = createNetworkLogger(); +const config = compose(orgMainConfigMain, debugEnabled); + +createFixture({ + title: + "C11001566: A display notification is sent when propositions are included in the sendEvent command", + url: `${TEST_PAGE_URL}?test=C11001566`, + requestHooks: [networkLogger.edgeEndpointLogs] +}); + +test.meta({ + ID: "C11001566", + SEVERITY: "P0", + TEST_RUN: "Regression" +}); + +test("Test C11001566: A display notification is sent when propositions are included in the sendEvent command", async () => { + const alloy = createAlloyProxy(); + await alloy.configure(config); + await alloy.sendEvent({ + propositions: [ + { + id: "C11001566", + scope: "myscope", + scopeDetails: { + type: "test" + } + } + ] + }); + + await responseStatus(networkLogger.edgeEndpointLogs.requests, 200); + await t.expect(networkLogger.edgeEndpointLogs.requests.length).eql(1); + + const sendEventRequest = networkLogger.edgeEndpointLogs.requests[0]; + const requestBody = JSON.parse(sendEventRequest.request.body); + await t.expect(requestBody.events[0].xdm._experience.decisioning).eql({ + propositionEventType: { + display: 1 + }, + propositions: [ + { + id: "C11001566", + scope: "myscope", + scopeDetails: { + type: "test" + } + } + ] + }); +});