From 3a107f0a576d85d475e37623824414cfa197ce17 Mon Sep 17 00:00:00 2001
From: Jovi De Croock <decroockjovi@gmail.com>
Date: Thu, 21 Mar 2019 12:10:11 +0100
Subject: [PATCH 1/4] tests: provide test for bugging scenario

---
 test-utils/test/shared/act.test.js | 50 +++++++++++++++++++++++-------
 1 file changed, 39 insertions(+), 11 deletions(-)

diff --git a/test-utils/test/shared/act.test.js b/test-utils/test/shared/act.test.js
index baf03c2aa5..e5fbb3806e 100644
--- a/test-utils/test/shared/act.test.js
+++ b/test-utils/test/shared/act.test.js
@@ -1,5 +1,6 @@
 import { options, createElement as h, render } from 'preact';
 import { useEffect, useState } from 'preact/hooks';
+import sinon from 'sinon';
 
 import { setupScratch, teardown } from '../../../test/_util/helpers';
 import { act } from '../../src';
@@ -24,9 +25,47 @@ describe('act', () => {
 		expect(options.requestAnimationFrame).to.equal(undefined);
 	});
 
+	it('should flush pending effects', () => {
+		let spy = sinon.spy();
+		function StateContainer() {
+			useEffect(spy);
+			return <div />;
+		}
+		act(() => render(<StateContainer />, scratch));
+		expect(spy).to.be.calledOnce;
+	});
+
+	it('should flush pending and initial effects', () => {
+		const spy = sinon.spy();
+		function StateContainer() {
+			const [count, setCount] = useState(0);
+			useEffect(() => spy(), [count]);
+			return (
+				<div>
+					<p>Count: {count}</p>
+					<button onClick={() => setCount(c => c + 11)} />
+				</div>
+			);
+		}
+
+		act(() => render(<StateContainer />, scratch));
+		expect(spy).to.be.calledOnce;
+		expect(scratch.textContent).to.include('Count: 0');
+		act(() => {
+			const button = scratch.querySelector('button');
+			button.click();
+			expect(spy).to.be.calledOnce;
+			expect(scratch.textContent).to.include('Count: 0');
+		});
+		expect(spy).to.be.calledTwice;
+		expect(scratch.textContent).to.include('Count: 1');
+	});
+
 	it('should drain the queue of hooks', () => {
+		const spy = sinon.spy();
 		function StateContainer() {
 			const [count, setCount] = useState(0);
+			useEffect(() => spy());
 			return (<div>
 				<p>Count: {count}</p>
 				<button onClick={() => setCount(c => c + 11)} />
@@ -43,17 +82,6 @@ describe('act', () => {
 		expect(scratch.textContent).to.include('Count: 1');
 	});
 
-	it('should flush pending effects', () => {
-		let spy = sinon.spy();
-		function StateContainer() {
-			useEffect(spy);
-			return <div />;
-		}
-
-		act(() => render(<StateContainer />, scratch));
-		expect(spy).to.be.calledOnce;
-	});
-
 	it('should restore options.requestAnimationFrame', () => {
 		const spy = sinon.spy();
 

From ad1afcdbaf2a1efc33bf0abd2726e328fad6af61 Mon Sep 17 00:00:00 2001
From: Jovi De Croock <decroockjovi@gmail.com>
Date: Thu, 21 Mar 2019 12:10:47 +0100
Subject: [PATCH 2/4] fix: solve scenario where a state update triggered an
 effect, this effect would be queued for next render and thus not be executed

---
 test-utils/src/index.js | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/test-utils/src/index.js b/test-utils/src/index.js
index 270147926b..77b2ffe2f2 100644
--- a/test-utils/src/index.js
+++ b/test-utils/src/index.js
@@ -11,16 +11,24 @@ export function setupRerender() {
 }
 
 export function act(cb) {
-	const previousDebounce = options.debounceRendering;
 	const previousRequestAnimationFrame = options.requestAnimationFrame;
 	const rerender = setupRerender();
 	let flush;
+	// Override requestAnimationFrame so we can flush pending hooks.
 	options.requestAnimationFrame = (fc) => flush = fc;
+	// Execute the callback we were passed.
 	cb();
+	// State COULD be built up flush it.
 	if (flush) {
 		flush();
 	}
+	// Rerender
 	rerender();
-	options.debounceRendering = previousDebounce;
+	// If rerendering with new state has triggered effects
+	// flush them aswell since options.raf will have repopulated this.
+	if (flush) {
+		flush();
+	}
+	options.debounceRendering = Component.__test__previousDebounce;
 	options.requestAnimationFrame = previousRequestAnimationFrame;
 }

From 1c5a777a5b2318248e0423f514ab67b21ca9d2f0 Mon Sep 17 00:00:00 2001
From: Jovi De Croock <decroockjovi@gmail.com>
Date: Thu, 21 Mar 2019 17:55:34 +0100
Subject: [PATCH 3/4] follow pr comments

---
 test-utils/src/index.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/test-utils/src/index.js b/test-utils/src/index.js
index 77b2ffe2f2..fb5457eea9 100644
--- a/test-utils/src/index.js
+++ b/test-utils/src/index.js
@@ -22,7 +22,6 @@ export function act(cb) {
 	if (flush) {
 		flush();
 	}
-	// Rerender
 	rerender();
 	// If rerendering with new state has triggered effects
 	// flush them aswell since options.raf will have repopulated this.

From 99e848fe01b263ee05a0dab725f2b5b9a5325a91 Mon Sep 17 00:00:00 2001
From: Jovi De Croock <decroockjovi@gmail.com>
Date: Thu, 21 Mar 2019 17:56:04 +0100
Subject: [PATCH 4/4] remove sinon

---
 test-utils/test/shared/act.test.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/test-utils/test/shared/act.test.js b/test-utils/test/shared/act.test.js
index e5fbb3806e..f0e410ee53 100644
--- a/test-utils/test/shared/act.test.js
+++ b/test-utils/test/shared/act.test.js
@@ -1,6 +1,5 @@
 import { options, createElement as h, render } from 'preact';
 import { useEffect, useState } from 'preact/hooks';
-import sinon from 'sinon';
 
 import { setupScratch, teardown } from '../../../test/_util/helpers';
 import { act } from '../../src';