136 lines
5.5 KiB
JavaScript
136 lines
5.5 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
|
|
const source = fs.readFileSync('background/plus-hosted-checkout-success.js', 'utf8');
|
|
|
|
function loadApi() {
|
|
const scope = {};
|
|
return new Function('self', `${source}; return self.MultiPagePlusHostedCheckoutSuccess;`)(scope);
|
|
}
|
|
|
|
function createState(overrides = {}) {
|
|
return {
|
|
plusModeEnabled: true,
|
|
plusPaymentMethod: 'paypal',
|
|
plusHostedCheckoutCompletionPending: true,
|
|
plusCheckoutTabId: 77,
|
|
nodeStatuses: {
|
|
'plus-checkout-create': 'running',
|
|
},
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
test('hosted checkout success manager completes plus-checkout-create on the tracked success tab', async () => {
|
|
const api = loadApi();
|
|
const events = [];
|
|
let state = createState();
|
|
const manager = api.createPlusHostedCheckoutSuccessManager({
|
|
addLog: async (message, level, options) => events.push({ type: 'log', message, level, options }),
|
|
completeNodeFromBackground: async (nodeId, payload) => events.push({ type: 'complete', nodeId, payload }),
|
|
getState: async () => state,
|
|
setState: async (payload) => {
|
|
events.push({ type: 'set-state', payload });
|
|
state = { ...state, ...payload };
|
|
},
|
|
});
|
|
|
|
const successUrl = 'https://chatgpt.com/payments/success?session_id=cs_test';
|
|
const result = await manager.handleTabUpdated(77, { status: 'complete' }, { url: successUrl });
|
|
|
|
assert.deepEqual(result, {
|
|
completed: true,
|
|
plusReturnUrl: successUrl,
|
|
});
|
|
assert.deepEqual(events.find((event) => event.type === 'complete'), {
|
|
type: 'complete',
|
|
nodeId: 'plus-checkout-create',
|
|
payload: {
|
|
plusReturnUrl: successUrl,
|
|
plusHostedCheckoutCompleted: true,
|
|
},
|
|
});
|
|
assert.equal(state.plusHostedCheckoutCompletionPending, false);
|
|
assert.equal(state.plusHostedCheckoutCompleted, true);
|
|
assert.equal(state.plusReturnUrl, successUrl);
|
|
assert.equal(events.find((event) => event.type === 'log')?.options?.nodeId, 'plus-checkout-create');
|
|
});
|
|
|
|
test('hosted checkout success manager ignores unrelated updates and inactive states', async () => {
|
|
const api = loadApi();
|
|
const events = [];
|
|
let state = createState();
|
|
const manager = api.createPlusHostedCheckoutSuccessManager({
|
|
completeNodeFromBackground: async () => events.push('complete'),
|
|
getState: async () => state,
|
|
setState: async () => events.push('set-state'),
|
|
});
|
|
|
|
assert.equal(api.isPaymentSuccessUrl('https://chatgpt.com/payments/success'), true);
|
|
assert.equal(api.isPaymentSuccessUrl('https://example.com/payments/success'), false);
|
|
assert.equal(await manager.handleTabUpdated(77, { status: 'loading' }, { url: 'https://chatgpt.com/payments/success' }), null);
|
|
assert.equal(await manager.handleTabUpdated(77, { status: 'complete' }, { url: 'https://chatgpt.com/' }), null);
|
|
assert.equal(await manager.handleTabUpdated(88, { status: 'complete' }, { url: 'https://chatgpt.com/payments/success' }), null);
|
|
|
|
state = createState({ plusHostedCheckoutCompletionPending: false });
|
|
assert.equal(await manager.handleTabUpdated(77, { status: 'complete' }, { url: 'https://chatgpt.com/payments/success' }), null);
|
|
|
|
state = createState({ plusHostedCheckoutCompletionPending: true, nodeStatuses: { 'plus-checkout-create': 'completed' } });
|
|
assert.equal(await manager.handleTabUpdated(77, { status: 'complete' }, { url: 'https://chatgpt.com/payments/success' }), null);
|
|
assert.deepEqual(events, []);
|
|
});
|
|
|
|
test('hosted checkout success manager deduplicates concurrent success events for one tab', async () => {
|
|
const api = loadApi();
|
|
const events = [];
|
|
const state = createState();
|
|
let releaseComplete;
|
|
const completeGate = new Promise((resolve) => {
|
|
releaseComplete = resolve;
|
|
});
|
|
const manager = api.createPlusHostedCheckoutSuccessManager({
|
|
addLog: async () => {},
|
|
completeNodeFromBackground: async (nodeId) => {
|
|
events.push({ type: 'complete', nodeId });
|
|
await completeGate;
|
|
},
|
|
getState: async () => state,
|
|
setState: async (payload) => events.push({ type: 'set-state', payload }),
|
|
});
|
|
|
|
const first = manager.handleTabUpdated(77, { status: 'complete' }, { url: 'https://chatgpt.com/payments/success' });
|
|
const second = manager.handleTabUpdated(77, { status: 'complete' }, { url: 'https://chatgpt.com/payments/success' });
|
|
await Promise.resolve();
|
|
releaseComplete();
|
|
|
|
const results = await Promise.all([first, second]);
|
|
assert.equal(results.filter(Boolean).length, 1);
|
|
assert.equal(events.filter((event) => event.type === 'complete').length, 1);
|
|
});
|
|
|
|
test('hosted checkout success manager fails the checkout node when continuation throws', async () => {
|
|
const api = loadApi();
|
|
const events = [];
|
|
const manager = api.createPlusHostedCheckoutSuccessManager({
|
|
addLog: async (message, level) => events.push({ type: 'log', message, level }),
|
|
completeNodeFromBackground: async () => {
|
|
throw new Error('boom');
|
|
},
|
|
failNodeFromBackground: async (nodeId, message) => events.push({ type: 'fail', nodeId, message }),
|
|
getState: async () => createState(),
|
|
setState: async () => {},
|
|
});
|
|
|
|
const result = await manager.handleTabUpdated(77, { status: 'complete' }, { url: 'https://chatgpt.com/payments/success' });
|
|
|
|
assert.equal(result.failed, true);
|
|
assert.equal(result.message, 'boom');
|
|
assert.deepEqual(events.find((event) => event.type === 'fail'), {
|
|
type: 'fail',
|
|
nodeId: 'plus-checkout-create',
|
|
message: 'boom',
|
|
});
|
|
assert.equal(events.some((event) => event.type === 'log' && event.level === 'error'), true);
|
|
});
|