[gold] Extract out eventPromise() from corpus-selector-sk_test.js into test_util.js.

Bug: skia:9525
Change-Id: I278a64a33aab35806ff27b6ef2a0a60d1134c9cd
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/255141
Commit-Queue: Leandro Lovisolo <lovisolo@google.com>
Reviewed-by: Kevin Lubick <kjlubick@google.com>
diff --git a/golden/modules/corpus-selector-sk/corpus-selector-sk_test.js b/golden/modules/corpus-selector-sk/corpus-selector-sk_test.js
index 7239b97..3adae1c 100644
--- a/golden/modules/corpus-selector-sk/corpus-selector-sk_test.js
+++ b/golden/modules/corpus-selector-sk/corpus-selector-sk_test.js
@@ -1,6 +1,7 @@
 import './index.js';
 import { $, $$ } from 'common-sk/modules/dom';
 import { deepCopy } from 'common-sk/modules/object';
+import { eventPromise } from '../test_util';
 import { fetchMock } from 'fetch-mock';
 import { trstatus } from './test_data';
 
@@ -249,53 +250,3 @@
     expect(corporaLiText(corpusSelectorSk)).to.be.empty;
   })
 });
-
-// TODO(lovisolo): Move to test_util.js.
-// Returns a promise that will resolve when the given event is caught at the
-// document's body element, or reject if the event isn't caught within the given
-// amount of time.
-//
-// Set timeoutMillis = 0 to skip call to setTimeout(). This is necessary on
-// tests that simulate the passing of time with sinon.useFakeTimers(), which
-// could trigger the timeout before the promise has a chance to catch the event.
-//
-// Sample usage:
-//
-//   // Code under test.
-//   function doSomethingThatTriggersCustomEvent() {
-//     ...
-//     this.dispatchEvent(
-//         new CustomEvent('my-event', {detail: {foo: 'bar'}, bubbles: true});
-//   }
-//
-//   // Test.
-//   it('should trigger a custom event', async () => {
-//     const myEvent = eventPromise('my-event');
-//     doSomethingThatTriggersCustomEvent();
-//     const ev = await myEvent;
-//     expect(ev.detail.foo).to.equal('bar');
-//   });
-function eventPromise(event, timeoutMillis = 5000) {
-  // The executor function passed as a constructor argument to the Promise
-  // object is executed immediately. This guarantees that the event handler
-  // is added to document.body before returning.
-  return new Promise((resolve, reject) => {
-    let timeout;
-    const handler = (e) => {
-      document.body.removeEventListener(event, handler);
-      clearTimeout(timeout);
-      resolve(e);
-    };
-    // Skip setTimeout() call with timeoutMillis = 0. Useful when faking time in
-    // tests with sinon.useFakeTimers(). See
-    // https://sinonjs.org/releases/v7.5.0/fake-timers/.
-    if (timeoutMillis !== 0) {
-      timeout = setTimeout(() => {
-        document.body.removeEventListener(event, handler);
-        reject(new Error(`timed out after ${timeoutMillis} ms ` +
-            `while waiting to catch event "${event}"`));
-      }, timeoutMillis);
-    }
-    document.body.addEventListener(event, handler);
-  });
-}
diff --git a/golden/modules/test_util.js b/golden/modules/test_util.js
index d4fbfef..504590e 100644
--- a/golden/modules/test_util.js
+++ b/golden/modules/test_util.js
@@ -23,4 +23,60 @@
     if (calls.length) {
       console.warn('unmatched POSTS', calls);
     }
-}
\ No newline at end of file
+}
+
+/**
+ * Returns a promise that will resolve when the given event is caught at the
+ * document's body element, or reject if the event isn't caught within the given
+ * amount of time.
+ *
+ * Set timeoutMillis = 0 to skip call to setTimeout(). This is necessary on
+ * tests that simulate the passing of time with sinon.useFakeTimers(), which
+ * could trigger the timeout before the promise has a chance to catch the event.
+ *
+ * Sample usage:
+ *
+ *   // Code under test.
+ *   function doSomethingThatTriggersCustomEvent() {
+ *     ...
+ *     this.dispatchEvent(
+ *         new CustomEvent('my-event', {detail: {foo: 'bar'}, bubbles: true});
+ *   }
+ *
+ *   // Test.
+ *   it('should trigger a custom event', async () => {
+ *     const myEvent = eventPromise('my-event');
+ *     doSomethingThatTriggersCustomEvent();
+ *     const ev = await myEvent;
+ *     expect(ev.detail.foo).to.equal('bar');
+ *   });
+ *
+ * @param event {string} Name of event to catch.
+ * @param timeoutMillis {number} How long to wait for the event before rejecting
+ *     the returned promise.
+ * @return {Promise} A promise that will resolve to the caught event.
+ */
+export function eventPromise(event, timeoutMillis = 5000) {
+  // The executor function passed as a constructor argument to the Promise
+  // object is executed immediately. This guarantees that the event handler
+  // is added to document.body before returning.
+  return new Promise((resolve, reject) => {
+    let timeout;
+    const handler = (e) => {
+      document.body.removeEventListener(event, handler);
+      clearTimeout(timeout);
+      resolve(e);
+    };
+    // Skip setTimeout() call with timeoutMillis = 0. Useful when faking time in
+    // tests with sinon.useFakeTimers(). See
+    // https://sinonjs.org/releases/v7.5.0/fake-timers/.
+    if (timeoutMillis !== 0) {
+      timeout = setTimeout(() => {
+        document.body.removeEventListener(event, handler);
+        reject(new Error(`timed out after ${timeoutMillis} ms ` +
+            `while waiting to catch event "${event}"`));
+      }, timeoutMillis);
+    }
+    document.body.addEventListener(event, handler);
+  });
+}
diff --git a/golden/modules/test_util_test.js b/golden/modules/test_util_test.js
new file mode 100644
index 0000000..00e0686
--- /dev/null
+++ b/golden/modules/test_util_test.js
@@ -0,0 +1,48 @@
+import {eventPromise} from './test_util';
+
+describe('test utilities', () => {
+
+  describe('eventPromise', () => {
+    let el; // Element that we'll dispatch custom events from.
+    let clock;
+
+    beforeEach(() => {
+      el = document.createElement('div');
+      document.body.appendChild(el);
+      clock = sinon.useFakeTimers();
+    });
+
+    afterEach(() => {
+      document.body.removeChild(el);
+      clock.restore();
+    });
+
+    it('resolves when event is caught', async () => {
+      const hello = eventPromise('hello');
+      el.dispatchEvent(new CustomEvent('hello', {bubbles: true, detail: 'hi'}));
+      const ev = await hello;
+      expect(ev.detail).to.equal('hi');
+    });
+
+    it('times out if event is not caught', async () => {
+      const hello = eventPromise('hello', 5000);
+      el.dispatchEvent(new CustomEvent('bye', {bubbles: true}));
+      clock.tick(10000);
+      try {
+        await hello;
+        expect.fail('promise should not have resolved');
+      } catch(error) {
+        expect(error.message).to.equal(
+          'timed out after 5000 ms while waiting to catch event "hello"');
+      }
+    });
+
+    it('never times out if timeoutMillis=0', async () => {
+      const hello = eventPromise('hello', 0);
+      clock.tick(Number.MAX_SAFE_INTEGER);
+      el.dispatchEvent(new CustomEvent('hello', {bubbles: true, detail: 'hi'}));
+      const ev = await hello;
+      expect(ev.detail).to.equal('hi');
+    });
+  });
+});
\ No newline at end of file