ttp-agent-sdk 2.43.0 → 2.45.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-widget.dev.js +5999 -2308
- package/dist/agent-widget.dev.js.map +1 -1
- package/dist/agent-widget.esm.js +1 -1
- package/dist/agent-widget.esm.js.map +1 -1
- package/dist/agent-widget.js +1 -1
- package/dist/agent-widget.js.map +1 -1
- package/dist/audio-processor.js +2 -2
- package/dist/demos/demo-sdk-loader.js +1 -0
- package/dist/demos/discovery.js +1 -0
- package/dist/demos/index.html +0 -13
- package/dist/demos/test-ecommerce.html +28 -45
- package/dist/demos/test-tour.html +19 -35
- package/dist/demos/test-widget.html +38 -18
- package/dist/demos/widget-customization-dev.html +15 -6
- package/dist/demos/widget-customization.html +14 -6
- package/dist/discovery.js +1 -0
- package/dist/examples/demo-sdk-loader.js +1 -0
- package/dist/examples/demo-v2.html +2 -2
- package/dist/examples/discovery.js +1 -0
- package/dist/examples/ocado-cart-network-probe.js +1 -0
- package/dist/examples/sobeys-cart-dispatch-probe.js +1 -0
- package/dist/examples/sobeys-cart-read-test.html +112 -0
- package/dist/examples/sobeys-cart-read-test.js +1 -0
- package/dist/examples/sobeys-cart-subscribe-probe.js +1 -0
- package/dist/examples/sobeys-cart-subscribe-test.html +168 -0
- package/dist/examples/sobeys-cart-subscribe-test.js +1 -0
- package/dist/examples/sobeys-inject-probe-min.js +1 -0
- package/dist/examples/sobeys-inject-probe.js +1 -0
- package/dist/examples/test-ecommerce.html +28 -45
- package/dist/examples/test-index.html +0 -13
- package/dist/examples/test-restaurant.html +4 -2
- package/dist/examples/test-tour.html +19 -35
- package/dist/examples/test-widget.html +38 -18
- package/dist/examples/widget-customization-dev.html +15 -6
- package/dist/examples/widget-customization.html +14 -6
- package/dist/hooks/noyhasade.js +1 -0
- package/examples/demo-sdk-loader.js +249 -0
- package/examples/demo-v2.html +2 -2
- package/examples/test-ecommerce.html +28 -45
- package/examples/test-index.html +0 -13
- package/examples/test-restaurant.html +4 -2
- package/examples/test-tour.html +19 -35
- package/examples/test-widget.html +38 -18
- package/examples/widget-customization-dev.html +15 -6
- package/examples/widget-customization.html +14 -6
- package/package.json +2 -2
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<title>Sobeys cart.read adapter test (mock Redux)</title>
|
|
6
|
+
<style>
|
|
7
|
+
body { font-family: system-ui, sans-serif; margin: 24px; max-width: 960px; }
|
|
8
|
+
pre { background: #111; color: #0f0; padding: 16px; overflow: auto; font-size: 13px; }
|
|
9
|
+
.pass { color: #0a0; font-weight: bold; }
|
|
10
|
+
.fail { color: #c00; font-weight: bold; }
|
|
11
|
+
button { padding: 8px 16px; margin-right: 8px; cursor: pointer; }
|
|
12
|
+
</style>
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<h1>Sobeys <code>cart.read</code> adapter test</h1>
|
|
16
|
+
<p>Mocks <code>shoppingListSlice</code> on <code>window.__reduxStore</code> (same path the live adapter uses).</p>
|
|
17
|
+
<button id="run">Run test</button>
|
|
18
|
+
<button id="seed">Seed cart item</button>
|
|
19
|
+
<button id="clear">Clear cart</button>
|
|
20
|
+
<div id="status"></div>
|
|
21
|
+
<pre id="out">Click Run test…</pre>
|
|
22
|
+
|
|
23
|
+
<script>
|
|
24
|
+
window.__TTP_SOBEYS_TEST__ = true;
|
|
25
|
+
// Mock Sobeys Redux store
|
|
26
|
+
(function initMockStore() {
|
|
27
|
+
const state = {
|
|
28
|
+
shoppingListSlice: {
|
|
29
|
+
listId: 'mock-list-42',
|
|
30
|
+
listName: 'My List',
|
|
31
|
+
shoppingListProducts: {
|
|
32
|
+
products: [
|
|
33
|
+
{
|
|
34
|
+
item_id: '577689_EA_0320',
|
|
35
|
+
product_type: 'Product',
|
|
36
|
+
quantity: 2,
|
|
37
|
+
name: 'Neilson 2% Milk',
|
|
38
|
+
brand: 'Neilson',
|
|
39
|
+
price: 5.49,
|
|
40
|
+
unit: '2 L',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
item_id: '123456_KG_0320',
|
|
44
|
+
product_type: 'Product',
|
|
45
|
+
quantity: 1,
|
|
46
|
+
name: 'Bananas',
|
|
47
|
+
price: 1.99,
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
taxSummary: { subtotal: 12.97, grandTotal: 14.65 },
|
|
52
|
+
},
|
|
53
|
+
productsSlice: { products: [] },
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
window.__reduxStore = {
|
|
57
|
+
getState: () => state,
|
|
58
|
+
dispatch: (action) => {
|
|
59
|
+
if (action.type === 'shoppingListSlice/setItemListData' && action.payload && action.payload.items) {
|
|
60
|
+
const map = {};
|
|
61
|
+
state.shoppingListSlice.shoppingListProducts.products.forEach((p) => {
|
|
62
|
+
map[p.item_id] = { ...p };
|
|
63
|
+
});
|
|
64
|
+
action.payload.items.forEach((item) => {
|
|
65
|
+
if (!item.item_id) return;
|
|
66
|
+
const qty = Number(item.quantity) || 0;
|
|
67
|
+
if (qty <= 0) delete map[item.item_id];
|
|
68
|
+
else map[item.item_id] = { ...map[item.item_id], ...item, quantity: qty };
|
|
69
|
+
});
|
|
70
|
+
state.shoppingListSlice.shoppingListProducts.products = Object.values(map);
|
|
71
|
+
}
|
|
72
|
+
return action;
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
})();
|
|
76
|
+
|
|
77
|
+
</script>
|
|
78
|
+
<script src="sobeys-cart-read-test.js"></script>
|
|
79
|
+
<script>
|
|
80
|
+
const out = document.getElementById('out');
|
|
81
|
+
const status = document.getElementById('status');
|
|
82
|
+
|
|
83
|
+
async function runTest() {
|
|
84
|
+
status.textContent = 'Running…';
|
|
85
|
+
const report = await window.__ttpSobeysCartReadTestReport();
|
|
86
|
+
out.textContent = JSON.stringify(report, null, 2);
|
|
87
|
+
status.innerHTML = report.pass
|
|
88
|
+
? '<span class="pass">PASS</span> — cart.read returned items'
|
|
89
|
+
: '<span class="fail">FAIL</span> — see JSON below';
|
|
90
|
+
return report;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
document.getElementById('run').onclick = runTest;
|
|
94
|
+
document.getElementById('seed').onclick = async () => {
|
|
95
|
+
window.__reduxStore.dispatch({
|
|
96
|
+
type: 'shoppingListSlice/setItemListData',
|
|
97
|
+
payload: { items: [{ item_id: '247400_EA_0320', product_type: 'Product', quantity: 1, name: 'Bread' }] },
|
|
98
|
+
});
|
|
99
|
+
await runTest();
|
|
100
|
+
};
|
|
101
|
+
document.getElementById('clear').onclick = async () => {
|
|
102
|
+
window.__reduxStore.dispatch({
|
|
103
|
+
type: 'shoppingListSlice/setItemListData',
|
|
104
|
+
payload: { items: [{ item_id: '577689_EA_0320', quantity: 0 }, { item_id: '123456_KG_0320', quantity: 0 }] },
|
|
105
|
+
});
|
|
106
|
+
await runTest();
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
runTest();
|
|
110
|
+
</script>
|
|
111
|
+
</body>
|
|
112
|
+
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(){"use strict";async function t(t){if(!window.__TTP_SOBEYS_TEST__&&!/(^|\.)sobeys\.com$/.test(location.hostname))return{ok:!1,success:!1,reason:"cross_origin_widget",host:location.hostname};function e(t){try{return t.getState().shoppingListSlice||{}}catch(t){return{}}}function n(t,e,n){const r={productName:"",brand:"",price:0,imageUrl:"",unit:""};if(!e||!n)return r;r.productName=String(e.name||e.product_name||e.productName||e.title||"").trim(),r.brand=String(e.brand||"").trim(),r.unit=String(e.unit||e.uom||e.packSize||"").trim(),r.imageUrl=String(e.imageUrl||e.image_url||e.image||"").trim();const i=Number(null!=e.price?e.price:e.unit_price);if(Number.isFinite(i)&&i>0&&(r.price=i),r.productName)return r;try{const e=t.getState(),i=[e.cartProductSlice,e.productsSlice,e.getShoppingListDataSlice,e.getProductDetailsDataSlice];for(let t=0;t<i.length;t++){const e=i[t];if(!e)continue;const o=Array.isArray(e)?e:e.products||e.items||e.data;if(Array.isArray(o))for(let t=0;t<o.length;t++){const e=o[t];if(!e)continue;if(String(e.item_id||e.objectID||e.id||e.productId||"")!==n)continue;r.productName=String(e.name||e.product_name||e.productName||"").trim();const i=Number(null!=e.price?e.price:e.unit_price);if(!r.price&&Number.isFinite(i)&&i>0&&(r.price=i),r.productName)return r}}}catch(t){}try{const t=document.querySelectorAll('[id^="ObjectID-'+n+'"]');for(let e=0;e<t.length;e++){const n=t[e].querySelector("a[title], a[aria-label]");if(!n)continue;const i=String(n.getAttribute("title")||n.getAttribute("aria-label")||"").trim();if(0===i.indexOf("Click here to go to")?r.productName=i.replace(/^Click here to go to\s+/i,"").replace(/\s+product detail page$/i,""):i&&(r.productName=i),r.productName)break}}catch(t){}return r}function r(t,e){return"KG"===(String(t||"").split("_")[1]||"")||e&&"weight"===String(e.product_type||"").toLowerCase()?"weight":"quantity"}const i=function(){if(window.__reduxStore&&"function"==typeof window.__reduxStore.dispatch)return window.__reduxStore;function t(t,e){const n=[t];for(;n.length&&e-- >0;){const t=n.shift();if(!t)continue;const e=t.memoizedProps&&t.memoizedProps.store||t.memoizedState&&t.memoizedState.store||t.pendingProps&&t.pendingProps.store||t.stateNode&&t.stateNode.store;if(e&&"function"==typeof e.dispatch)return e;t.child&&n.push(t.child),t.sibling&&n.push(t.sibling),t.return&&n.push(t.return)}return null}function e(e){if(!e)return null;const n=Object.keys(e).filter(function(t){return 0===t.indexOf("__reactFiber")||0===t.indexOf("__reactContainer")||0===t.indexOf("__reactInternalInstance")});for(let r=0;r<n.length;r++){const i=t(e[n[r]]&&(e[n[r]].current||e[n[r]])||null,800);if(i)return i}return null}const n=[document.getElementById("root"),document.getElementById("app"),document.body,document.body&&document.body.firstElementChild].filter(Boolean);for(let t=0;t<n.length;t++){const r=e(n[t]);if(r)return r}const r=document.querySelectorAll("body *");for(let t=0;t<r.length;t++){const n=e(r[t]);if(n)return n}return null}();if(!i)return{ok:!1,success:!1,reason:"redux_store_not_found"};const o=e(i),c=function(t){const n=e(t),r=n.shoppingListProducts&&n.shoppingListProducts.products;return Array.isArray(r)?r:[]}(i),u=o.taxSummary||{};if(0===c.length)return{ok:!0,success:!0,items:[],itemCount:0,total:Number(null!=u.grandTotal?u.grandTotal:u.subtotal)||0,currency:"CAD",checkoutUrl:"",listId:o.listId||"",cartId:o.listId||null};const s=[];let a=0,d=0,l=0;for(let t=0;t<c.length;t++){const e=c[t];if(!e||!e.item_id)continue;const o=String(e.item_id),u=Number(e.quantity)||0;if(u<=0)continue;const m=n(i,e,o);m.productName||l++;const p=m.price||0,f=p>0?+(p*u).toFixed(2):0;f>0&&(d+=f),a+=u,s.push({productId:o,productName:m.productName,brand:m.brand,unit:m.unit,imageUrl:m.imageUrl,quantity:u,price:p,subtotal:f,sellBy:r(o,e),product_type:e.product_type||"Product"})}const m=Number(null!=u.grandTotal?u.grandTotal:u.subtotal),p={ok:!0,success:!0,items:s,itemCount:a,total:Number.isFinite(m)&&m>0?m:d>0?+d.toFixed(2):0,currency:"CAD",checkoutUrl:"",listId:o.listId||"",cartId:o.listId||null,listName:o.listName||""};return l>0&&l===s.length?p.degraded="no_product_names":l>0&&(p.degraded="partial_product_names"),p}window.__ttpSobeysCartRead=t,window.__ttpSobeysCartReadTestReport=async function(){window.fetch.bind(window),console.log.bind(console,"[adapter:cart.read]");const e=await t();let n=null;if(0===(e.items||[]).length){const t=function(){const t=Array.from(document.querySelectorAll('[id^="ObjectID-"]')).find(function(t){return-1===t.id.indexOf("undefined")&&t.querySelector('button[aria-label="Add to list"]')});if(!t)return null;const e=t.id.match(/^ObjectID-(.+?) OfferType-/);return e?e[1]:null}()||"247400_EA_0320",e=function(){const t=document.getElementById("root")||document.body.firstElementChild;if(!t)return null;const e=Object.keys(t).find(function(t){return 0===t.indexOf("__reactFiber")||0===t.indexOf("__reactContainer")});return e?function(t,e){const n=[t];for(;n.length&&e-- >0;){const t=n.shift();if(!t)continue;const e=t.memoizedProps&&t.memoizedProps.store||t.memoizedState&&t.memoizedState.store||t.pendingProps&&t.pendingProps.store;if(e&&"function"==typeof e.dispatch)return e;t.child&&n.push(t.child),t.sibling&&n.push(t.sibling),t.return&&n.push(t.return)}return null}(t[e].current||t[e],800):null}();e&&(e.dispatch({type:"shoppingListSlice/setItemListData",payload:{items:[{item_id:t,product_type:"Product",quantity:1}]}}),await new Promise(function(t){setTimeout(t,500)}),n={objectId:t,via:"redux_dispatch"})}const r=await t(),i={hostname:location.hostname,emptyCartRead:e,seeded:n,cartRead:r,pass:!!(r.ok&&r.success&&(r.itemCount||0)>0&&(r.items||[]).length>0)};return console.log("[SOBEYS-CART-READ-TEST]",i),i},window.__ttpSobeysCartReadTestReport()}();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(){"use strict";async function t(){const t={hostname:location.hostname,hostOk:/(^|\.)sobeys\.com$/.test(location.hostname),subscribeInstalled:!!window.__ttpSobeysCartSubscribeInstalled,scheduleRefresh:"function"==typeof window.__ttpSobeysCartScheduleRefresh,dispatchPatched:!1,persistPoll:!!window.__ttpSobeysPersistPollId,suppressedCount:window.__ttpSobeysSuppressedIds?window.__ttpSobeysSuppressedIds.size:0,prevMapSize:window.__ttpSobeysPrevItemMap?window.__ttpSobeysPrevItemMap.size:null,ecommerce:void 0!==window.__TTP_ECOMMERCE__,bundleActions:window.__TTP_ECOMMERCE__&&window.__TTP_ECOMMERCE__.runner?[...window.__TTP_ECOMMERCE__.runner.adapters?.keys?.()||[]]:[],persistFingerprint:"",reduxLines:[]},e=function(){if(window.__reduxStore)return window.__reduxStore;const t=document.getElementById("root")||document.body.firstElementChild;if(!t)return null;const e=Object.keys(t).find(function(t){return 0===t.indexOf("__reactFiber")||0===t.indexOf("__reactContainer")});if(!e)return null;const n=[t[e].current||t[e]];let i=400;for(;n.length&&i-- >0;){const t=n.shift();if(!t)continue;const e=t.memoizedProps&&t.memoizedProps.store||t.memoizedState&&t.memoizedState.store;if(e&&"function"==typeof e.dispatch)return e;t.child&&n.push(t.child),t.sibling&&n.push(t.sibling)}return null}();if(e){t.dispatchPatched=!!e.__ttpSobeysDispatchPatched;try{const n=e.getState().shoppingListSlice?.shoppingListProducts?.products||[];t.reduxLines=n.map(function(t){return{item_id:t.item_id,quantity:t.quantity}})}catch(e){t.reduxError=String(e.message||e)}}else t.reduxStore="not_found";try{const e=sessionStorage.getItem("persist:root");if(e){const n=JSON.parse(e),i=JSON.parse(n.shoppingListSlice||"{}"),s=i.shoppingListProducts?.products||[];t.persistFingerprint=s.map(function(t){return t.item_id+":"+t.quantity}).join("|")}}catch(e){t.persistError=String(e.message||e)}return window.__TTP_ECOMMERCE__&&window.__TTP_ECOMMERCE__.runAdapter&&(t.manualSubscribe=await window.__TTP_ECOMMERCE__.runAdapter("cart.subscribe",{}),t.cartRead=await window.__TTP_ECOMMERCE__.runAdapter("cart.read",{})),console.log("[SOBEYS-CART-SUBSCRIBE-PROBE]",t),t}window.__ttpSobeysCartSubscribeProbe=t,t()}();
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<title>Sobeys cart.subscribe test</title>
|
|
6
|
+
<style>
|
|
7
|
+
body { font-family: system-ui, sans-serif; margin: 24px; max-width: 960px; }
|
|
8
|
+
pre { background: #111; color: #0f0; padding: 16px; overflow: auto; font-size: 12px; }
|
|
9
|
+
button { margin: 4px 8px 4px 0; padding: 8px 12px; cursor: pointer; }
|
|
10
|
+
.pass { color: #0a0; font-weight: bold; }
|
|
11
|
+
.fail { color: #c00; font-weight: bold; }
|
|
12
|
+
</style>
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<h1>Sobeys <code>cart.subscribe</code> + <code>cart.add</code> integration test</h1>
|
|
16
|
+
<p>Mocks Redux store, cart.read adapter, and <code>netcost:partner-action:cart_mutation</code>.</p>
|
|
17
|
+
<button id="runAll">Run all tests</button>
|
|
18
|
+
<button id="manualAdd">Simulate manual site add</button>
|
|
19
|
+
<button id="voiceAdd">Simulate voice cart.add (with suppression)</button>
|
|
20
|
+
<div id="status"></div>
|
|
21
|
+
<pre id="out">Click Run all tests…</pre>
|
|
22
|
+
|
|
23
|
+
<script>
|
|
24
|
+
window.__TTP_SOBEYS_TEST__ = true;
|
|
25
|
+
|
|
26
|
+
const state = {
|
|
27
|
+
shoppingListSlice: {
|
|
28
|
+
listId: 'mock-list',
|
|
29
|
+
shoppingListProducts: { products: [] },
|
|
30
|
+
taxSummary: { grandTotal: 0 },
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
window.__reduxStore = {
|
|
35
|
+
getState: () => state,
|
|
36
|
+
_subs: [],
|
|
37
|
+
subscribe(fn) {
|
|
38
|
+
this._subs.push(fn);
|
|
39
|
+
return () => { this._subs = this._subs.filter((f) => f !== fn); };
|
|
40
|
+
},
|
|
41
|
+
dispatch(action) {
|
|
42
|
+
if (action.type === 'shoppingListSlice/setItemListData' && action.payload && action.payload.items) {
|
|
43
|
+
const map = {};
|
|
44
|
+
state.shoppingListSlice.shoppingListProducts.products.forEach((p) => { map[p.item_id] = { ...p }; });
|
|
45
|
+
action.payload.items.forEach((item) => {
|
|
46
|
+
const qty = Number(item.quantity) || 0;
|
|
47
|
+
if (qty <= 0) delete map[item.item_id];
|
|
48
|
+
else map[item.item_id] = { ...map[item.item_id], ...item, quantity: qty };
|
|
49
|
+
});
|
|
50
|
+
state.shoppingListSlice.shoppingListProducts.products = Object.values(map);
|
|
51
|
+
}
|
|
52
|
+
this._subs.forEach((fn) => { try { fn(); } catch (e) {} });
|
|
53
|
+
return action;
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
async function mockCartRead() {
|
|
58
|
+
const products = state.shoppingListSlice.shoppingListProducts.products || [];
|
|
59
|
+
const items = products.filter((p) => (Number(p.quantity) || 0) > 0).map((p) => ({
|
|
60
|
+
productId: p.item_id,
|
|
61
|
+
productName: p.name || '',
|
|
62
|
+
quantity: Number(p.quantity) || 0,
|
|
63
|
+
price: Number(p.price) || 0,
|
|
64
|
+
subtotal: 0,
|
|
65
|
+
sellBy: String(p.item_id).indexOf('_KG_') >= 0 ? 'weight' : 'quantity',
|
|
66
|
+
}));
|
|
67
|
+
const itemCount = items.reduce((n, i) => n + i.quantity, 0);
|
|
68
|
+
return { ok: true, success: true, items, itemCount, total: 0, currency: 'CAD' };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const emits = [];
|
|
72
|
+
|
|
73
|
+
async function installSubscribe() {
|
|
74
|
+
// Reset for repeatable test runs
|
|
75
|
+
if (typeof window.__ttpSobeysStoreUnsub === 'function') {
|
|
76
|
+
window.__ttpSobeysStoreUnsub();
|
|
77
|
+
window.__ttpSobeysStoreUnsub = null;
|
|
78
|
+
}
|
|
79
|
+
window.__ttpSobeysCartSubscribeInstalled = false;
|
|
80
|
+
window.__ttpSobeysPrevItemMap = new Map();
|
|
81
|
+
window.__ttpSobeysSuppressedIds = new Map();
|
|
82
|
+
|
|
83
|
+
if (!window.__ttpSobeysCartSubscribeInstall) {
|
|
84
|
+
const script = document.createElement('script');
|
|
85
|
+
script.src = 'sobeys-cart-subscribe-test.js';
|
|
86
|
+
document.head.appendChild(script);
|
|
87
|
+
await new Promise((r) => { script.onload = r; });
|
|
88
|
+
}
|
|
89
|
+
const ctx = {
|
|
90
|
+
args: {},
|
|
91
|
+
log: (...a) => console.log('[adapter:cart.subscribe]', ...a),
|
|
92
|
+
emit: (payload) => { emits.push(payload); },
|
|
93
|
+
runAdapter: async (action) => {
|
|
94
|
+
if (action === 'cart.read') return mockCartRead();
|
|
95
|
+
return { ok: false, reason: 'unknown' };
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
await window.__ttpSobeysCartSubscribeInstall(ctx);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function firePartnerMutation(args) {
|
|
102
|
+
window.dispatchEvent(new CustomEvent('netcost:partner-action:cart_mutation', {
|
|
103
|
+
detail: { action: 'cart.add', args },
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function voiceCartAdd(sku, qty) {
|
|
108
|
+
firePartnerMutation({ sku, qty, name: 'voice item' });
|
|
109
|
+
window.__reduxStore.dispatch({
|
|
110
|
+
type: 'shoppingListSlice/setItemListData',
|
|
111
|
+
payload: { items: [{ item_id: sku, product_type: 'Product', quantity: qty, name: 'voice item' }] },
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function manualSiteAdd(sku, name) {
|
|
116
|
+
window.__reduxStore.dispatch({
|
|
117
|
+
type: 'shoppingListSlice/setItemListData',
|
|
118
|
+
payload: { items: [{ item_id: sku, product_type: 'Product', quantity: 1, name }] },
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function runAllTests() {
|
|
123
|
+
emits.length = 0;
|
|
124
|
+
state.shoppingListSlice.shoppingListProducts.products = [];
|
|
125
|
+
await installSubscribe();
|
|
126
|
+
await new Promise((r) => setTimeout(r, 700));
|
|
127
|
+
|
|
128
|
+
const initialCount = emits.filter((e) => e.t === 'cart_change_event' && !e.change).length;
|
|
129
|
+
emits.length = 0;
|
|
130
|
+
|
|
131
|
+
voiceCartAdd('577689_EA_0320', 1);
|
|
132
|
+
await new Promise((r) => setTimeout(r, 700));
|
|
133
|
+
const voiceEmits = emits.filter((e) => e.change).length;
|
|
134
|
+
|
|
135
|
+
manualSiteAdd('247400_EA_0320', 'Bread');
|
|
136
|
+
await new Promise((r) => setTimeout(r, 700));
|
|
137
|
+
const manualEmits = emits.filter((e) => e.change && e.change.added.some((a) => a.id === '247400_EA_0320')).length;
|
|
138
|
+
|
|
139
|
+
const report = {
|
|
140
|
+
initialSnapshot: initialCount === 1,
|
|
141
|
+
voiceAddSuppressed: voiceEmits === 0,
|
|
142
|
+
voiceAddCacheOnly: emits.some(function (e) {
|
|
143
|
+
return e.t === 'cart_change_event' && !e.change &&
|
|
144
|
+
(e.cart && e.cart.items || []).some(function (i) { return i.productId === '577689_EA_0320'; });
|
|
145
|
+
}),
|
|
146
|
+
manualAddEmitted: manualEmits >= 1,
|
|
147
|
+
pass: initialCount === 1 && voiceEmits === 0 && manualEmits >= 1,
|
|
148
|
+
emits,
|
|
149
|
+
};
|
|
150
|
+
document.getElementById('out').textContent = JSON.stringify(report, null, 2);
|
|
151
|
+
document.getElementById('status').innerHTML = report.pass
|
|
152
|
+
? '<span class="pass">PASS</span>'
|
|
153
|
+
: '<span class="fail">FAIL</span>';
|
|
154
|
+
return report;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
document.getElementById('runAll').onclick = runAllTests;
|
|
158
|
+
document.getElementById('manualAdd').onclick = async () => {
|
|
159
|
+
await installSubscribe();
|
|
160
|
+
manualSiteAdd('999999_EA_0320', 'Manual Product');
|
|
161
|
+
};
|
|
162
|
+
document.getElementById('voiceAdd').onclick = async () => {
|
|
163
|
+
await installSubscribe();
|
|
164
|
+
voiceCartAdd('888888_EA_0320', 1);
|
|
165
|
+
};
|
|
166
|
+
</script>
|
|
167
|
+
</body>
|
|
168
|
+
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
window.__ttpSobeysCartSubscribeInstall=async t=>{if(!window.__TTP_SOBEYS_TEST__&&!/(^|\.)sobeys\.com$/.test(location.hostname))return{ok:!1,reason:"cross_origin_widget",host:location.hostname};if("function"!=typeof t.emit)return t.log("ctx.emit not available — falling back to on-demand get_cart"),{ok:!0,subscribed:!1,reason:"emit_unavailable"};window.__ttpSobeysSuppressedIds||(window.__ttpSobeysSuppressedIds=new Map);const e=window.__ttpSobeysSuppressedIds;function n(t){if(!t)return!1;const n=String(t).trim(),o=e.get(n);return!(!o||Date.now()>o&&(e.delete(n),1))}function o(){if(window.__reduxStore&&"function"==typeof window.__reduxStore.dispatch)return window.__reduxStore;function t(t,e){const n=[t];for(;n.length&&e-- >0;){const t=n.shift();if(!t)continue;const e=t.memoizedProps&&t.memoizedProps.store||t.memoizedState&&t.memoizedState.store||t.pendingProps&&t.pendingProps.store||t.stateNode&&t.stateNode.store;if(e&&"function"==typeof e.dispatch)return e;t.child&&n.push(t.child),t.sibling&&n.push(t.sibling),t.return&&n.push(t.return)}return null}function e(e){if(!e)return null;const n=Object.keys(e).filter(function(t){return 0===t.indexOf("__reactFiber")||0===t.indexOf("__reactContainer")||0===t.indexOf("__reactInternalInstance")});for(let o=0;o<n.length;o++){const r=n[o],i=t(e[r]&&(e[r].current||e[r])||null,800);if(i)return i}return null}const n=[document.getElementById("root"),document.getElementById("app"),document.body,document.body&&document.body.firstElementChild].filter(Boolean);for(let t=0;t<n.length;t++){const o=e(n[t]);if(o)return o}const o=document.querySelectorAll("body *");for(let t=0;t<Math.min(o.length,200);t++){const n=e(o[t]);if(n)return n}return null}function r(t){const e=new Map,n=t&&t.shoppingListSlice&&t.shoppingListSlice.shoppingListProducts&&t.shoppingListSlice.shoppingListProducts.products;if(!Array.isArray(n))return e;for(let t=0;t<n.length;t++){const o=n[t];if(!o||!o.item_id)continue;const r=Number(o.quantity)||0;r<=0||e.set(String(o.item_id),{qty:r,name:String(o.name||o.product_name||o.productName||"").trim()})}return e}async function i(){window.__ttpSobeysCartReadInFlight=(window.__ttpSobeysCartReadInFlight||0)+1;try{const e=await t.runAdapter("cart.read",{});return e&&!1!==e.ok?e:null}catch(e){return t.log("[cart.subscribe] cart.read error:",String(e&&e.message||e)),null}finally{window.__ttpSobeysCartReadInFlight=Math.max(0,(window.__ttpSobeysCartReadInFlight||1)-1)}}function s(){return window.__ttpSobeysPrevItemMap||new Map}function c(t){window.__ttpSobeysPrevItemMap=t}function a(e,a){window.__ttpSobeysDebounceTimer&&clearTimeout(window.__ttpSobeysDebounceTimer),window.__ttpSobeysDebounceTimer=setTimeout(function(){window.__ttpSobeysDebounceTimer=null,async function(e,a){const u=o(),d=r(u?u.getState():{});let l=s();if(a){const n=await i();return n?(c(d),t.emit({t:"cart_change_event",cart:n}),void t.log("[cart.subscribe] initial snapshot lines="+d.size+" itemCount="+(n.itemCount||0))):void t.log("[cart.subscribe] cart.read null — skip initial emit (source="+e+")")}const p=function(t,e){const n=[],o=[],r=[];for(const o of e){const e=o[0],i=o[1],s=t.get(e);s?s.qty!==i.qty&&r.push({id:e,name:i.name,from:s.qty,to:i.qty}):n.push({id:e,qty:i.qty,name:i.name})}for(const n of t){const t=n[0],r=n[1];e.has(t)||o.push({id:t,qty:r.qty,name:r.name})}return{added:n,removed:o,qtyChanged:r}}(l,d),_=p.added.filter(function(t){return!n(t.id)}),b=p.removed.filter(function(t){return!n(t.id)}),f=p.qtyChanged.filter(function(t){return!n(t.id)});if(t.log("[cart.subscribe] diff source="+e+" raw added="+p.added.length+" removed="+p.removed.length+" qty="+p.qtyChanged.length+" filtered added="+_.length+" removed="+b.length+" qty="+f.length),c(d),!_.length&&!b.length&&!f.length)return void t.log("[cart.subscribe] no emit (suppressed or no cart diff)");const w=await i();w?(t.log("[cart.subscribe] emitting cart_change_event"),t.emit({t:"cart_change_event",cart:w,change:{type:_.length?"add":b.length?"remove":"qty",added:_,removed:b,qtyChanged:f,at:Date.now()}})):t.log("[cart.subscribe] cart.read null — skip emit (source="+e+")")}(e,!!a).catch(function(e){t.log("[cart.subscribe] flush failed:",String(e&&e.message||e))})},500)}function u(){try{const t=sessionStorage.getItem("persist:root");if(!t)return"";const e=JSON.parse(t),n=e.shoppingListSlice?JSON.parse(e.shoppingListSlice):null,o=n&&n.shoppingListProducts&&n.shoppingListProducts.products;return Array.isArray(o)?o.map(function(t){return String(t.item_id||"")+":"+String(t.quantity||0)}).sort().join("|"):"empty"}catch(t){return""}}window.__ttpSobeysSuppressListenerBound||(window.__ttpSobeysSuppressListenerBound=!0,window.addEventListener("netcost:partner-action:cart_mutation",function(t){"function"==typeof window.__ttpSobeysOnPartnerMutation&&window.__ttpSobeysOnPartnerMutation(t)})),window.__ttpSobeysOnPartnerMutation=function(n){const o=function(t){if(!t||"object"!=typeof t)return[];const e=[],n=function(t){if(null==t||""===t)return;const n=String(t).trim();n&&e.push(n)};if(n(t.productId),n(t.sku),Array.isArray(t.items))for(let e=0;e<t.items.length;e++){const o=t.items[e];o&&"object"==typeof o&&(n(o.productId),n(o.sku),n(o.item_id))}return e}(n&&n.detail&&n.detail.args||{}),r=Date.now()+8e3;o.forEach(function(t){e.set(t,r)}),t.log("[cart.subscribe] suppressing productIds="+o.join(",")+" for 8000ms")},window.__ttpSobeysCartSubscribeInstalled?(t.log("[cart.subscribe] re-wiring refresh handler for new session ctx"),"function"==typeof window.__ttpSobeysStoreUnsub&&(window.__ttpSobeysStoreUnsub(),window.__ttpSobeysStoreUnsub=null),window.__ttpSobeysPersistPollId&&(clearInterval(window.__ttpSobeysPersistPollId),window.__ttpSobeysPersistPollId=null),window.__ttpSobeysDebounceTimer&&(clearTimeout(window.__ttpSobeysDebounceTimer),window.__ttpSobeysDebounceTimer=null)):window.__ttpSobeysCartSubscribeInstalled=!0,window.__ttpSobeysPrevItemMap||(window.__ttpSobeysPrevItemMap=new Map),window.__ttpSobeysCartScheduleRefresh=a;const d=await async function(){const e=Date.now()+3e4;let n=0;for(;Date.now()<e;){n++;const e=o();if(e)return t.log("[cart.subscribe] redux store found after "+n+" attempt(s)"),e;await new Promise(function(t){setTimeout(t,250)})}return t.log("[cart.subscribe] redux store not found after "+n+" attempts / 30000ms"),null}();if(!d)return{ok:!1,reason:"redux_store_not_found",detail:"timeout_30000ms"};!function(e){if(!e||e.__ttpSobeysDispatchPatched)return;e.__ttpSobeysDispatchPatched=!0;const n=e.dispatch.bind(e);e.dispatch=function(e){const o=n(e);try{const n=e&&"object"==typeof e?e.type:null;n&&0===String(n).indexOf("shoppingListSlice/")&&(t.log("[cart.subscribe] dispatch-hit "+n),a("dispatch",!1))}catch(t){}return o},t.log("[cart.subscribe] dispatch hook installed on redux store")}(d),c(r(d.getState()));const l=d.subscribe(function(){const t=r(d.getState());(function(t,e){if(t.size!==e.size)return!1;for(const n of t){const t=n[0],o=n[1],r=e.get(t);if(!r||r.qty!==o.qty)return!1}return!0})(s(),t)||a("redux",!1)});return window.__ttpSobeysStoreUnsub=l,window.__ttpSobeysPersistPollId||(window.__ttpSobeysPersistFingerprint=u(),window.__ttpSobeysPersistPollId=setInterval(function(){const e=u();e!==window.__ttpSobeysPersistFingerprint&&(window.__ttpSobeysPersistFingerprint=e,t.log("[cart.subscribe] persist:root fingerprint changed"),a("persist",!1))},400),t.log("[cart.subscribe] sessionStorage persist:root poll installed")),a("initial",!0),{ok:!0,listening:!0,strategy:"redux_dispatch_hook + subscribe + persist_poll",autoRunExpected:!0}};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(){"use strict";const t="ACSYSHF8AU",e="dxp_product_en";function n(t){const e=String(t||"").replace(/\D/g,"");return e?e.length>=4?e.slice(-4):e.padStart(4,"0"):null}function o(t){return t.includes("algolia.net")&&t.includes("/indexes/")}async function r(n,o,r){const i="https://"+t+"-dsn.algolia.net/1/indexes/*/queries?x-algolia-api-key=fe555974f588b3e76ad0f1c548113b22&x-algolia-application-id="+t,s=JSON.stringify({requests:[{indexName:e,query:n,hitsPerPage:r||3,clickAnalytics:!0,filters:"storeId:"+o+" AND isVisible:true AND isMassOffers:false",analyticsTags:["A","website"]}]}),a=await fetch(i,{method:"POST",credentials:"omit",headers:{Accept:"application/json","Content-Type":"application/x-www-form-urlencoded"},body:s});return(((await a.json()).results||[])[0]||{}).hits||[]}function i(t){const e=new URL(location.origin+"/");e.searchParams.set("query",t),e.searchParams.set("tab","products"),history.pushState(null,"",e.toString()),window.dispatchEvent(new PopStateEvent("popstate",{state:null}))}function s(){return document.querySelectorAll('[id^="ObjectID-"]').length}function a(t){return new Promise(function(e){setTimeout(e,t)})}window.__ttpSobeysInjectProbeMin=async function(t){const c=(t=t||{}).hookMode||"both",u=function(){try{const t=JSON.parse(localStorage.getItem("store")||"{}"),e=n(t.storeNumber||t.objectID);if(e)return e}catch(t){}const t=document.cookie.match(/(?:^|;\s*)storeId=([^;]+)/);return t?n(decodeURIComponent(t[1]).split("_")[0]):null}();if(!u)throw new Error("Pick a store first");const l={transport:[],forged:[],hookMode:c,sid:u};if(!(await r("coffee",u,1)).length)throw new Error("Algolia donor failed");const d=[];for(const e of t.queries||["tomatoes","cucumbers"]){const t=await r(e,u,1);if(!t.length)throw new Error("No hits for "+e);d.push(t[0])}const p="spcrt-probe-"+Math.random().toString(36).slice(2,8),f={prefix:p,revision:0,algoliaHits:[],meta:{},index:e};function h(){const t=f.revision?p+"-r"+f.revision:p;return JSON.stringify({results:[{hits:f.algoliaHits,nbHits:f.algoliaHits.length,page:0,nbPages:f.algoliaHits.length?1:0,hitsPerPage:50,exhaustiveNbHits:!0,query:t,index:e,processingTimeMS:1}]})}function g(t,e){return o(t)&&(function(t,e){try{const n=JSON.parse(t).requests;return Array.isArray(n)&&n.some(function(t){return String(t.query||"").includes(e)})}catch(t){return!1}}(e,p)||t.includes(encodeURIComponent(p)))}function y(t,e,n,o,r){const i={kind:e,forged:!!r,q:function(){try{return JSON.parse(o).requests[0].query}catch(t){return null}}()};l[t].push(i)}window.__speacart_inject_state=window.__speacart_inject_state||{},window.__speacart_inject_state[p]=f;const w=window.fetch;"fetch"!==c&&"both"!==c||(window.fetch=function(t,e){const n="string"==typeof t?t:t&&t.url||"",r=e&&null!=e.body?String(e.body):"";return o(n)&&y("transport","fetch",0,r,!1),g(n,r)?(y("forged","fetch",0,r,!0),Promise.resolve(new Response(h(),{status:200,headers:{"content-type":"application/json"}}))):w.apply(this,arguments)});const b=XMLHttpRequest.prototype.open,S=XMLHttpRequest.prototype.send;"xhr"!==c&&"both"!==c||(XMLHttpRequest.prototype.open=function(t,e){return this.__u=e,b.apply(this,arguments)},XMLHttpRequest.prototype.send=function(t){const e=this.__u||"",n=null!=t?String(t):"";if(o(e)&&y("transport","xhr",0,n,!1),g(e,n)){y("forged","xhr",0,n,!0);const t=this,e=h();return void setTimeout(function(){try{Object.defineProperty(t,"readyState",{configurable:!0,writable:!0,value:4}),Object.defineProperty(t,"status",{configurable:!0,writable:!0,value:200}),Object.defineProperty(t,"responseText",{configurable:!0,writable:!0,value:e}),Object.defineProperty(t,"response",{configurable:!0,writable:!0,value:e})}catch(t){}t.dispatchEvent(new Event("readystatechange")),t.dispatchEvent(new Event("load")),t.dispatchEvent(new Event("loadend"))},0)}return S.apply(this,arguments)});const _=s();i(p),await a(2e3);const m=s();for(const t of d)f.algoliaHits.push(JSON.parse(JSON.stringify(t))),f.revision++,i(p+"-r"+f.revision),await a(2e3);const v=s(),O=l.transport.reduce(function(t,e){return t[e.kind]=(t[e.kind]||0)+1,t},{}),E=v>0?"SUCCESS":0===l.forged.length?"NO_FORGE — Sobeys did not Algolia-query the sentinel; try hookMode both":"FORGED_BUT_NO_TILES — hook works, React/grid issue",I={ok:v>0,hookMode:c,prefix:p,sid:u,tiles:{before:_,afterInit:m,afterAppend:v},hitsInState:f.algoliaHits.length,productIds:f.algoliaHits.map(function(t){return t.objectID}),transportByKind:O,forgedCount:l.forged.length,transport:l.transport,forged:l.forged,url:location.href,verdict:E};return console.log("[SOBEYS-INJECT-MIN]",I),window.__ttpSobeysInjectProbeMinLast=I,I},console.log("[SOBEYS-INJECT-MIN] ready — await __ttpSobeysInjectProbeMin()")}();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(){"use strict";if(window.__ttpSobeysInjectProbeInstalled)return void console.warn("[SOBEYS-INJECT-PROBE] already installed");const t="ACSYSHF8AU",e="dxp_product_en",o=[],n=[];let r=null,i=!1;function s(t){if(null==t||""===t)return null;const e=String(t).replace(/\D/g,"");return e?e.length>=4?e.slice(-4):e.padStart(4,"0"):null}function a(t){return"string"==typeof t&&-1!==t.indexOf("algolia.net")&&-1!==t.indexOf("/indexes/")}async function c(o,n,r){const i="https://"+t+"-dsn.algolia.net/1/indexes/*/queries?x-algolia-agent="+encodeURIComponent("Algolia for JavaScript (5.52.1); Search (5.52.1); Browser")+"&x-algolia-api-key=fe555974f588b3e76ad0f1c548113b22&x-algolia-application-id="+t,s={requests:[{indexName:e,query:String(o).trim(),hitsPerPage:r||3,clickAnalytics:!0,userToken:"",filters:"storeId:"+n+" AND isVisible:true AND isMassOffers:false",analyticsTags:["A","website"]}]},a=await fetch(i,{method:"POST",credentials:"omit",headers:{Accept:"application/json","Content-Type":"application/x-www-form-urlencoded"},body:JSON.stringify(s)});if(!a.ok)throw new Error("algolia "+a.status);const c=await a.json(),l=c&&c.results&&c.results[0]||{};return{hits:Array.isArray(l.hits)?l.hits:[],meta:{processingTimeMS:l.processingTimeMS,hitsPerPage:l.hitsPerPage,exhaustiveNbHits:l.exhaustiveNbHits}}}function l(){return document.querySelectorAll('[id^="ObjectID-"]').length}function p(t){return new Promise(function(e){setTimeout(e,t)})}function u(t){const e=new URL(location.origin+"/");e.searchParams.set("query",t),e.searchParams.set("tab","products"),history.pushState(null,"",e.toString()),window.dispatchEvent(new PopStateEvent("popstate",{state:null}))}function h(t,e,o,i){const s={at:(new Date).toISOString(),kind:t,url:String(e).slice(0,120),forged:!!i,bodyHasSentinel:!!o&&-1!==o.indexOf(r&&r.prefix)};return n.push(s),console.log("[SOBEYS-INJECT-PROBE] hook",t,i?"FORGED":"pass-through",s.url),s}function d(t){if(t){try{t.origFetch&&(window.fetch=t.origFetch)}catch(t){}try{t.origXhrOpen&&(XMLHttpRequest.prototype.open=t.origXhrOpen),t.origXhrSend&&(XMLHttpRequest.prototype.send=t.origXhrSend)}catch(t){}try{t.styleEl&&t.styleEl.parentNode&&t.styleEl.parentNode.removeChild(t.styleEl)}catch(t){}try{t.cleanupTimer&&clearTimeout(t.cleanupTimer)}catch(t){}window.__speacart_inject_state&&t.sentinel&&delete window.__speacart_inject_state[t.sentinel]}}window.__ttpSobeysAlgoliaTransportProbeInstall=function(){return/(^|\.)sobeys\.com$/.test(location.hostname)||console.warn("[SOBEYS-INJECT-PROBE] not on sobeys.com"),function(){if(i)return;i=!0;const t=window.fetch;window.__ttpSobeysInjectProbeNativeFetch=t,window.fetch=function(e,n){const r="string"==typeof e?e:e&&e.url||"";if(a(r)){let t="";n&&null!=n.body&&(t="string"==typeof n.body?n.body:String(n.body)),o.push({at:(new Date).toISOString(),kind:"fetch",url:r,query:function(){try{const e=JSON.parse(t).requests;return e&&e[0]&&e[0].query}catch(t){return null}}()}),console.log("[SOBEYS-INJECT-PROBE] transport fetch",r.slice(0,80))}return t.apply(this,arguments)};const e=XMLHttpRequest.prototype.open,n=XMLHttpRequest.prototype.send;window.__ttpSobeysInjectProbeNativeXhrOpen=e,window.__ttpSobeysInjectProbeNativeXhrSend=n,XMLHttpRequest.prototype.open=function(t,o){return this.__ttpProbeMethod=t,this.__ttpProbeUrl="string"==typeof o?o:String(o||""),e.apply(this,arguments)},XMLHttpRequest.prototype.send=function(t){const e=this.__ttpProbeUrl||"";if(a(e)){const n="string"==typeof t?t:t?String(t):"";o.push({at:(new Date).toISOString(),kind:"xhr",url:e,query:function(){try{const t=JSON.parse(n).requests;return t&&t[0]&&t[0].query}catch(t){return null}}()}),console.log("[SOBEYS-INJECT-PROBE] transport xhr",e.slice(0,80))}return n.apply(this,arguments)}}(),console.log("[SOBEYS-INJECT-PROBE] transport logger on — search or soft-nav, then __ttpSobeysAlgoliaTransportProbeReport()"),o},window.__ttpSobeysAlgoliaTransportProbeReport=function(){const t=o.reduce(function(t,e){return t[e.kind]=(t[e.kind]||0)+1,t},{}),e={hitCount:o.length,byKind:t,hits:o.slice(),verdict:t.xhr>0&&!t.fetch?"Sobeys uses XHR for Algolia — fetch-only injectInit will NOT render tiles":t.fetch>0&&!t.xhr?"Sobeys uses fetch for Algolia — fetch hook should work":t.fetch>0&&t.xhr>0?"Both fetch and XHR seen — use both hooks":"No Algolia traffic yet — run a search or inject probe first"};return console.table(o),console.log("[SOBEYS-INJECT-PROBE] transport report",e),e},window.__ttpSobeysInjectProbeRun=async function(t){const o=(t=t||{}).hookMode||"both",i=t.queries||["tomatoes","cucumbers"],f=t.templateQuery||"coffee";if(!/(^|\.)sobeys\.com$/.test(location.hostname))throw new Error("Run on www.sobeys.com top-level tab");const g=function(){let t=null;try{const e=JSON.parse(localStorage.getItem("store")||"{}");e&&(e.storeNumber||e.objectID)&&(t=s(e.storeNumber||e.objectID))}catch(t){}if(!t){const e=document.cookie.match(/(?:^|;\s*)storeId=([^;]+)/);e&&(t=s(decodeURIComponent(e[1]).split("_")[0]))}return t}();if(!g)throw new Error("No store — pick a store on sobeys.com first");r&&d(r),n.length=0,console.log("[SOBEYS-INJECT-PROBE] fetching donor template + probe products… storeId="+g);let y=window.__speacart_sobeys_template;const w=Date.now();if(!y||!y.sampleHit||y.storeId!==g){const t=await c(f,g,5);if(!t.hits.length)throw new Error("no donor hit for "+f);y={sampleHit:t.hits[0],resultMeta:t.meta,storeId:g,indexName:e,capturedAt:w},window.__speacart_sobeys_template=y}const b=[];for(let t=0;t<i.length;t++){const e=await c(i[t],g,3);if(!e.hits.length)throw new Error("no hits for "+i[t]);b.push(e.hits[0]),console.log("[SOBEYS-INJECT-PROBE] product",i[t],"→",e.hits[0].objectID,e.hits[0].name)}const S="spcrt-sl-probe-"+Math.random().toString(36).slice(2,10);window.__speacart_inject_state||(window.__speacart_inject_state={});const _={sentinel:S,prefix:S,revision:0,algoliaHits:[],storeId:g,indexName:e,resultMeta:y.resultMeta||{},hookMode:o};window.__speacart_inject_state[S]=_,r=_,function(t,e){const o=t.prefix;function n(){const e=t.revision>0?o+"-r"+t.revision:o;return JSON.stringify({results:[{hits:t.algoliaHits,nbHits:t.algoliaHits.length,page:0,nbPages:t.algoliaHits.length>0?1:0,hitsPerPage:50,exhaustiveNbHits:!0,exhaustiveTypo:!0,query:e,params:"",processingTimeMS:t.resultMeta&&t.resultMeta.processingTimeMS||1,index:t.indexName}]})}function r(t,e){return!!a(t)&&(function(t,e){if(!t||"string"!=typeof t||!e)return!1;try{const o=JSON.parse(t).requests;if(!Array.isArray(o))return!1;for(let t=0;t<o.length;t++)if(-1!==String(o[t]&&o[t].query||"").indexOf(e))return!0}catch(t){}return!1}(e,o)||-1!==t.indexOf(encodeURIComponent(o)))}if("fetch"===e||"both"===e){const e=window.fetch;t.origFetch=e,window.fetch=function(t,o){const i="string"==typeof t?t:t&&t.url||"";let s="";return o&&null!=o.body&&(s="string"==typeof o.body?o.body:String(o.body)),r(i,s)?(h("fetch",i,s,!0),Promise.resolve((c=n(),new Response(c,{status:200,statusText:"OK",headers:{"content-type":"application/json; charset=utf-8"}})))):(a(i)&&h("fetch",i,s,!1),e.apply(this,arguments));var c}}if("xhr"===e||"both"===e){const e=XMLHttpRequest.prototype.open,o=XMLHttpRequest.prototype.send;t.origXhrOpen=e,t.origXhrSend=o,XMLHttpRequest.prototype.open=function(t,o){return this.__ttpInjectUrl="string"==typeof o?o:String(o||""),e.apply(this,arguments)},XMLHttpRequest.prototype.send=function(t){const e=this.__ttpInjectUrl||"",i="string"==typeof t?t:t?String(t):"";if(r(e,i)){h("xhr",e,i,!0);const t=this,o=n();return void setTimeout(function(){!function(t,e){const o=function(e){try{Object.defineProperty(t,"readyState",{configurable:!0,writable:!0,value:e})}catch(t){}t.dispatchEvent(new Event("readystatechange"))};try{Object.defineProperty(t,"status",{configurable:!0,writable:!0,value:200}),Object.defineProperty(t,"statusText",{configurable:!0,writable:!0,value:"OK"}),Object.defineProperty(t,"responseText",{configurable:!0,writable:!0,value:e}),Object.defineProperty(t,"response",{configurable:!0,writable:!0,value:e})}catch(t){}o(2),o(3),o(4),t.dispatchEvent(new Event("load")),t.dispatchEvent(new Event("loadend"))}(t,o)},0)}return a(e)&&h("xhr",e,i,!1),o.apply(this,arguments)}}t.buildForgedAlgoliaJson=n}(_,o);const m=document.createElement("style");m.id="speacart-probe-hide-promo-"+S,m.textContent='[data-testid="in-grid-module"] { display: none !important; }',document.head.appendChild(m),_.styleEl=m,_.cleanupTimer=setTimeout(function(){console.log("[SOBEYS-INJECT-PROBE] auto cleanup after 60s"),window.__ttpSobeysInjectProbeUninstall()},6e4);const I=l();u(S),await p(1500);const O=l();n.filter(function(t){return t.forged}).length;for(let t=0;t<b.length;t++)_.algoliaHits.push(JSON.parse(JSON.stringify(b[t]))),_.revision+=1,u(S+"-r"+_.revision),await p(1200);const E=l(),P=n.filter(function(t){return t.forged}).length,N={ok:E>0,hookMode:o,storeId:g,sentinel:S,tilesBefore:I,tilesAfterInit:O,tilesAfterAppend:E,algoliaHitsInState:_.algoliaHits.length,hookIntercepts:n.slice(),forgedInterceptCount:P,productObjectIds:_.algoliaHits.map(function(t){return t.objectID}),url:location.href,verdict:E>0?"SUCCESS — inject channel rendered "+E+" tile(s) with hookMode="+o:0===P?"FAIL — no forged intercepts; Sobeys never queried Algolia with sentinel (try hookMode both + longer wait)":"FAIL — hook forged "+P+" response(s) but 0 tiles; hit shape or React path issue"};return console.log("[SOBEYS-INJECT-PROBE] run report",N),console.table(n),N},window.__ttpSobeysInjectProbeReport=function(){const t=r,e=t&&t.sentinel,i=e&&window.__speacart_inject_state&&window.__speacart_inject_state[e];return{activeSentinel:e||null,hookMode:t&&t.hookMode,algoliaHits:i?i.algoliaHits.length:0,revision:i?i.revision:null,tiles:l(),hookIntercepts:n.slice(),transportHits:o.slice(),url:location.href}},window.__ttpSobeysInjectProbeUninstall=function(){r&&d(r),r=null,window.__ttpSobeysInjectProbeNativeFetch&&(window.fetch=window.__ttpSobeysInjectProbeNativeFetch),window.__ttpSobeysInjectProbeNativeXhrOpen&&(XMLHttpRequest.prototype.open=window.__ttpSobeysInjectProbeNativeXhrOpen,XMLHttpRequest.prototype.send=window.__ttpSobeysInjectProbeNativeXhrSend),i=!1,console.log("[SOBEYS-INJECT-PROBE] uninstalled")},window.__ttpSobeysInjectProbeInstalled=!0,console.log("[SOBEYS-INJECT-PROBE] installed on",location.hostname,"— await __ttpSobeysInjectProbeRun() (store required)")}();
|
|
@@ -119,16 +119,14 @@
|
|
|
119
119
|
</head>
|
|
120
120
|
<body>
|
|
121
121
|
<div class="container">
|
|
122
|
-
<h1>TTP Hotels
|
|
122
|
+
<h1>TTP Flavor Test (Ecommerce / Hotels / …)</h1>
|
|
123
123
|
|
|
124
124
|
<div class="info">
|
|
125
125
|
<strong>How it works:</strong>
|
|
126
126
|
<ul>
|
|
127
|
-
<li>
|
|
128
|
-
<li>
|
|
129
|
-
<li>
|
|
130
|
-
<li>Booking summary updates on <code>booking_updated</code> messages</li>
|
|
131
|
-
<li>Gallery (<code>show_media</code>) opens fullscreen with widget minimized</li>
|
|
127
|
+
<li>Pick an <strong>Agent</strong> below — the demo passes <code>flavor: { type, partnerId, callView: 'minimized' }</code> for every vertical.</li>
|
|
128
|
+
<li>On <strong>desktop</strong> (>768px), <strong><code>callView: 'minimized'</code></strong> uses the bottom voice strip when a call is active (in-panel active call UI is hidden). Mobile keeps the existing minimized bar behavior.</li>
|
|
129
|
+
<li>Each vertical wires WebSocket handlers (e.g. hotels: <code>show_items</code> rooms, <code>booking_updated</code>, <code>show_media</code>).</li>
|
|
132
130
|
</ul>
|
|
133
131
|
</div>
|
|
134
132
|
|
|
@@ -138,8 +136,8 @@
|
|
|
138
136
|
<div class="settings-grid">
|
|
139
137
|
<label>Agent
|
|
140
138
|
<select id="agentSelect" onchange="handleAgentChange()">
|
|
141
|
-
<option value="agent_ed18369b3|app_wNGQKiMUfUT5JdVIaxzXOJfdcgBegxGY5hZo|ecommerce|mock-store">Ecommerce Demo (agent_ed18369b3)</option>
|
|
142
|
-
<option value="agent_d0774f1af|app_wNGQKiMUfUT5JdVIaxzXOJfdcgBegxGY5hZo|hotels|mock-hotel"
|
|
139
|
+
<option value="agent_ed18369b3|app_wNGQKiMUfUT5JdVIaxzXOJfdcgBegxGY5hZo|ecommerce|mock-store" selected>Ecommerce Demo (agent_ed18369b3)</option>
|
|
140
|
+
<option value="agent_d0774f1af|app_wNGQKiMUfUT5JdVIaxzXOJfdcgBegxGY5hZo|hotels|mock-hotel">Hotels Demo (agent_d0774f1af)</option>
|
|
143
141
|
<option value="agent_PHARMA_ID|app_wNGQKiMUfUT5JdVIaxzXOJfdcgBegxGY5hZo|pharma|mock-pharm">Pharma Demo (update agent ID)</option>
|
|
144
142
|
<option value="agent_RESTAURANT_ID|app_wNGQKiMUfUT5JdVIaxzXOJfdcgBegxGY5hZo|restaurants|mock-restaurant">Restaurant Demo (update agent ID)</option>
|
|
145
143
|
<option value="agent_TOUR_ID|app_wNGQKiMUfUT5JdVIaxzXOJfdcgBegxGY5hZo|tours|mock-tour">Tour Demo (update agent ID)</option>
|
|
@@ -166,11 +164,11 @@
|
|
|
166
164
|
</label>
|
|
167
165
|
<label>STT Provider
|
|
168
166
|
<select id="sttSelect">
|
|
167
|
+
<option value="soniox" selected>Soniox (default — no override)</option>
|
|
169
168
|
<option value="">Default (server)</option>
|
|
170
|
-
<option value="azure-stt"
|
|
169
|
+
<option value="azure-stt">Azure STT</option>
|
|
171
170
|
<option value="cartesia-stt">Cartesia (Ink-Whisper)</option>
|
|
172
171
|
<option value="openai-realtime">OpenAI Realtime</option>
|
|
173
|
-
<option value="soniox">Soniox</option>
|
|
174
172
|
</select>
|
|
175
173
|
</label>
|
|
176
174
|
<label>Start Open
|
|
@@ -233,24 +231,7 @@ window.testWidget._flavor.messageHandlers['show_media']({
|
|
|
233
231
|
});</div>
|
|
234
232
|
</div>
|
|
235
233
|
|
|
236
|
-
<script>
|
|
237
|
-
(function() {
|
|
238
|
-
var isNgrok = window.location.hostname.includes('ngrok');
|
|
239
|
-
if (isNgrok) {
|
|
240
|
-
var s = document.createElement('script');
|
|
241
|
-
s.type = 'text/javascript';
|
|
242
|
-
document.head.appendChild(s);
|
|
243
|
-
fetch('/agent-widget.js', { headers: { 'ngrok-skip-browser-warning': '1' } })
|
|
244
|
-
.then(function(r) { return r.text(); })
|
|
245
|
-
.then(function(code) { s.textContent = code; })
|
|
246
|
-
.catch(function(e) { console.error('SDK fetch failed:', e); });
|
|
247
|
-
} else {
|
|
248
|
-
var s = document.createElement('script');
|
|
249
|
-
s.src = '/agent-widget.js';
|
|
250
|
-
document.head.appendChild(s);
|
|
251
|
-
}
|
|
252
|
-
})();
|
|
253
|
-
</script>
|
|
234
|
+
<script src="demo-sdk-loader.js"></script>
|
|
254
235
|
|
|
255
236
|
<script>
|
|
256
237
|
let chatWidget = null;
|
|
@@ -296,7 +277,7 @@ window.testWidget._flavor.messageHandlers['show_media']({
|
|
|
296
277
|
|
|
297
278
|
const agent = getAgentConfig();
|
|
298
279
|
|
|
299
|
-
|
|
280
|
+
const widgetConfig = window.TTPDemoSdkLoader.applyWebSocketUrl({
|
|
300
281
|
agentId: agent.agentId,
|
|
301
282
|
appId: agent.appId,
|
|
302
283
|
language: document.getElementById('languageSelect').value,
|
|
@@ -304,7 +285,8 @@ window.testWidget._flavor.messageHandlers['show_media']({
|
|
|
304
285
|
agentSettingsOverride: (() => {
|
|
305
286
|
const override = {};
|
|
306
287
|
const stt = document.getElementById('sttSelect').value;
|
|
307
|
-
|
|
288
|
+
// Soniox is server default; only override when picking a non-default provider
|
|
289
|
+
if (stt && stt !== 'soniox') override.sttProvider = stt;
|
|
308
290
|
return override;
|
|
309
291
|
})(),
|
|
310
292
|
behavior: {
|
|
@@ -312,7 +294,8 @@ window.testWidget._flavor.messageHandlers['show_media']({
|
|
|
312
294
|
},
|
|
313
295
|
flavor: {
|
|
314
296
|
type: agent.flavorType,
|
|
315
|
-
partnerId: agent.partnerId
|
|
297
|
+
partnerId: agent.partnerId,
|
|
298
|
+
callView: 'minimized',
|
|
316
299
|
},
|
|
317
300
|
icon: {
|
|
318
301
|
type: 'custom',
|
|
@@ -325,6 +308,7 @@ window.testWidget._flavor.messageHandlers['show_media']({
|
|
|
325
308
|
text: 'היי, אשמח אם תוכלי לעזור לי '
|
|
326
309
|
}
|
|
327
310
|
});
|
|
311
|
+
chatWidget = new TTPAgentSDK.TTPChatWidget(widgetConfig);
|
|
328
312
|
|
|
329
313
|
window.testWidget = chatWidget;
|
|
330
314
|
const sttLabel = document.getElementById('sttSelect').selectedOptions[0].text;
|
|
@@ -415,24 +399,23 @@ window.testWidget._flavor.messageHandlers['show_media']({
|
|
|
415
399
|
};
|
|
416
400
|
|
|
417
401
|
// Wait for SDK and auto-create
|
|
418
|
-
function
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
const iv = setInterval(() => {
|
|
423
|
-
attempts++;
|
|
424
|
-
if (window.TTPAgentSDK?.TTPChatWidget) { clearInterval(iv); resolve(); }
|
|
425
|
-
else if (attempts >= 150) { clearInterval(iv); reject(new Error('SDK failed to load')); }
|
|
426
|
-
}, 100);
|
|
427
|
-
});
|
|
402
|
+
function boot() {
|
|
403
|
+
const mode = window.TTPDemoSdkLoader?.getMode?.() || 'prod';
|
|
404
|
+
updateStatus('SDK loaded (' + mode + ') — click Create Widget or wait...', '');
|
|
405
|
+
createWidget();
|
|
428
406
|
}
|
|
429
407
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
408
|
+
window.addEventListener('ttp-demo-sdk-ready', () => {
|
|
409
|
+
if (chatWidget) {
|
|
410
|
+
chatWidget.destroy();
|
|
411
|
+
chatWidget = null;
|
|
412
|
+
}
|
|
413
|
+
boot();
|
|
414
|
+
});
|
|
415
|
+
window.TTPDemoSdkLoader.waitForReady().then(boot).catch((e) => {
|
|
434
416
|
updateStatus('SDK load failed: ' + e.message, 'error');
|
|
435
417
|
});
|
|
436
418
|
</script>
|
|
419
|
+
<script src="discovery.js"></script>
|
|
437
420
|
</body>
|
|
438
421
|
</html>
|
|
@@ -192,19 +192,6 @@
|
|
|
192
192
|
</div>
|
|
193
193
|
</a>
|
|
194
194
|
|
|
195
|
-
<a href="../examples/test-signed-link.html" class="test-card">
|
|
196
|
-
<span class="test-card__icon">🔐</span>
|
|
197
|
-
<h3 class="test-card__title">Signed Link Test</h3>
|
|
198
|
-
<p class="test-card__description">
|
|
199
|
-
Test secure authentication using signed links. Demonstrates how to use signed URLs
|
|
200
|
-
for production-ready voice agent integration without exposing agent IDs.
|
|
201
|
-
</p>
|
|
202
|
-
<div class="test-card__badges">
|
|
203
|
-
<span class="badge badge--voice">Voice</span>
|
|
204
|
-
<span class="badge badge--sdk">Security</span>
|
|
205
|
-
</div>
|
|
206
|
-
</a>
|
|
207
|
-
|
|
208
195
|
<a href="../examples/test.html" class="test-card">
|
|
209
196
|
<span class="test-card__icon">⚡</span>
|
|
210
197
|
<h3 class="test-card__title">Basic Voice Test</h3>
|
|
@@ -149,8 +149,9 @@
|
|
|
149
149
|
</label>
|
|
150
150
|
<label>STT Provider
|
|
151
151
|
<select id="sttSelect">
|
|
152
|
+
<option value="soniox" selected>Soniox (default — no override)</option>
|
|
152
153
|
<option value="">Default (server)</option>
|
|
153
|
-
<option value="azure-stt"
|
|
154
|
+
<option value="azure-stt">Azure STT</option>
|
|
154
155
|
</select>
|
|
155
156
|
</label>
|
|
156
157
|
</div>
|
|
@@ -251,7 +252,8 @@ window.testWidget._flavor.messageHandlers['show_items']({
|
|
|
251
252
|
agentSettingsOverride: (() => {
|
|
252
253
|
const override = {};
|
|
253
254
|
const stt = document.getElementById('sttSelect').value;
|
|
254
|
-
|
|
255
|
+
// Soniox is server default; only override when picking a non-default provider
|
|
256
|
+
if (stt && stt !== 'soniox') override.sttProvider = stt;
|
|
255
257
|
return override;
|
|
256
258
|
})(),
|
|
257
259
|
behavior: {
|