const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const { readStepDefinitionsBundle } = require('./helpers/script-bundles.js'); test('step definitions module exposes ordered normal and Plus step metadata', () => { const globalScope = {}; const api = new Function('self', `${readStepDefinitionsBundle()}; return self.MultiPageStepDefinitions;`)(globalScope); const steps = api.getSteps(); const phoneSteps = api.getSteps({ signupMethod: 'phone' }); const phoneReloginSteps = api.getSteps({ signupMethod: 'phone', phoneSignupReloginAfterBindEmailEnabled: true, }); const plusSteps = api.getSteps({ plusModeEnabled: true }); const hostedSteps = api.getSteps({ plusModeEnabled: true, plusPaymentMethod: 'paypal-hosted' }); const plusPhoneSteps = api.getSteps({ plusModeEnabled: true, signupMethod: 'phone' }); const plusPhoneReloginSteps = api.getSteps({ plusModeEnabled: true, signupMethod: 'phone', phoneSignupReloginAfterBindEmailEnabled: true, }); const goPaySteps = api.getSteps({ plusModeEnabled: true, plusPaymentMethod: 'gopay' }); const gpcSteps = api.getSteps({ plusModeEnabled: true, plusPaymentMethod: 'gpc-helper' }); const kiroSteps = api.getSteps({ activeFlowId: 'kiro' }); const grokSteps = api.getSteps({ activeFlowId: 'grok' }); assert.equal(Array.isArray(steps), true); assert.equal(steps.length, 11); assert.equal(steps.every((step) => step.flowId === 'openai'), true); assert.deepStrictEqual( steps.map((step) => step.order), steps.map((step) => step.order).slice().sort((left, right) => left - right) ); assert.deepStrictEqual( steps.map((step) => step.key), [ 'open-chatgpt', 'submit-signup-email', 'fill-password', 'fetch-signup-code', 'fill-profile', 'wait-registration-success', 'oauth-login', 'fetch-login-code', 'post-login-phone-verification', 'confirm-oauth', 'platform-verify', ] ); assert.equal(steps[0].title, '打开 ChatGPT 官网'); assert.equal(steps[5].title, '等待注册成功'); assert.equal(phoneSteps[1].title, '注册并输入手机号'); assert.equal(phoneSteps[3].title, '获取手机验证码'); assert.deepStrictEqual( phoneSteps.map((step) => step.key), [ 'open-chatgpt', 'submit-signup-email', 'fill-password', 'fetch-signup-code', 'fill-profile', 'wait-registration-success', 'oauth-login', 'fetch-login-code', 'bind-email', 'fetch-bind-email-code', 'confirm-oauth', 'platform-verify', ] ); assert.deepStrictEqual( phoneReloginSteps.map((step) => step.key), [ 'open-chatgpt', 'submit-signup-email', 'fill-password', 'fetch-signup-code', 'fill-profile', 'wait-registration-success', 'oauth-login', 'fetch-login-code', 'bind-email', 'fetch-bind-email-code', 'relogin-bound-email', 'fetch-bound-email-login-code', 'post-bound-email-phone-verification', 'confirm-oauth', 'platform-verify', ] ); assert.equal(phoneReloginSteps.find((step) => step.key === 'relogin-bound-email')?.title, '绑定邮箱后刷新 OAuth 并登录(邮箱)'); assert.equal(phoneReloginSteps.find((step) => step.key === 'fetch-bind-email-code')?.title, '获取绑定邮箱验证码'); assert.deepStrictEqual( plusSteps.map((step) => step.key), [ 'open-chatgpt', 'submit-signup-email', 'fill-password', 'fetch-signup-code', 'fill-profile', 'wait-registration-success', 'plus-checkout-create', 'plus-checkout-billing', 'paypal-approve', 'plus-checkout-return', 'oauth-login', 'fetch-login-code', 'post-login-phone-verification', 'confirm-oauth', 'platform-verify', ] ); assert.equal(plusSteps[5].title, '等待注册成功'); assert.equal(plusSteps.some((step) => step.key === 'fetch-login-code'), true); assert.equal(plusSteps.find((step) => step.key === 'paypal-approve')?.title, 'PayPal 登录与授权'); assert.equal(plusPhoneSteps[1].title, '注册并输入手机号'); assert.equal(plusPhoneSteps[3].title, '获取手机验证码'); assert.deepStrictEqual( plusPhoneSteps.map((step) => step.key), [ 'open-chatgpt', 'submit-signup-email', 'fill-password', 'fetch-signup-code', 'fill-profile', 'wait-registration-success', 'plus-checkout-create', 'plus-checkout-billing', 'paypal-approve', 'plus-checkout-return', 'oauth-login', 'fetch-login-code', 'bind-email', 'fetch-bind-email-code', 'confirm-oauth', 'platform-verify', ] ); assert.deepStrictEqual( plusPhoneReloginSteps.map((step) => step.key).slice(-9), [ 'oauth-login', 'fetch-login-code', 'bind-email', 'fetch-bind-email-code', 'relogin-bound-email', 'fetch-bound-email-login-code', 'post-bound-email-phone-verification', 'confirm-oauth', 'platform-verify', ] ); assert.equal(goPaySteps.some((step) => step.key === 'paypal-approve'), false); assert.equal(api.getStepById(9, { plusModeEnabled: true, plusPaymentMethod: 'gopay' })?.key, 'oauth-login'); assert.equal(api.getPlusPaymentStepTitle({ plusModeEnabled: true, plusPaymentMethod: 'gopay' }), ''); assert.deepStrictEqual(api.getStepIds({ plusModeEnabled: true }), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); assert.equal(api.getLastStepId({ plusModeEnabled: true }), 15); assert.deepStrictEqual(api.getStepIds({ plusModeEnabled: true, signupMethod: 'phone' }), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); assert.equal(api.getLastStepId({ plusModeEnabled: true, signupMethod: 'phone' }), 16); assert.deepStrictEqual(api.getStepIds({ plusModeEnabled: true, signupMethod: 'phone', phoneSignupReloginAfterBindEmailEnabled: true }), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]); assert.equal(api.getLastStepId({ plusModeEnabled: true, signupMethod: 'phone', phoneSignupReloginAfterBindEmailEnabled: true }), 19); assert.equal(api.hasFlow('openai'), true); assert.equal(api.hasFlow('kiro'), true); assert.equal(api.hasFlow('grok'), true); assert.equal(api.hasFlow('site-a'), false); assert.deepStrictEqual(api.getRegisteredFlowIds(), ['openai', 'kiro', 'grok']); assert.deepStrictEqual(api.getSteps({ activeFlowId: 'site-a' }), []); assert.equal(api.getStepById(2, { activeFlowId: 'site-a' }), null); assert.deepStrictEqual( kiroSteps.map((step) => step.key), [ 'kiro-open-register-page', 'kiro-submit-email', 'kiro-submit-name', 'kiro-submit-verification-code', 'kiro-submit-password', 'kiro-complete-register-consent', 'kiro-start-desktop-authorize', 'kiro-complete-desktop-authorize', 'kiro-upload-credential', ] ); assert.equal(kiroSteps.every((step) => step.flowId === 'kiro'), true); assert.equal(kiroSteps[0].driverId, 'flows/kiro/background/register-runner'); assert.equal(kiroSteps[8].sourceId, 'kiro-rs-admin'); assert.equal(kiroSteps[0].title, '打开注册页'); assert.equal(kiroSteps[1].title, '获取邮箱并继续'); assert.equal(kiroSteps[2].title, '填写姓名并继续'); assert.equal(kiroSteps[3].title, '获取验证码并继续'); assert.equal(kiroSteps[4].title, '设置密码并继续'); assert.equal(kiroSteps[5].title, '完成注册授权'); assert.equal(kiroSteps[6].title, '启动桌面授权'); assert.equal(kiroSteps[7].title, '完成桌面授权'); assert.equal(kiroSteps[8].title, '上传凭据到 kiro.rs'); const kiroContributionSteps = api.getSteps({ activeFlowId: 'kiro', accountContributionEnabled: true }); assert.equal(kiroContributionSteps[8].title, '贡献上传'); assert.deepStrictEqual(api.getStepIds({ activeFlowId: 'kiro' }), [1, 2, 3, 4, 5, 6, 7, 8, 9]); assert.equal(api.getLastStepId({ activeFlowId: 'kiro' }), 9); assert.deepStrictEqual( api.getNodes({ activeFlowId: 'kiro' }).map((node) => node.next), [ ['kiro-submit-email'], ['kiro-submit-name'], ['kiro-submit-verification-code'], ['kiro-submit-password'], ['kiro-complete-register-consent'], ['kiro-start-desktop-authorize'], ['kiro-complete-desktop-authorize'], ['kiro-upload-credential'], [], ] ); assert.deepStrictEqual( grokSteps.map((step) => step.key), [ 'grok-open-signup-page', 'grok-submit-email', 'grok-submit-verification-code', 'grok-submit-profile', 'grok-extract-sso-cookie', ] ); assert.equal(grokSteps.every((step) => step.flowId === 'grok'), true); assert.equal(grokSteps[0].driverId, 'flows/grok/background/register-runner'); assert.equal(grokSteps[0].sourceId, 'grok-register-page'); assert.equal(grokSteps[2].mailRuleId, 'grok-submit-verification-code'); assert.deepStrictEqual( grokSteps.map((step) => step.title), ['打开 Grok 注册页', '获取邮箱并继续', '获取验证码并继续', '填写资料并继续', '提取 SSO Cookie'] ); assert.deepStrictEqual(api.getStepIds({ activeFlowId: 'grok' }), [1, 2, 3, 4, 5]); assert.equal(api.getLastStepId({ activeFlowId: 'grok' }), 5); assert.deepStrictEqual( api.getNodes({ activeFlowId: 'grok' }).map((node) => node.next), [ ['grok-submit-email'], ['grok-submit-verification-code'], ['grok-submit-profile'], ['grok-extract-sso-cookie'], [], ] ); assert.equal(plusSteps[6].title, '创建 Plus Checkout'); assert.equal(plusSteps[8].title, 'PayPal 登录与授权'); assert.deepStrictEqual( hostedSteps.map((step) => step.key), [ 'open-chatgpt', 'submit-signup-email', 'fill-password', 'fetch-signup-code', 'fill-profile', 'wait-registration-success', 'plus-checkout-create', 'paypal-hosted-email', 'paypal-hosted-card', 'paypal-hosted-create-account', 'paypal-hosted-review', 'oauth-login', 'fetch-login-code', 'confirm-oauth', 'platform-verify', ] ); assert.equal(hostedSteps.some((step) => step.key === 'plus-checkout-billing'), false); assert.equal(hostedSteps.some((step) => step.key === 'paypal-approve'), false); assert.equal(hostedSteps.some((step) => step.key === 'plus-checkout-return'), false); assert.equal(hostedSteps.some((step) => step.key === 'paypal-hosted-openai-checkout'), false); assert.equal(hostedSteps.some((step) => step.key === 'paypal-hosted-verification'), false); assert.equal(hostedSteps.find((step) => step.key === 'paypal-hosted-card')?.title, '无卡直绑填写 PayPal 资料'); assert.deepStrictEqual(api.getStepIds({ plusModeEnabled: true, plusPaymentMethod: 'paypal-hosted' }), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); assert.equal(api.getLastStepId({ plusModeEnabled: true, plusPaymentMethod: 'paypal-hosted' }), 15); assert.deepStrictEqual( goPaySteps.map((step) => step.key), [ 'open-chatgpt', 'submit-signup-email', 'fill-password', 'fetch-signup-code', 'fill-profile', 'wait-registration-success', 'plus-checkout-create', 'gopay-subscription-confirm', 'oauth-login', 'fetch-login-code', 'post-login-phone-verification', 'confirm-oauth', 'platform-verify', ] ); assert.deepStrictEqual(api.getStepIds({ plusModeEnabled: true, plusPaymentMethod: 'gopay' }), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]); assert.equal(api.getLastStepId({ plusModeEnabled: true, plusPaymentMethod: 'gopay' }), 13); assert.equal(goPaySteps[6].title, '打开 GoPay 订阅页'); assert.equal(goPaySteps[7].title, '等待 GoPay 订阅确认'); assert.deepStrictEqual( gpcSteps.map((step) => step.key), [ 'open-chatgpt', 'submit-signup-email', 'fill-password', 'fetch-signup-code', 'fill-profile', 'wait-registration-success', 'plus-checkout-create', 'plus-checkout-billing', 'oauth-login', 'fetch-login-code', 'post-login-phone-verification', 'confirm-oauth', 'platform-verify', ] ); assert.deepStrictEqual(api.getStepIds({ plusModeEnabled: true, plusPaymentMethod: 'gpc-helper' }), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]); assert.equal(api.getLastStepId({ plusModeEnabled: true, plusPaymentMethod: 'gpc-helper' }), 13); assert.equal(gpcSteps[6].title, '创建 GPC 订单'); assert.equal(gpcSteps[7].title, '等待 GPC 任务完成'); }); test('Plus no-payment mode removes only payment chain nodes', () => { const globalScope = {}; const api = new Function('self', `${readStepDefinitionsBundle()}; return self.MultiPageStepDefinitions;`)(globalScope); const paymentChainKeys = [ 'plus-checkout-create', 'plus-checkout-billing', 'paypal-approve', 'plus-checkout-return', 'paypal-hosted-email', 'paypal-hosted-card', 'paypal-hosted-create-account', 'paypal-hosted-review', 'gopay-subscription-confirm', ]; const oauthSteps = api.getSteps({ plusModeEnabled: true, plusPaymentMethod: 'none' }); const oauthNodes = api.getNodes({ plusModeEnabled: true, plusPaymentMethod: 'none' }); const oauthStepKeys = oauthSteps.map((step) => step.key); assert.deepStrictEqual(oauthStepKeys, [ 'open-chatgpt', 'submit-signup-email', 'fill-password', 'fetch-signup-code', 'fill-profile', 'wait-registration-success', 'oauth-login', 'fetch-login-code', 'post-login-phone-verification', 'confirm-oauth', 'platform-verify', ]); paymentChainKeys.forEach((key) => { assert.equal(oauthStepKeys.includes(key), false, `no-payment OAuth should not keep ${key}`); assert.equal(oauthNodes.some((node) => node.nodeId === key), false, `no-payment OAuth nodes should not keep ${key}`); }); assert.deepStrictEqual(api.getStepIds({ plusModeEnabled: true, plusPaymentMethod: 'none' }), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); assert.equal(api.getPlusPaymentStepTitle({ plusModeEnabled: true, plusPaymentMethod: 'none' }), ''); assert.deepStrictEqual( oauthNodes.find((node) => node.nodeId === 'fill-profile')?.next, ['wait-registration-success'] ); assert.deepStrictEqual( oauthNodes.find((node) => node.nodeId === 'wait-registration-success')?.next, ['oauth-login'] ); const sub2apiSteps = api.getSteps({ plusModeEnabled: true, plusPaymentMethod: 'none', plusAccountAccessStrategy: 'sub2api_codex_session', }); const sub2apiNodes = api.getNodes({ plusModeEnabled: true, plusPaymentMethod: 'none', plusAccountAccessStrategy: 'sub2api_codex_session', }); assert.deepStrictEqual(sub2apiSteps.map((step) => step.key), [ 'open-chatgpt', 'submit-signup-email', 'fill-password', 'fetch-signup-code', 'fill-profile', 'wait-registration-success', 'sub2api-session-import', ]); paymentChainKeys.forEach((key) => { assert.equal(sub2apiSteps.some((step) => step.key === key), false, `no-payment SUB2API should not keep ${key}`); }); assert.deepStrictEqual(api.getStepIds({ plusModeEnabled: true, plusPaymentMethod: 'none', plusAccountAccessStrategy: 'sub2api_codex_session', }), [1, 2, 3, 4, 5, 6, 7]); assert.equal(sub2apiNodes.at(-1)?.nodeId, 'sub2api-session-import'); assert.deepStrictEqual(sub2apiNodes.find((node) => node.nodeId === 'fill-profile')?.next, ['wait-registration-success']); assert.deepStrictEqual(sub2apiNodes.find((node) => node.nodeId === 'wait-registration-success')?.next, ['sub2api-session-import']); const cpaSteps = api.getSteps({ plusModeEnabled: true, plusPaymentMethod: 'none', plusAccountAccessStrategy: 'cpa_codex_session', }); const cpaNodes = api.getNodes({ plusModeEnabled: true, plusPaymentMethod: 'none', plusAccountAccessStrategy: 'cpa_codex_session', }); assert.deepStrictEqual(cpaSteps.map((step) => step.key), [ 'open-chatgpt', 'submit-signup-email', 'fill-password', 'fetch-signup-code', 'fill-profile', 'wait-registration-success', 'cpa-session-import', ]); paymentChainKeys.forEach((key) => { assert.equal(cpaSteps.some((step) => step.key === key), false, `no-payment CPA should not keep ${key}`); }); assert.deepStrictEqual(api.getStepIds({ plusModeEnabled: true, plusPaymentMethod: 'none', plusAccountAccessStrategy: 'cpa_codex_session', }), [1, 2, 3, 4, 5, 6, 7]); assert.equal(cpaNodes.at(-1)?.nodeId, 'cpa-session-import'); assert.deepStrictEqual(cpaNodes.find((node) => node.nodeId === 'fill-profile')?.next, ['wait-registration-success']); assert.deepStrictEqual(cpaNodes.find((node) => node.nodeId === 'wait-registration-success')?.next, ['cpa-session-import']); }); test('Plus session strategy swaps the OAuth tail for a single SUB2API import node', () => { const globalScope = {}; const api = new Function('self', `${readStepDefinitionsBundle()}; return self.MultiPageStepDefinitions;`)(globalScope); const forbiddenTailKeys = [ 'oauth-login', 'fetch-login-code', 'post-login-phone-verification', 'confirm-oauth', 'platform-verify', ]; [ { label: 'paypal', options: { plusModeEnabled: true, plusPaymentMethod: 'paypal', plusAccountAccessStrategy: 'sub2api_codex_session', }, previousNodeId: 'plus-checkout-return', expectedStepIds: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], }, { label: 'paypal-hosted', options: { plusModeEnabled: true, plusPaymentMethod: 'paypal-hosted', plusAccountAccessStrategy: 'sub2api_codex_session', }, previousNodeId: 'paypal-hosted-review', expectedStepIds: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], }, { label: 'gopay', options: { plusModeEnabled: true, plusPaymentMethod: 'gopay', plusAccountAccessStrategy: 'sub2api_codex_session', }, previousNodeId: 'gopay-subscription-confirm', expectedStepIds: [1, 2, 3, 4, 5, 6, 7, 8, 9], }, { label: 'gpc-helper', options: { plusModeEnabled: true, plusPaymentMethod: 'gpc-helper', plusAccountAccessStrategy: 'sub2api_codex_session', }, previousNodeId: 'plus-checkout-billing', expectedStepIds: [1, 2, 3, 4, 5, 6, 7, 8, 9], }, ].forEach(({ label, options, previousNodeId, expectedStepIds }) => { const steps = api.getSteps(options); const nodes = api.getNodes(options); const stepKeys = steps.map((step) => step.key); const nodeIds = nodes.map((node) => node.nodeId); const previousNode = nodes.find((node) => node.nodeId === previousNodeId); const waitNode = nodes.find((node) => node.nodeId === 'wait-registration-success'); const sessionImportNode = nodes.find((node) => node.nodeId === 'sub2api-session-import'); assert.equal(stepKeys.at(-1), 'sub2api-session-import', `${label} should end with session import`); assert.equal(nodeIds.at(-1), 'sub2api-session-import', `${label} node order should end with session import`); forbiddenTailKeys.forEach((key) => { assert.equal(stepKeys.includes(key), false, `${label} should not keep ${key} in session mode`); assert.equal(nodeIds.includes(key), false, `${label} nodes should not keep ${key} in session mode`); }); assert.deepStrictEqual(api.getStepIds(options), expectedStepIds, `${label} step ids should follow the new tail`); assert.equal(api.getLastStepId(options), expectedStepIds.at(-1), `${label} last step id should match session import`); assert.deepStrictEqual(waitNode?.next, ['plus-checkout-create'], `${label} wait node should link to checkout chain`); assert.deepStrictEqual(previousNode?.next, ['sub2api-session-import'], `${label} previous node should link to session import`); assert.deepStrictEqual(sessionImportNode?.next, [], `${label} session import should be terminal`); }); }); test('Plus phone signup never switches to SUB2API session tail even if the requested strategy is session import', () => { const globalScope = {}; const api = new Function('self', `${readStepDefinitionsBundle()}; return self.MultiPageStepDefinitions;`)(globalScope); const steps = api.getSteps({ plusModeEnabled: true, plusPaymentMethod: 'paypal', signupMethod: 'phone', plusAccountAccessStrategy: 'sub2api_codex_session', }); const stepKeys = steps.map((step) => step.key); assert.equal(stepKeys.includes('sub2api-session-import'), false); assert.equal(stepKeys.includes('oauth-login'), true); assert.equal(stepKeys.includes('platform-verify'), true); }); test('Plus session strategy swaps the OAuth tail for a single CPA import node', () => { const globalScope = {}; const api = new Function('self', `${readStepDefinitionsBundle()}; return self.MultiPageStepDefinitions;`)(globalScope); const forbiddenTailKeys = [ 'oauth-login', 'fetch-login-code', 'post-login-phone-verification', 'confirm-oauth', 'platform-verify', ]; [ { label: 'paypal', options: { plusModeEnabled: true, plusPaymentMethod: 'paypal', plusAccountAccessStrategy: 'cpa_codex_session', }, previousNodeId: 'plus-checkout-return', expectedStepIds: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], }, { label: 'paypal-hosted', options: { plusModeEnabled: true, plusPaymentMethod: 'paypal-hosted', plusAccountAccessStrategy: 'cpa_codex_session', }, previousNodeId: 'paypal-hosted-review', expectedStepIds: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], }, { label: 'gopay', options: { plusModeEnabled: true, plusPaymentMethod: 'gopay', plusAccountAccessStrategy: 'cpa_codex_session', }, previousNodeId: 'gopay-subscription-confirm', expectedStepIds: [1, 2, 3, 4, 5, 6, 7, 8, 9], }, { label: 'gpc-helper', options: { plusModeEnabled: true, plusPaymentMethod: 'gpc-helper', plusAccountAccessStrategy: 'cpa_codex_session', }, previousNodeId: 'plus-checkout-billing', expectedStepIds: [1, 2, 3, 4, 5, 6, 7, 8, 9], }, ].forEach(({ label, options, previousNodeId, expectedStepIds }) => { const steps = api.getSteps(options); const nodes = api.getNodes(options); const stepKeys = steps.map((step) => step.key); const nodeIds = nodes.map((node) => node.nodeId); const previousNode = nodes.find((node) => node.nodeId === previousNodeId); const waitNode = nodes.find((node) => node.nodeId === 'wait-registration-success'); const sessionImportNode = nodes.find((node) => node.nodeId === 'cpa-session-import'); assert.equal(stepKeys.at(-1), 'cpa-session-import', `${label} should end with CPA session import`); assert.equal(nodeIds.at(-1), 'cpa-session-import', `${label} node order should end with CPA session import`); forbiddenTailKeys.forEach((key) => { assert.equal(stepKeys.includes(key), false, `${label} should not keep ${key} in CPA session mode`); assert.equal(nodeIds.includes(key), false, `${label} nodes should not keep ${key} in CPA session mode`); }); assert.deepStrictEqual(api.getStepIds(options), expectedStepIds, `${label} step ids should follow the CPA tail`); assert.equal(api.getLastStepId(options), expectedStepIds.at(-1), `${label} last step id should match CPA session import`); assert.deepStrictEqual(waitNode?.next, ['plus-checkout-create'], `${label} wait node should link to checkout chain`); assert.deepStrictEqual(previousNode?.next, ['cpa-session-import'], `${label} previous node should link to CPA session import`); assert.deepStrictEqual(sessionImportNode?.next, [], `${label} CPA session import should be terminal`); }); }); test('sidepanel html loads shared step definitions before sidepanel bootstrap', () => { const html = fs.readFileSync('sidepanel/sidepanel.html', 'utf8'); const definitionsIndex = html.indexOf(''); const sidepanelIndex = html.indexOf(''); assert.notEqual(definitionsIndex, -1); assert.notEqual(sidepanelIndex, -1); assert.ok(definitionsIndex < sidepanelIndex); }); test('sidepanel html exposes Plus mode, PayPal, and GoPay settings', () => { const html = fs.readFileSync('sidepanel/sidepanel.html', 'utf8'); assert.match(html, /id="input-plus-mode-enabled"/); assert.match(html, /id="select-plus-payment-method"/); assert.match(html, /