Skip to content

Commit

Permalink
Implement support for using async callbacks with act
Browse files Browse the repository at this point in the history
Implement initial support for `act` callbacks returning a Promise, in
which case the promise is awaited before state updates and effects are
flushed.

See https://reactjs.org/blog/2019/08/08/react-v16.9.0.html#async-act-for-testing
for more details on the motivation.
  • Loading branch information
robertknight committed Aug 11, 2019
1 parent 066c789 commit 5658e22
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 10 deletions.
28 changes: 18 additions & 10 deletions test-utils/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,28 @@ export function act(cb) {
// Override requestAnimationFrame so we can flush pending hooks.
options.requestAnimationFrame = (fc) => flush = fc;

// Execute the callback we were passed.
cb();
rerender();
const finish = () => {
rerender();
while (flush) {
toFlush = flush;
flush = null;

while (flush) {
toFlush = flush;
flush = null;
toFlush();
rerender();
}

toFlush();
rerender();
teardown();
options.requestAnimationFrame = previousRequestAnimationFrame;
};

const result = cb();

if (result != null && typeof result.then === 'function') {
return result.then(finish);
}

teardown();
options.requestAnimationFrame = previousRequestAnimationFrame;
finish();
return Promise.resolve();
}

/**
Expand Down
47 changes: 47 additions & 0 deletions test-utils/test/shared/act.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,4 +191,51 @@ describe('act', () => {
});
expect(scratch.firstChild.textContent).to.equal('1');
});

it('returns a Promise if invoked with a sync callback', () => {
const result = act(() => {});
expect(result.then).to.be.a('function');
return result;
});

it('returns a Promise if invoked with an async callback', () => {
const result = act(async () => {});
expect(result.then).to.be.a('function');
return result;
});

it('should await "thenable" result of callback before flushing', async () => {
const events = [];

function TestComponent() {
useEffect(() => {
events.push('flushed effect');
}, []);
events.push('scheduled effect');
return <div>Test</div>;
}

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

events.push('began test');
const acted = act(async () => {
events.push('began act callback');
await delay(1);
render(<TestComponent />, scratch);
events.push('end act callback');
});
events.push('act returned');
await acted;
events.push('act result resolved');

expect(events).to.deep.equal([
'began test',
'began act callback',
'act returned',
'scheduled effect',
'end act callback',
'flushed effect',
'act result resolved'
]);
});
});

0 comments on commit 5658e22

Please sign in to comment.