tests/app-onboarding-cli-progress-polling.unit.test.ts
Rama main · réplica de lectura del repositorio original.
// @vitest-environment happy-dom
import { FunctionsHttpError } from '../src/services/consoleClient'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { createApp, defineComponent, h, nextTick, ref } from 'vue'
import { APP_ONBOARDING_OTA_V1_STEP_IDS } from '../src/services/appOnboarding'
const invokeCapgoApiMock = vi.hoisted(() => vi.fn())
vi.mock('~/services/capgoApi', () => ({
invokeCapgoApi: invokeCapgoApiMock,
}))
const PENDING_ONBOARDING = {
version: 3,
flow: 'ota',
outcome: 'pending',
steps: {},
}
async function mountProgress(initialOnboarding: unknown = PENDING_ONBOARDING, active = ref(true)) {
const { useAppOnboardingCliProgress } = await import('../src/composables/useAppOnboardingCliProgress')
const host = document.createElement('div')
const app = createApp(defineComponent({
setup() {
useAppOnboardingCliProgress('com.example.app', initialOnboarding, active)
return () => h('div')
},
}))
app.mount(host)
return app
}
async function flushRequests() {
for (let i = 0; i < 5; i++)
await Promise.resolve()
}
describe('useAppOnboardingCliProgress polling', () => {
beforeEach(() => {
vi.useFakeTimers()
invokeCapgoApiMock.mockReset()
})
afterEach(() => {
vi.useRealTimers()
})
it.each([403, 404])('stops polling after a %i response', async (status) => {
invokeCapgoApiMock.mockResolvedValue({ data: null, error: new FunctionsHttpError(new Response('{}', { status })) })
const app = await mountProgress()
await flushRequests()
expect(invokeCapgoApiMock).toHaveBeenCalledTimes(1)
await vi.advanceTimersByTimeAsync(20_000)
expect(invokeCapgoApiMock).toHaveBeenCalledTimes(1)
app.unmount()
})
it('stops polling once an OTA install proves the setup, even without a current channel', async () => {
const onboarding = { setup: { todo_list_version: 3, steps: {} }, features: { ota: { succeeded_at: '2026-09-01T00:00:00.000Z' } } }
invokeCapgoApiMock.mockResolvedValue({ data: { onboarding, hasChannel: false, checkErrors: [] }, error: null })
const app = await mountProgress(onboarding)
await vi.advanceTimersByTimeAsync(10_000)
expect(invokeCapgoApiMock).toHaveBeenCalledTimes(1)
app.unmount()
})
it('keeps polling after a transient server error', async () => {
invokeCapgoApiMock.mockResolvedValue({ data: null, error: new FunctionsHttpError(new Response('{}', { status: 503 })) })
const app = await mountProgress()
await vi.advanceTimersByTimeAsync(6_000)
expect(invokeCapgoApiMock.mock.calls.length).toBeGreaterThan(1)
app.unmount()
})
it('pauses hidden panels and refreshes when they become active again', async () => {
invokeCapgoApiMock.mockResolvedValue({ data: { onboarding: PENDING_ONBOARDING }, error: null })
const active = ref(false)
const app = await mountProgress(PENDING_ONBOARDING, active)
await vi.advanceTimersByTimeAsync(6_000)
expect(invokeCapgoApiMock).not.toHaveBeenCalled()
active.value = true
await nextTick()
await vi.advanceTimersByTimeAsync(4_000)
expect(invokeCapgoApiMock.mock.calls.length).toBeGreaterThan(1)
active.value = false
await nextTick()
const calls = invokeCapgoApiMock.mock.calls.length
await vi.advanceTimersByTimeAsync(6_000)
expect(invokeCapgoApiMock).toHaveBeenCalledTimes(calls)
app.unmount()
})
it('uses OTA steps for terminal polling in dual setup instead of the shared completion summary', async () => {
const onboarding = { setup: {
todo_list_version: 4,
ota_todo_list_version: '1',
builder_todo_list_version: '1',
paths: ['ota', 'builder'],
outcome: 'completed',
steps: { ota: Object.fromEntries(APP_ONBOARDING_OTA_V1_STEP_IDS.map(id => [id, { status: 'pending' }])) },
} }
invokeCapgoApiMock.mockImplementation(async () => ({ data: { onboarding: structuredClone(onboarding) }, error: null }))
const app = await mountProgress(onboarding)
await vi.advanceTimersByTimeAsync(4_000)
expect(invokeCapgoApiMock.mock.calls.length).toBeGreaterThan(1)
for (const step of Object.values(onboarding.setup.steps.ota))
step.status = 'done'
await vi.advanceTimersByTimeAsync(2_000)
const calls = invokeCapgoApiMock.mock.calls.length
await vi.advanceTimersByTimeAsync(6_000)
expect(invokeCapgoApiMock).toHaveBeenCalledTimes(calls)
app.unmount()
})
})