Skip to content

Commit 707a6a9

Browse files
committed
fix: resolve CI failures (lint, coverage) in renderAsync
- Refactor resolveElementProps to use Promise.all instead of await-in-loop (fixes no-await-in-loop lint error) - Wrap resolved values in {value, changed} objects so Promise.all does not inadvertently flatten Promise-valued props (e.g. dataPromise for use()) - Remove unreachable props == null guard (React elements always have props) - Add test for non-element objects in children to cover resolveElement safety branch (96.15% branch coverage, above 95% threshold) Made-with: Cursor
1 parent 4a4e74c commit 707a6a9

2 files changed

Lines changed: 40 additions & 28 deletions

File tree

src/__tests__/renderAsync.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,21 @@ describe('renderAsync', () => {
260260
expect(screen.getByTestId('content')).toHaveTextContent('Content')
261261
})
262262

263+
test('passes through non-element objects in children unchanged', async () => {
264+
function Container({children}) {
265+
return <div data-testid="container">{String(children)}</div>
266+
}
267+
268+
const plainObj = {
269+
toString() {
270+
return 'stringified'
271+
},
272+
}
273+
274+
await renderAsync(React.createElement(Container, null, plainObj))
275+
expect(screen.getByTestId('container')).toHaveTextContent('stringified')
276+
})
277+
263278
test('returns standard render result properties', async () => {
264279
const {container, baseElement, debug, unmount, asFragment} =
265280
await renderAsync(<AsyncHelloWorld />)

src/pure.js

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -350,37 +350,34 @@ async function resolveElement(element) {
350350
}
351351

352352
async function resolveElementProps(element) {
353-
const props = element.props
354-
if (props == null) {
355-
return element
356-
}
353+
const keys = Object.keys(element.props).filter(k => k !== 'children')
354+
355+
// Resolve React-element-valued props in parallel.
356+
// Wrap results in {value, changed} objects so Promise.all does not
357+
// inadvertently flatten Promise-valued props (e.g. dataPromise for use()).
358+
const results = await Promise.all(
359+
keys.map(key => {
360+
const value = element.props[key]
361+
if (React.isValidElement(value)) {
362+
return resolveElement(value).then(resolved => ({
363+
value: resolved,
364+
changed: resolved !== value,
365+
}))
366+
}
367+
return {value, changed: false}
368+
}),
369+
)
357370

358-
let propsChanged = false
359-
let childrenChanged = false
371+
const propsChanged = results.some(r => r.changed)
360372
const newProps = {}
361-
let resolvedChildren
362-
363-
for (const key of Object.keys(props)) {
364-
const value = props[key]
365-
366-
if (key === 'children') {
367-
resolvedChildren = await resolveElement(value)
368-
childrenChanged = resolvedChildren !== value
369-
continue
370-
}
373+
keys.forEach((key, i) => {
374+
newProps[key] = results[i].value
375+
})
371376

372-
// Only resolve values that are React elements to avoid inadvertently
373-
// awaiting Promise-valued props (e.g. dataPromise for use())
374-
if (React.isValidElement(value)) {
375-
const resolved = await resolveElement(value)
376-
newProps[key] = resolved
377-
if (resolved !== value) {
378-
propsChanged = true
379-
}
380-
} else {
381-
newProps[key] = value
382-
}
383-
}
377+
const children = element.props.children
378+
const resolvedChildren =
379+
children == null ? children : await resolveElement(children)
380+
const childrenChanged = resolvedChildren !== children
384381

385382
if (!propsChanged && !childrenChanged) {
386383
return element

0 commit comments

Comments
 (0)