diff --git a/dom/push/test/test_register_key.html b/dom/push/test/test_register_key.html
index ff80b1afda7224d49808bc82128a0078aeb09ef6..b3e75707705ca9011042db8587676769abc8d54b 100644
--- a/dom/push/test/test_register_key.html
+++ b/dom/push/test/test_register_key.html
@@ -78,34 +78,6 @@ http://creativecommons.org/licenses/publicdomain/
     controlledFrame = await injectControlledFrame();
   });
 
-  add_task(async function emptyKey() {
-    try {
-      await registration.pushManager.subscribe({
-        applicationServerKey: new ArrayBuffer(0),
-      });
-      ok(false, "Should reject for empty app server keys");
-    } catch (error) {
-      ok(error instanceof DOMException,
-        "Wrong exception type for empty key");
-      is(error.name, "InvalidAccessError",
-        "Wrong exception name for empty key");
-    }
-  });
-
-  add_task(async function invalidKey() {
-    try {
-      await registration.pushManager.subscribe({
-        applicationServerKey: new Uint8Array([0]),
-      });
-      ok(false, "Should reject for invalid app server keys");
-    } catch (error) {
-      ok(error instanceof DOMException,
-        "Wrong exception type for invalid key");
-      is(error.name, "InvalidAccessError",
-        "Wrong exception name for invalid key");
-    }
-  });
-
   add_task(async function validKey() {
     var pushSubscription = await registration.pushManager.subscribe({
       applicationServerKey: await generateKey(),
diff --git a/testing/web-platform/meta/push-api/__dir__.ini b/testing/web-platform/meta/push-api/__dir__.ini
index 7a2b4cc48ef3da61f41dce132b941fa529d78537..77d79360bae1eb79b78f7a2e0069a318b9f7cea1 100644
--- a/testing/web-platform/meta/push-api/__dir__.ini
+++ b/testing/web-platform/meta/push-api/__dir__.ini
@@ -1 +1,2 @@
 lsan-allowed: [Alloc, Create, Malloc, Realloc, Then, mozilla::BasePrincipal::CreateContentPrincipal, mozilla::dom::DocGroup::Create, mozilla::dom::ServiceWorkerManager::Unregister, mozilla::dom::ServiceWorkerRegistrationMainThread::Unregister, mozilla::dom::UnregisterCallback::UnregisterCallback, mozilla::net::nsStandardURL::TemplatedMutator, operator]
+prefs: [notification.prompt.testing:true, dom.push.testing.ignorePermission:true, marionette.setpermission.enabled:true]
diff --git a/testing/web-platform/tests/push-api/noop-sw.js b/testing/web-platform/tests/push-api/noop-sw.js
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/testing/web-platform/tests/push-api/resources/helpers.js b/testing/web-platform/tests/push-api/resources/helpers.js
new file mode 100644
index 0000000000000000000000000000000000000000..8395c638f3ee0170e44b35bac866729371db70c6
--- /dev/null
+++ b/testing/web-platform/tests/push-api/resources/helpers.js
@@ -0,0 +1,12 @@
+function resetSw() {
+  return navigator.serviceWorker.getRegistrations().then(registrations => {
+    return Promise.all(registrations.map(r => r.unregister()));
+  });
+}
+
+async function registerSw(path) {
+  await resetSw();
+  add_completion_callback(resetSw);
+  const reg = await navigator.serviceWorker.register(path);
+  return reg;
+}
diff --git a/testing/web-platform/tests/push-api/subscribe-with-faulty-applicationServerKey.https.window.js b/testing/web-platform/tests/push-api/subscribe-with-faulty-applicationServerKey.https.window.js
new file mode 100644
index 0000000000000000000000000000000000000000..7d600803e4329da981f8f4be1a017a5e711e8698
--- /dev/null
+++ b/testing/web-platform/tests/push-api/subscribe-with-faulty-applicationServerKey.https.window.js
@@ -0,0 +1,62 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=resources/helpers.js
+
+// NOTE:
+// We are not testing success cases here as doing so will try creating external network
+// connection, which is not allowed by all browser test environments.
+// (e.g. Gecko explicitly disables push service for testing environment.)
+// Ideally we should have WPT-specific mock server in this case. See also
+// https://github.com/w3c/push-api/issues/365.
+
+promise_setup(async () => {
+  // The spec does not enforce validation order and implementations
+  // indeed check other things before checking applicationServerKey.
+
+  // Get the permission because Firefox checks it before key validation.
+  // (The permission test is done in permission.https.html.)
+  await test_driver.set_permission({ name: "notifications" }, "granted");
+  // Get the active service worker because Chrome checks it before key validation
+  registration = await registerSw("noop-sw.js");
+  await navigator.serviceWorker.ready;
+});
+
+promise_test(async (t) => {
+  await promise_rejects_dom(
+    t,
+    "InvalidAccessError",
+    registration.pushManager.subscribe({ applicationServerKey: "" }),
+  );
+}, "Reject empty string applicationServerKey");
+
+promise_test(async (t) => {
+  await promise_rejects_dom(
+    t,
+    "InvalidAccessError",
+    registration.pushManager.subscribe({ applicationServerKey: new ArrayBuffer(0) }),
+  );
+}, "Reject empty ArrayBuffer applicationServerKey");
+
+promise_test(async (t) => {
+  await promise_rejects_dom(
+    t,
+    "InvalidAccessError",
+    registration.pushManager.subscribe({ applicationServerKey: new Uint8Array(0) }),
+  );
+}, "Reject empty Uint8Array applicationServerKey");
+
+promise_test(async (t) => {
+  await promise_rejects_dom(
+    t,
+    "InvalidAccessError",
+    registration.pushManager.subscribe({ applicationServerKey: new Uint8Array([1, 2, 3]) }),
+  );
+}, "Reject a key that is not a valid point on P-256 curve");
+
+promise_test(async (t) => {
+  await promise_rejects_dom(
+    t,
+    "InvalidCharacterError",
+    registration.pushManager.subscribe({ applicationServerKey: "!@#$^&*" }),
+  );
+}, "Reject a string key that can't be decoded by base64url");