const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const sidepanelSource = fs.readFileSync('sidepanel/sidepanel.js', 'utf8'); const sidepanelHtml = fs.readFileSync('sidepanel/sidepanel.html', 'utf8'); function extractFunction(source, name) { const asyncStart = source.indexOf(`async function ${name}`); const normalStart = source.indexOf(`function ${name}`); const start = asyncStart !== -1 ? asyncStart : normalStart; if (start === -1) { throw new Error(`Function ${name} not found`); } const signatureEnd = source.indexOf(')', start); const bodyStart = source.indexOf('{', signatureEnd); let depth = 0; let end = bodyStart; for (; end < source.length; end += 1) { const char = source[end]; if (char === '{') { depth += 1; } else if (char === '}') { depth -= 1; if (depth === 0) { end += 1; break; } } } return source.slice(start, end); } test('sidepanel html exposes flow selector and kiro source fields', () => { [ 'id="select-flow"', 'id="label-source-selector"', 'id="row-kiro-rs-url"', 'id="row-kiro-rs-key"', 'id="row-kiro-region"', 'id="row-kiro-device-code"', 'id="row-kiro-login-url"', 'id="row-kiro-upload-status"', ].forEach((snippet) => { assert.match(sidepanelHtml, new RegExp(snippet.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))); }); }); test('sidepanel step definitions rerender when active flow changes even if plus/signup settings stay the same', () => { const bundle = [ extractFunction(sidepanelSource, 'normalizeSignupMethod'), extractFunction(sidepanelSource, 'normalizePlusPaymentMethod'), extractFunction(sidepanelSource, 'getStepDefinitionsForMode'), extractFunction(sidepanelSource, 'rebuildStepDefinitionState'), extractFunction(sidepanelSource, 'syncStepDefinitionsForMode'), ].join('\n'); const api = new Function(` const calls = []; const window = { MultiPageStepDefinitions: { getSteps(options) { calls.push({ type: 'getSteps', options }); return [{ id: options.activeFlowId === 'kiro' ? 88 : 6, order: 1, key: options.activeFlowId }]; }, }, }; let latestState = { activeFlowId: 'openai' }; let currentPlusModeEnabled = false; let currentPlusPaymentMethod = 'paypal'; let currentSignupMethod = 'email'; let currentPhoneSignupReloginAfterBindEmailEnabled = false; let currentStepDefinitionFlowId = 'openai'; const DEFAULT_ACTIVE_FLOW_ID = 'openai'; const DEFAULT_SIGNUP_METHOD = 'email'; const DEFAULT_PLUS_PAYMENT_METHOD = 'paypal'; let stepDefinitions = [{ id: 6, key: 'openai' }]; let STEP_IDS = [6]; let STEP_DEFAULT_STATUSES = { 6: 'pending' }; let SKIPPABLE_STEPS = new Set([6]); function renderStepsList() { calls.push({ type: 'render', stepIds: [...STEP_IDS] }); } ${bundle} return { calls, syncStepDefinitionsForMode, getStepIds: () => [...STEP_IDS], getCurrentFlowId: () => currentStepDefinitionFlowId, }; `)(); api.syncStepDefinitionsForMode(false, { activeFlowId: 'kiro', plusPaymentMethod: 'paypal', signupMethod: 'email', phoneSignupReloginAfterBindEmailEnabled: false, }); assert.equal(api.getCurrentFlowId(), 'kiro'); assert.deepEqual(api.getStepIds(), [88]); assert.deepEqual(api.calls[0], { type: 'getSteps', options: { activeFlowId: 'kiro', plusModeEnabled: false, plusPaymentMethod: 'paypal', signupMethod: 'email', phoneSignupReloginAfterBindEmailEnabled: false, }, }); assert.deepEqual(api.calls[1], { type: 'render', stepIds: [88] }); });