Skip to content

Commit 2441dbd

Browse files
bodia-uzclaude
andcommitted
testKit: configurable waitForLoading timeout + report all root unready APIs on timeout
The timeout message previously came from getRootUnreadyAPI(), which walks a single dependency chain from an arbitrary first unready entry point - sibling branches are dropped and only one API is reported even when several independent APIs are missing. Compute all root unready APIs instead: dependencies of unready entry points that are neither ready nor declared by another unready entry point. The hardcoded 3s timeout becomes an optional parameter (default unchanged), so heavy hosts can raise it above their boot time and let this error surface before the test runner's own opaque timeout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 27738f7 commit 2441dbd

2 files changed

Lines changed: 61 additions & 5 deletions

File tree

packages/repluggable-core/test/testKit.spec.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,5 +149,47 @@ describe('App Host TestKit', () => {
149149
jest.runAllTimers()
150150
await expect(hostPromise).rejects.toThrow(new RegExp(MockPublicAPI.name))
151151
})
152+
153+
it('should report all root unready APIs of independent unready entry points', async () => {
154+
const hostPromise = createAppHostAndWaitForLoading(
155+
[
156+
{
157+
name: 'entryPoint A',
158+
declareAPIs: () => [{ name: 'API A' }],
159+
getDependencyAPIs: () => [{ name: 'missing A' }]
160+
},
161+
{
162+
name: 'entryPoint B',
163+
declareAPIs: () => [{ name: 'API B' }],
164+
getDependencyAPIs: () => [{ name: 'missing B' }]
165+
}
166+
],
167+
[]
168+
)
169+
jest.runAllTimers()
170+
const error: Error = await hostPromise.then(
171+
() => {
172+
throw new Error('expected hostPromise to reject')
173+
},
174+
e => e
175+
)
176+
177+
expect(error.message).toContain('"missing A"')
178+
expect(error.message).toContain('"missing B"')
179+
})
180+
181+
it('should respect a custom loading timeout', async () => {
182+
let settled = false
183+
const hostPromise = createAppHostAndWaitForLoading([dependsOnMockPackageEntryPoint], [], 10000)
184+
hostPromise.catch(() => (settled = true))
185+
186+
jest.advanceTimersByTime(9999)
187+
await Promise.resolve()
188+
await Promise.resolve()
189+
expect(settled).toBe(false)
190+
191+
jest.advanceTimersByTime(1)
192+
await expect(hostPromise).rejects.toThrow('timed out after 10000ms')
193+
})
152194
})
153195
})

packages/repluggable-core/testKit/index.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,11 @@ export function createAppHostWithPacts(packages: EntryPointOrPackage[], pacts: P
8181
})
8282
}
8383

84-
export async function createAppHostAndWaitForLoading(packages: EntryPointOrPackage[], pacts: PactAPIBase[]): Promise<AppHost> {
84+
export async function createAppHostAndWaitForLoading(
85+
packages: EntryPointOrPackage[],
86+
pacts: PactAPIBase[],
87+
timeout: number = 3000
88+
): Promise<AppHost> {
8589
const appHost = createAppHostWithPacts(packages, pacts)
8690
const declaredAPIs = _(packages)
8791
.flatten()
@@ -92,17 +96,27 @@ export async function createAppHostAndWaitForLoading(packages: EntryPointOrPacka
9296
setTimeout(() => {
9397
const readyAPIs = Array.from(globalThis.repluggableAppDebug.readyAPIs)
9498
const unreadyAPIs = declaredAPIs.filter(api => !readyAPIs.some(readyAPI => readyAPI.name === api.name))
95-
const rootUnreadyAPI = globalThis.repluggableAppDebug.utils.getRootUnreadyAPI()
99+
const unreadyEntryPoints = globalThis.repluggableAppDebug.utils.unReadyEntryPoints()
100+
const readyNames = new Set(readyAPIs.map(api => api.name))
101+
const declaredByUnready = new Set(unreadyEntryPoints.flatMap(ep => (ep.declareAPIs?.() || []).map(key => key.name)))
102+
const rootUnreadyAPIs = _.uniqBy(
103+
unreadyEntryPoints
104+
.flatMap(ep => ep.getDependencyAPIs?.() || [])
105+
.filter(key => !readyNames.has(key.name) && !declaredByUnready.has(key.name)),
106+
'name'
107+
)
96108

97109
reject(
98110
new Error(
99-
`createAppHostAndWaitForLoading - waiting for loading timed out.
100-
there's a high chance this missing API is the main reason for it: ${JSON.stringify(rootUnreadyAPI)}
111+
`createAppHostAndWaitForLoading - waiting for loading timed out after ${timeout}ms.
112+
these root unready APIs are most likely the reason - unready entry points require them but nothing declares them (missing entry point or pact?): ${JSON.stringify(
113+
rootUnreadyAPIs
114+
)}
101115
102116
in addition here's the full list of declared APIs that have not been contributed: ${JSON.stringify(unreadyAPIs)}`
103117
)
104118
)
105-
}, 3000)
119+
}, timeout)
106120
})
107121

108122
const loadingPromise = new Promise<void>(async resolve => {

0 commit comments

Comments
 (0)