mirror of
https://github.com/primedigitaltech/azon_seeker.git
synced 2026-10-05 15:22:59 +08:00
116 lines
3.8 KiB
JavaScript
116 lines
3.8 KiB
JavaScript
const debugPort = process.env.CHROMIUM_DEBUG_PORT || '9226';
|
|
const syncId = process.env.AZON_E2E_SYNC_ID;
|
|
const expectFailure = process.env.AZON_EXPECT_SYNC_FAILURE === '1';
|
|
if (!syncId) throw new Error('AZON_E2E_SYNC_ID is required.');
|
|
|
|
let worker;
|
|
for (let attempt = 0; attempt < 60 && !worker; attempt++) {
|
|
const targets = await fetch(`http://127.0.0.1:${debugPort}/json/list`).then((response) =>
|
|
response.json(),
|
|
);
|
|
worker = targets.find(
|
|
(target) =>
|
|
target.type === 'service_worker' && target.url.endsWith('/dist/background/index.mjs'),
|
|
);
|
|
if (!worker) await new Promise((resolve) => setTimeout(resolve, 250));
|
|
}
|
|
if (!worker) throw new Error('Azon Seeker background service worker was not found.');
|
|
|
|
const socket = new WebSocket(worker.webSocketDebuggerUrl);
|
|
const pending = new Map();
|
|
let nextId = 1;
|
|
socket.addEventListener('message', (event) => {
|
|
const message = JSON.parse(event.data);
|
|
if (!message.id || !pending.has(message.id)) return;
|
|
const { resolve, reject } = pending.get(message.id);
|
|
pending.delete(message.id);
|
|
message.error ? reject(new Error(message.error.message)) : resolve(message.result);
|
|
});
|
|
await new Promise((resolve, reject) => {
|
|
socket.addEventListener('open', resolve, { once: true });
|
|
socket.addEventListener('error', reject, { once: true });
|
|
});
|
|
function request(method, params = {}) {
|
|
return new Promise((resolve, reject) => {
|
|
const id = nextId++;
|
|
pending.set(id, { resolve, reject });
|
|
socket.send(JSON.stringify({ id, method, params }));
|
|
});
|
|
}
|
|
async function evaluate(expression) {
|
|
const result = await request('Runtime.evaluate', {
|
|
expression,
|
|
awaitPromise: true,
|
|
returnByValue: true,
|
|
});
|
|
if (result.exceptionDetails) throw new Error(result.exceptionDetails.text);
|
|
return result.result.value;
|
|
}
|
|
|
|
const searchItem = {
|
|
syncId,
|
|
asin: 'B000000098',
|
|
keywords: 'extension queue e2e',
|
|
rank: 1,
|
|
page: 1,
|
|
pageRank: 1,
|
|
link: 'https://www.amazon.com/dp/B000000098',
|
|
title: 'Extension queue end-to-end fixture',
|
|
price: '$33.93',
|
|
strikethroughPrice: '$39.99',
|
|
adType: 'sponsoredDisplay',
|
|
adRank: 1,
|
|
placement: 'sidebar',
|
|
placementRank: 1,
|
|
placementId: 'extension-e2e-placement',
|
|
adProductIndex: 1,
|
|
adFormat: 'standard',
|
|
imageSrc: 'https://example.com/extension-e2e.jpg',
|
|
createTime: '2026/08/31 11:45:00',
|
|
};
|
|
await evaluate(`(async () => {
|
|
await chrome.storage.local.set({
|
|
'amazon-sync-queue-v1': ${JSON.stringify({
|
|
searchItems: [searchItem],
|
|
detailItems: [],
|
|
reviews: [],
|
|
})},
|
|
'amazon-sync-state-v1': JSON.stringify({status: 'pending', pendingCount: 1})
|
|
});
|
|
await chrome.alarms.create('amazon-sync-retry', { when: Date.now() + 200 });
|
|
return true;
|
|
})()`);
|
|
|
|
let state;
|
|
for (let attempt = 0; attempt < 30; attempt++) {
|
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
state = await evaluate(`(async () => {
|
|
const stored = await chrome.storage.local.get(['amazon-sync-queue-v1', 'amazon-sync-state-v1']);
|
|
return {
|
|
queue: stored['amazon-sync-queue-v1'],
|
|
state: JSON.parse(stored['amazon-sync-state-v1'])
|
|
};
|
|
})()`);
|
|
if (state.state.status === 'failed') {
|
|
if (expectFailure) break;
|
|
throw new Error(state.state.lastError);
|
|
}
|
|
if (state.state.status === 'synced' && state.state.pendingCount === 0) break;
|
|
}
|
|
socket.close();
|
|
if (expectFailure) {
|
|
if (
|
|
state.state.status !== 'failed' ||
|
|
state.state.pendingCount !== 1 ||
|
|
state.queue.searchItems.length !== 1
|
|
) {
|
|
throw new Error(`Failed synchronization was not retained: ${JSON.stringify(state)}`);
|
|
}
|
|
console.log(JSON.stringify({ syncId, retained: true, ...state.state }, null, 2));
|
|
process.exit(0);
|
|
}
|
|
if (state.state.status !== 'synced' || state.queue.searchItems.length !== 0) {
|
|
throw new Error(`Queue did not drain: ${JSON.stringify(state)}`);
|
|
}
|
|
console.log(JSON.stringify({ syncId, ...state.state }, null, 2));
|