diff --git a/.eslintrc.js b/.eslintrc.js index 5f73792de..2ba845d09 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -75,7 +75,8 @@ module.exports = { } ] } - ] + ], + "no-underscore-dangle": ["error", { allow: ["_experience"] }] }, globals: { expectAsync: "readonly", // newer jasmine feature diff --git a/sandbox/src/App.js b/sandbox/src/App.js index c4077ca67..18e59ecaf 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"; import ConfigOverrides from "./ConfigOverrides.jsx"; @@ -93,6 +94,9 @@ function BasicExample() {
  • Identity
  • +
  • + Top/Bottom +
  • Config Overrides
  • @@ -124,6 +128,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 6f72fb50e..6df90bb03 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 = { diff --git a/src/components/DataCollector/index.js b/src/components/DataCollector/index.js index d1f5c3bc2..2b6937144 100644 --- a/src/components/DataCollector/index.js +++ b/src/components/DataCollector/index.js @@ -33,6 +33,7 @@ const createDataCollector = ({ eventManager, logger }) => { decisionScopes = [], // Note: this option will soon be deprecated, please use personalization.decisionScopes instead personalization = {}, datasetId, + propositions = [], edgeConfigOverrides } = options; const event = eventManager.createEvent(); @@ -59,7 +60,8 @@ const createDataCollector = ({ eventManager, logger }) => { const sendEventOptions = { renderDecisions, decisionScopes, - personalization + personalization, + propositions }; if (edgeConfigOverrides) { diff --git a/src/components/DataCollector/validateUserEventOptions.js b/src/components/DataCollector/validateUserEventOptions.js index 9ba6e320c..d9d9e449c 100644 --- a/src/components/DataCollector/validateUserEventOptions.js +++ b/src/components/DataCollector/validateUserEventOptions.js @@ -34,6 +34,12 @@ export default ({ options }) => { }), datasetId: string(), mergeId: string(), + propositions: arrayOf( + objectOf({ + id: string().required(), + scope: string().required() + }) + ), edgeConfigOverrides: validateConfigOverride }) .required() 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 0d0ff6719..a2dc08210 100644 --- a/src/core/createEventManager.js +++ b/src/core/createEventManager.js @@ -54,6 +54,7 @@ export default ({ const { renderDecisions = false, decisionScopes, + propositions, edgeConfigOverrides: localConfigOverrides, personalization } = options; @@ -70,6 +71,7 @@ export default ({ renderDecisions, decisionScopes, personalization, + propositions, onResponse: onResponseCallbackAggregator.add, onRequestFailure: onRequestFailureCallbackAggregator.add }) diff --git a/test/functional/helpers/constants/configParts/configOverridesAlt.js b/test/functional/helpers/constants/configParts/configOverridesAlt.js index 478d1a469..4df0f5a92 100644 --- a/test/functional/helpers/constants/configParts/configOverridesAlt.js +++ b/test/functional/helpers/constants/configParts/configOverridesAlt.js @@ -1,3 +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. +*/ export default { com_adobe_experience_platform: { datasets: { diff --git a/test/functional/helpers/constants/configParts/configOverridesMain.js b/test/functional/helpers/constants/configParts/configOverridesMain.js index 03ec868ec..2c2e987bd 100644 --- a/test/functional/helpers/constants/configParts/configOverridesMain.js +++ b/test/functional/helpers/constants/configParts/configOverridesMain.js @@ -1,3 +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. +*/ export default { com_adobe_experience_platform: { datasets: { diff --git a/test/functional/specs/Config Overrides/C7437530.js b/test/functional/specs/Config Overrides/C7437530.js index c5c61b5b2..3b78312c4 100644 --- a/test/functional/specs/Config Overrides/C7437530.js +++ b/test/functional/specs/Config Overrides/C7437530.js @@ -1,3 +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. +*/ import { t } from "testcafe"; import createNetworkLogger from "../../helpers/networkLogger"; import { responseStatus } from "../../helpers/assertions/index"; diff --git a/test/functional/specs/Config Overrides/C7437531.js b/test/functional/specs/Config Overrides/C7437531.js index 585b511bd..035f39105 100644 --- a/test/functional/specs/Config Overrides/C7437531.js +++ b/test/functional/specs/Config Overrides/C7437531.js @@ -1,3 +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. +*/ import { t } from "testcafe"; import createNetworkLogger from "../../helpers/networkLogger"; import { responseStatus } from "../../helpers/assertions/index"; diff --git a/test/functional/specs/Config Overrides/C7437532.js b/test/functional/specs/Config Overrides/C7437532.js index 3451967c6..d150fb7c5 100644 --- a/test/functional/specs/Config Overrides/C7437532.js +++ b/test/functional/specs/Config Overrides/C7437532.js @@ -1,3 +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. +*/ import { t } from "testcafe"; import createNetworkLogger from "../../helpers/networkLogger"; import { responseStatus } from "../../helpers/assertions/index"; diff --git a/test/functional/specs/Config Overrides/C7437533.js b/test/functional/specs/Config Overrides/C7437533.js index c718a3354..6bec2d9d9 100644 --- a/test/functional/specs/Config Overrides/C7437533.js +++ b/test/functional/specs/Config Overrides/C7437533.js @@ -1,3 +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. +*/ import { t } from "testcafe"; import createNetworkLogger from "../../helpers/networkLogger"; import { responseStatus } from "../../helpers/assertions/index"; 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" + } + } + ] + }); +}); diff --git a/test/unit/specs/components/Audiences/injectProcessDestinations.spec.js b/test/unit/specs/components/Audiences/injectProcessDestinations.spec.js index 2334b30e7..e407315f4 100644 --- a/test/unit/specs/components/Audiences/injectProcessDestinations.spec.js +++ b/test/unit/specs/components/Audiences/injectProcessDestinations.spec.js @@ -3,174 +3,12 @@ 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. */ -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 - ); - }); -}); +// TODO +// eslint-disable-next-line no-unused-vars +import injectProcessDestinations from "../../../../../src/components/Audiences/injectProcessDestinations"; 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 2c73cdf13..d285480b0 100644 --- a/test/unit/specs/components/DataCollector/index.spec.js +++ b/test/unit/specs/components/DataCollector/index.spec.js @@ -65,7 +65,8 @@ describe("Event Command", () => { expect(eventManager.sendEvent).toHaveBeenCalledWith(event, { renderDecisions: true, decisionScopes: [], - personalization: {} + personalization: {}, + propositions: [] }); expect(result).toEqual("sendEventResult"); }); @@ -77,7 +78,8 @@ describe("Event Command", () => { decisionScopes: ["Foo1"], personalization: { decisionScopes: ["Foo2"] - } + }, + propositions: [] }; return sendEventCommand.run(options).then(result => { @@ -86,7 +88,8 @@ describe("Event Command", () => { decisionScopes: ["Foo1"], personalization: { decisionScopes: ["Foo2"] - } + }, + propositions: [] }); expect(result).toEqual("sendEventResult"); }); @@ -106,7 +109,8 @@ describe("Event Command", () => { decisionScopes: [], personalization: { surfaces: ["Foo1", "Foo2"] - } + }, + propositions: [] }); expect(result).toEqual("sendEventResult"); }); @@ -123,7 +127,8 @@ describe("Event Command", () => { expect(eventManager.sendEvent).toHaveBeenCalledWith(event, { renderDecisions: false, decisionScopes: [], - personalization: {} + personalization: {}, + propositions: [] }); }); }); @@ -165,6 +170,7 @@ describe("Event Command", () => { renderDecisions: false, decisionScopes: [], personalization: {}, + propositions: [], edgeConfigOverrides: { com_adobe_experience_platform: { datasets: { @@ -195,6 +201,7 @@ describe("Event Command", () => { renderDecisions: true, decisionScopes: [], personalization: {}, + propositions: [], edgeConfigOverrides: { target: { propertyToken: "hello" @@ -204,4 +211,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 c43a2c5f7..7ed590248 100644 --- a/test/unit/specs/core/createEventManager.spec.js +++ b/test/unit/specs/core/createEventManager.spec.js @@ -121,6 +121,7 @@ describe("createEventManager", () => { renderDecisions: true, decisionScopes: undefined, personalization: undefined, + propositions: undefined, onResponse: jasmine.any(Function), onRequestFailure: jasmine.any(Function) });