[beacon-api] Add basic wpt to cover navigation + pending beacon

Bug: 1293679
Change-Id: I241cbb7737aba89c2f925f71ee64dc0eacde5d17
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3990119
Reviewed-by: Rakina Zata Amni <rakina@chromium.org>
Commit-Queue: Ming-Ying Chung <mych@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1065387}
diff --git a/pending-beacon/pending_beacon-sendonhidden.tentative.https.window.js b/pending-beacon/pending_beacon-sendonhidden.tentative.https.window.js
new file mode 100644
index 0000000..5fca0a6
--- /dev/null
+++ b/pending-beacon/pending_beacon-sendonhidden.tentative.https.window.js
@@ -0,0 +1,84 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=/html/browsers/browsing-the-web/back-forward-cache/resources/rc-helper.js
+// META: script=./resources/pending_beacon-helper.js
+
+'use strict';
+
+for (const beaconType of BeaconTypes) {
+  const beaconName = beaconType.name;
+
+  parallelPromiseTest(async t => {
+    const uuid = token();
+    const url = generateSetBeaconURL(uuid);
+    // backgroundTimeout = 0s means `beacon should be sent out right on
+    // entering `hidden` state after navigating away.
+    const options = {backgroundTimeout: 0};
+    const helper = new RemoteContextHelper();
+    // Opens a window with noopener so that BFCache will work.
+    const rc1 = await helper.addWindow(
+        /*config=*/ null, /*options=*/ {features: 'noopener'});
+
+    // Creates a PendingBeacon in remote which should only be sent on navigating
+    // away.
+    await rc1.executeScript((beaconName, url, options) => {
+      const beacon = beaconName == 'PendingGetBeacon' ?
+          new PendingGetBeacon(url, options) :
+          new PendingPostBeacon(url, options);
+    }, [beaconName, url, options]);
+
+    await expectBeacon(uuid, {count: 0});
+  }, `${beaconName}: does not send without page navigation.`);
+
+  parallelPromiseTest(async t => {
+    const uuid = token();
+    const url = generateSetBeaconURL(uuid);
+    // backgroundTimeout = 0s means `beacon should be sent out right on
+    // entering `hidden` state after navigating away.
+    const options = {backgroundTimeout: 0};
+    const helper = new RemoteContextHelper();
+    // Opens a window with noopener so that BFCache will work.
+    const rc1 = await helper.addWindow(
+        /*config=*/ null, /*options=*/ {features: 'noopener'});
+
+    // Creates a PendingBeacon in remote which should only be sent on navigating
+    // away.
+    await rc1.executeScript((beaconName, url, options) => {
+      const beacon = beaconName == 'PendingGetBeacon' ?
+          new PendingGetBeacon(url, options) :
+          new PendingPostBeacon(url, options);
+    }, [beaconName, url, options]);
+    // Navigates away to trigger beacon sending.
+    rc1.navigateToNew();
+
+    await expectBeacon(uuid, {count: 1});
+  }, `${beaconName}: sends on page entering hidden state (w/ BFCache).`);
+
+  parallelPromiseTest(async t => {
+    const uuid = token();
+    const url = generateSetBeaconURL(uuid);
+    // backgroundTimeout = 0s means `beacon should be sent out right on
+    // entering `hidden` state after navigating away.
+    const options = {backgroundTimeout: 0};
+    const helper = new RemoteContextHelper();
+    // Opens a window without BFCache.
+    const rc1 = await helper.addWindow();
+
+    // Creates a PendingBeacon in remote which should only be sent on navigating
+    // away.
+    await rc1.executeScript((beaconName, url, options) => {
+      const beacon = beaconName == 'PendingGetBeacon' ?
+          new PendingGetBeacon(url, options) :
+          new PendingPostBeacon(url, options);
+    }, [beaconName, url, options]);
+    // Navigates away to trigger beacon sending.
+    rc1.navigateToNew();
+
+    // TODO(crbug.com/1378830): expectBeacon count should be 1 after fixing.
+    await expectBeacon(uuid, {count: 0});
+  }, `${beaconName}: sends on page entering hidden state (w/o BFCache).`);
+}
diff --git a/pending-beacon/resources/pending_beacon-helper.js b/pending-beacon/resources/pending_beacon-helper.js
index 6d1cc80..3e8bd20 100644
--- a/pending-beacon/resources/pending_beacon-helper.js
+++ b/pending-beacon/resources/pending_beacon-helper.js
@@ -148,6 +148,12 @@
         return await res.json();
       },
       (res) => {
+        if (expectedCount == 0) {
+          // If expecting no beacon, we should try to wait as long as possible.
+          // So always returning false here until `poll()` decides to terminate
+          // itself.
+          return false;
+        }
         return res.data.length == expectedCount;
       });
   if (!options || !options.data) {