URLPattern: Add per-component pattern getters.

This CL exposes the parsed, canonical pattern as a property on the
URLPattern object.  This canonical pattern string is generated from
the liburlpattern::Pattern object.

The CL also expands on the WPT test to verify pattern string properties
are exposed properly.  The test driver does most of the work and the
data file only needs to provide overrides where the canonicalization
has made the expected pattern non-obvious.

Finally, this CL also drops the TODO to add a stringifier.  As discussed
in the following issue we have decided not to add a stringifier for now:

https://github.com/WICG/urlpattern/issues/47

Developers may try to use a stringifier as a serialization mechanism
which is something we want to avoid.  The URLPattern properties,
however, are exposed and human readable in devtools.

Bug: 1141510
Change-Id: Ifeacd06d5885158777b290f8eb54ed2175be7201
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2729489
Commit-Queue: Ben Kelly <wanderview@chromium.org>
Reviewed-by: Jeremy Roman <jbroman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#863284}
diff --git a/urlpattern/resources/urlpatterntestdata.json b/urlpattern/resources/urlpatterntestdata.json
index ae9405e..b135406 100644
--- a/urlpattern/resources/urlpatterntestdata.json
+++ b/urlpattern/resources/urlpatterntestdata.json
@@ -2,7 +2,7 @@
   {
     "pattern": { "pathname": "/foo/bar" },
     "input": { "pathname": "/foo/bar" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar" },
       "pathname": { "input": "/foo/bar", "groups": {} }
     }
@@ -10,22 +10,22 @@
   {
     "pattern": { "pathname": "/foo/bar" },
     "input": { "pathname": "/foo/ba" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/bar" },
     "input": { "pathname": "/foo/bar/" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/bar" },
     "input": { "pathname": "/foo/bar/baz" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/bar" },
     "input": "https://example.com/foo/bar",
-    "expected": {
+    "expected_match": {
       "input": "https://example.com/foo/bar",
       "hostname": { "input": "example.com", "groups": { "0": "example.com" } },
       "pathname": { "input": "/foo/bar", "groups": {} },
@@ -35,12 +35,12 @@
   {
     "pattern": { "pathname": "/foo/bar" },
     "input": "https://example.com/foo/bar/baz",
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/bar" },
     "input": { "hostname": "example.com", "pathname": "/foo/bar" },
-    "expected": {
+    "expected_match": {
       "input": { "hostname": "example.com", "pathname": "/foo/bar" },
       "hostname": { "input": "example.com", "groups": { "0": "example.com" } },
       "pathname": { "input": "/foo/bar", "groups": {} }
@@ -49,12 +49,12 @@
   {
     "pattern": { "pathname": "/foo/bar" },
     "input": { "hostname": "example.com", "pathname": "/foo/bar/baz" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/bar" },
     "input": { "pathname": "/foo/bar", "baseURL": "https://example.com" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar", "baseURL": "https://example.com" },
       "hostname": { "input": "example.com", "groups": { "0": "example.com" } },
       "pathname": { "input": "/foo/bar", "groups": {} },
@@ -64,26 +64,26 @@
   {
     "pattern": { "pathname": "/foo/bar" },
     "input": { "pathname": "/foo/bar/baz", "baseURL": "https://example.com" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/bar",
                  "baseURL": "https://example.com?query#hash" },
     "input": { "pathname": "/foo/bar" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/bar",
                  "baseURL": "https://example.com?query#hash" },
     "input": { "hostname": "example.com", "pathname": "/foo/bar" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/bar",
                  "baseURL": "https://example.com?query#hash" },
     "input": { "protocol": "https", "hostname": "example.com",
                "pathname": "/foo/bar" },
-    "expected": {
+    "expected_match": {
       "input": { "protocol": "https", "hostname": "example.com",
                  "pathname": "/foo/bar" },
       "exactly_empty_components": [ "username", "password", "port" ],
@@ -97,7 +97,7 @@
                  "baseURL": "https://example.com?query#hash" },
     "input": { "protocol": "https", "hostname": "example.com",
                "pathname": "/foo/bar/baz" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/bar",
@@ -105,7 +105,7 @@
     "input": { "protocol": "https", "hostname": "example.com",
                "pathname": "/foo/bar", "search": "otherquery",
                "hash": "otherhash" },
-    "expected": {
+    "expected_match": {
       "input": { "protocol": "https", "hostname": "example.com",
                  "pathname": "/foo/bar", "search": "otherquery",
                  "hash": "otherhash" },
@@ -121,7 +121,7 @@
     "pattern": { "pathname": "/foo/bar",
                  "baseURL": "https://example.com?query#hash" },
     "input": "https://example.com/foo/bar",
-    "expected": {
+    "expected_match": {
       "input": "https://example.com/foo/bar",
       "exactly_empty_components": [ "username", "password", "port" ],
       "hostname": { "input": "example.com", "groups": {} },
@@ -133,7 +133,7 @@
     "pattern": { "pathname": "/foo/bar",
                  "baseURL": "https://example.com?query#hash" },
     "input": "https://example.com/foo/bar?otherquery#otherhash",
-    "expected": {
+    "expected_match": {
       "input": "https://example.com/foo/bar?otherquery#otherhash",
       "exactly_empty_components": [ "username", "password", "port" ],
       "hash": { "input": "otherhash", "groups": { "0": "otherhash" } },
@@ -147,25 +147,25 @@
     "pattern": { "pathname": "/foo/bar",
                  "baseURL": "https://example.com?query#hash" },
     "input": "https://example.com/foo/bar/baz",
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/bar",
                  "baseURL": "https://example.com?query#hash" },
     "input": "https://other.com/foo/bar",
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/bar",
                  "baseURL": "https://example.com?query#hash" },
     "input": "http://other.com/foo/bar",
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/bar",
                  "baseURL": "https://example.com?query#hash" },
     "input": { "pathname": "/foo/bar", "baseURL": "https://example.com" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar", "baseURL": "https://example.com" },
       "exactly_empty_components": [ "username", "password", "port" ],
       "hostname": { "input": "example.com", "groups": {} },
@@ -177,24 +177,24 @@
     "pattern": { "pathname": "/foo/bar",
                  "baseURL": "https://example.com?query#hash" },
     "input": { "pathname": "/foo/bar/baz", "baseURL": "https://example.com" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/bar",
                  "baseURL": "https://example.com?query#hash" },
     "input": { "pathname": "/foo/bar", "baseURL": "https://other.com" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/bar",
                  "baseURL": "https://example.com?query#hash" },
     "input": { "pathname": "/foo/bar", "baseURL": "http://example.com" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/:bar" },
     "input": { "pathname": "/foo/bar" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar" },
       "pathname": { "input": "/foo/bar", "groups": { "bar": "bar" } }
     }
@@ -202,7 +202,7 @@
   {
     "pattern": { "pathname": "/foo/:bar" },
     "input": { "pathname": "/foo/index.html" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/index.html" },
       "pathname": { "input": "/foo/index.html", "groups": { "bar": "index.html" } }
     }
@@ -210,17 +210,20 @@
   {
     "pattern": { "pathname": "/foo/:bar" },
     "input": { "pathname": "/foo/bar/" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/:bar" },
     "input": { "pathname": "/foo/" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/(.*)" },
     "input": { "pathname": "/foo/bar" },
-    "expected": {
+    "expected_obj": {
+      "pathname": "/foo/*"
+    },
+    "expected_match": {
       "input": { "pathname": "/foo/bar" },
       "pathname": { "input": "/foo/bar", "groups": { "0": "bar" } }
     }
@@ -228,7 +231,7 @@
   {
     "pattern": { "pathname": "/foo/*" },
     "input": { "pathname": "/foo/bar" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar" },
       "pathname": { "input": "/foo/bar", "groups": { "0": "bar" } }
     }
@@ -236,7 +239,10 @@
   {
     "pattern": { "pathname": "/foo/(.*)" },
     "input": { "pathname": "/foo/bar/baz" },
-    "expected": {
+    "expected_obj": {
+      "pathname": "/foo/*"
+    },
+    "expected_match": {
       "input": { "pathname": "/foo/bar/baz" },
       "pathname": { "input": "/foo/bar/baz", "groups": { "0": "bar/baz" } }
     }
@@ -244,7 +250,7 @@
   {
     "pattern": { "pathname": "/foo/*" },
     "input": { "pathname": "/foo/bar/baz" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar/baz" },
       "pathname": { "input": "/foo/bar/baz", "groups": { "0": "bar/baz" } }
     }
@@ -252,7 +258,10 @@
   {
     "pattern": { "pathname": "/foo/(.*)" },
     "input": { "pathname": "/foo/" },
-    "expected": {
+    "expected_obj": {
+      "pathname": "/foo/*"
+    },
+    "expected_match": {
       "input": { "pathname": "/foo/" },
       "pathname": { "input": "/foo/", "groups": { "0": "" } }
     }
@@ -260,7 +269,7 @@
   {
     "pattern": { "pathname": "/foo/*" },
     "input": { "pathname": "/foo/" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/" },
       "pathname": { "input": "/foo/", "groups": { "0": "" } }
     }
@@ -268,17 +277,20 @@
   {
     "pattern": { "pathname": "/foo/(.*)" },
     "input": { "pathname": "/foo" },
-    "expected": null
+    "expected_obj": {
+      "pathname": "/foo/*"
+    },
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/*" },
     "input": { "pathname": "/foo" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/:bar(.*)" },
     "input": { "pathname": "/foo/bar" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar" },
       "pathname": { "input": "/foo/bar", "groups": { "bar": "bar" } }
     }
@@ -286,7 +298,7 @@
   {
     "pattern": { "pathname": "/foo/:bar(.*)" },
     "input": { "pathname": "/foo/bar/baz" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar/baz" },
       "pathname": { "input": "/foo/bar/baz", "groups": { "bar": "bar/baz" } }
     }
@@ -294,7 +306,7 @@
   {
     "pattern": { "pathname": "/foo/:bar(.*)" },
     "input": { "pathname": "/foo/" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/" },
       "pathname": { "input": "/foo/", "groups": { "bar": "" } }
     }
@@ -302,12 +314,12 @@
   {
     "pattern": { "pathname": "/foo/:bar(.*)" },
     "input": { "pathname": "/foo" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/:bar?" },
     "input": { "pathname": "/foo/bar" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar" },
       "pathname": { "input": "/foo/bar", "groups": { "bar": "bar" } }
     }
@@ -315,7 +327,7 @@
   {
     "pattern": { "pathname": "/foo/:bar?" },
     "input": { "pathname": "/foo" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo" },
       "pathname": { "input": "/foo", "groups": { "bar": "" } }
     }
@@ -323,22 +335,22 @@
   {
     "pattern": { "pathname": "/foo/:bar?" },
     "input": { "pathname": "/foo/" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/:bar?" },
     "input": { "pathname": "/foobar" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/:bar?" },
     "input": { "pathname": "/foo/bar/baz" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/:bar+" },
     "input": { "pathname": "/foo/bar" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar" },
       "pathname": { "input": "/foo/bar", "groups": { "bar": "bar" } }
     }
@@ -346,7 +358,7 @@
   {
     "pattern": { "pathname": "/foo/:bar+" },
     "input": { "pathname": "/foo/bar/baz" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar/baz" },
       "pathname": { "input": "/foo/bar/baz", "groups": { "bar": "bar/baz" } }
     }
@@ -354,22 +366,22 @@
   {
     "pattern": { "pathname": "/foo/:bar+" },
     "input": { "pathname": "/foo" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/:bar+" },
     "input": { "pathname": "/foo/" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/:bar+" },
     "input": { "pathname": "/foobar" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/:bar*" },
     "input": { "pathname": "/foo/bar" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar" },
       "pathname": { "input": "/foo/bar", "groups": { "bar": "bar" } }
     }
@@ -377,7 +389,7 @@
   {
     "pattern": { "pathname": "/foo/:bar*" },
     "input": { "pathname": "/foo/bar/baz" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar/baz" },
       "pathname": { "input": "/foo/bar/baz", "groups": { "bar": "bar/baz" } }
     }
@@ -385,7 +397,7 @@
   {
     "pattern": { "pathname": "/foo/:bar*" },
     "input": { "pathname": "/foo" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo" },
       "pathname": { "input": "/foo", "groups": { "bar": "" } }
     }
@@ -393,17 +405,20 @@
   {
     "pattern": { "pathname": "/foo/:bar*" },
     "input": { "pathname": "/foo/" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/:bar*" },
     "input": { "pathname": "/foobar" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/(.*)?" },
     "input": { "pathname": "/foo/bar" },
-    "expected": {
+    "expected_obj": {
+      "pathname": "/foo/*?"
+    },
+    "expected_match": {
       "input": { "pathname": "/foo/bar" },
       "pathname": { "input": "/foo/bar", "groups": { "0": "bar" } }
     }
@@ -411,7 +426,7 @@
   {
     "pattern": { "pathname": "/foo/*?" },
     "input": { "pathname": "/foo/bar" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar" },
       "pathname": { "input": "/foo/bar", "groups": { "0": "bar" } }
     }
@@ -419,7 +434,10 @@
   {
     "pattern": { "pathname": "/foo/(.*)?" },
     "input": { "pathname": "/foo/bar/baz" },
-    "expected": {
+    "expected_obj": {
+      "pathname": "/foo/*?"
+    },
+    "expected_match": {
       "input": { "pathname": "/foo/bar/baz" },
       "pathname": { "input": "/foo/bar/baz", "groups": { "0": "bar/baz" } }
     }
@@ -427,7 +445,7 @@
   {
     "pattern": { "pathname": "/foo/*?" },
     "input": { "pathname": "/foo/bar/baz" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar/baz" },
       "pathname": { "input": "/foo/bar/baz", "groups": { "0": "bar/baz" } }
     }
@@ -435,7 +453,10 @@
   {
     "pattern": { "pathname": "/foo/(.*)?" },
     "input": { "pathname": "/foo" },
-    "expected": {
+    "expected_obj": {
+      "pathname": "/foo/*?"
+    },
+    "expected_match": {
       "input": { "pathname": "/foo" },
       "pathname": { "input": "/foo", "groups": { "0": "" } }
     }
@@ -443,7 +464,7 @@
   {
     "pattern": { "pathname": "/foo/*?" },
     "input": { "pathname": "/foo" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo" },
       "pathname": { "input": "/foo", "groups": { "0": "" } }
     }
@@ -451,7 +472,10 @@
   {
     "pattern": { "pathname": "/foo/(.*)?" },
     "input": { "pathname": "/foo/" },
-    "expected": {
+    "expected_obj": {
+      "pathname": "/foo/*?"
+    },
+    "expected_match": {
       "input": { "pathname": "/foo/" },
       "pathname": { "input": "/foo/", "groups": { "0": "" } }
     }
@@ -459,7 +483,7 @@
   {
     "pattern": { "pathname": "/foo/*?" },
     "input": { "pathname": "/foo/" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/" },
       "pathname": { "input": "/foo/", "groups": { "0": "" } }
     }
@@ -467,27 +491,36 @@
   {
     "pattern": { "pathname": "/foo/(.*)?" },
     "input": { "pathname": "/foobar" },
-    "expected": null
+    "expected_obj": {
+      "pathname": "/foo/*?"
+    },
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/*?" },
     "input": { "pathname": "/foobar" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/(.*)?" },
     "input": { "pathname": "/fo" },
-    "expected": null
+    "expected_obj": {
+      "pathname": "/foo/*?"
+    },
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/*?" },
     "input": { "pathname": "/fo" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/(.*)+" },
     "input": { "pathname": "/foo/bar" },
-    "expected": {
+    "expected_obj": {
+      "pathname": "/foo/*+"
+    },
+    "expected_match": {
       "input": { "pathname": "/foo/bar" },
       "pathname": { "input": "/foo/bar", "groups": { "0": "bar" } }
     }
@@ -495,7 +528,7 @@
   {
     "pattern": { "pathname": "/foo/*+" },
     "input": { "pathname": "/foo/bar" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar" },
       "pathname": { "input": "/foo/bar", "groups": { "0": "bar" } }
     }
@@ -503,7 +536,10 @@
   {
     "pattern": { "pathname": "/foo/(.*)+" },
     "input": { "pathname": "/foo/bar/baz" },
-    "expected": {
+    "expected_obj": {
+      "pathname": "/foo/*+"
+    },
+    "expected_match": {
       "input": { "pathname": "/foo/bar/baz" },
       "pathname": { "input": "/foo/bar/baz", "groups": { "0": "bar/baz" } }
     }
@@ -511,7 +547,7 @@
   {
     "pattern": { "pathname": "/foo/*+" },
     "input": { "pathname": "/foo/bar/baz" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar/baz" },
       "pathname": { "input": "/foo/bar/baz", "groups": { "0": "bar/baz" } }
     }
@@ -519,17 +555,23 @@
   {
     "pattern": { "pathname": "/foo/(.*)+" },
     "input": { "pathname": "/foo" },
-    "expected": null
+    "expected_obj": {
+      "pathname": "/foo/*+"
+    },
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/*+" },
     "input": { "pathname": "/foo" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/(.*)+" },
     "input": { "pathname": "/foo/" },
-    "expected": {
+    "expected_obj": {
+      "pathname": "/foo/*+"
+    },
+    "expected_match": {
       "input": { "pathname": "/foo/" },
       "pathname": { "input": "/foo/", "groups": { "0": "" } }
     }
@@ -537,7 +579,7 @@
   {
     "pattern": { "pathname": "/foo/*+" },
     "input": { "pathname": "/foo/" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/" },
       "pathname": { "input": "/foo/", "groups": { "0": "" } }
     }
@@ -545,27 +587,36 @@
   {
     "pattern": { "pathname": "/foo/(.*)+" },
     "input": { "pathname": "/foobar" },
-    "expected": null
+    "expected_obj": {
+      "pathname": "/foo/*+"
+    },
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/*+" },
     "input": { "pathname": "/foobar" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/(.*)+" },
     "input": { "pathname": "/fo" },
-    "expected": null
+    "expected_obj": {
+      "pathname": "/foo/*+"
+    },
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/*+" },
     "input": { "pathname": "/fo" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/(.*)*" },
     "input": { "pathname": "/foo/bar" },
-    "expected": {
+    "expected_obj": {
+      "pathname": "/foo/**"
+    },
+    "expected_match": {
       "input": { "pathname": "/foo/bar" },
       "pathname": { "input": "/foo/bar", "groups": { "0": "bar" } }
     }
@@ -573,7 +624,7 @@
   {
     "pattern": { "pathname": "/foo/**" },
     "input": { "pathname": "/foo/bar" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar" },
       "pathname": { "input": "/foo/bar", "groups": { "0": "bar" } }
     }
@@ -581,7 +632,10 @@
   {
     "pattern": { "pathname": "/foo/(.*)*" },
     "input": { "pathname": "/foo/bar/baz" },
-    "expected": {
+    "expected_obj": {
+      "pathname": "/foo/**"
+    },
+    "expected_match": {
       "input": { "pathname": "/foo/bar/baz" },
       "pathname": { "input": "/foo/bar/baz", "groups": { "0": "bar/baz" } }
     }
@@ -589,7 +643,7 @@
   {
     "pattern": { "pathname": "/foo/**" },
     "input": { "pathname": "/foo/bar/baz" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar/baz" },
       "pathname": { "input": "/foo/bar/baz", "groups": { "0": "bar/baz" } }
     }
@@ -597,7 +651,10 @@
   {
     "pattern": { "pathname": "/foo/(.*)*" },
     "input": { "pathname": "/foo" },
-    "expected": {
+    "expected_obj": {
+      "pathname": "/foo/**"
+    },
+    "expected_match": {
       "input": { "pathname": "/foo" },
       "pathname": { "input": "/foo", "groups": { "0": "" } }
     }
@@ -605,7 +662,7 @@
   {
     "pattern": { "pathname": "/foo/**" },
     "input": { "pathname": "/foo" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo" },
       "pathname": { "input": "/foo", "groups": { "0": "" } }
     }
@@ -613,7 +670,10 @@
   {
     "pattern": { "pathname": "/foo/(.*)*" },
     "input": { "pathname": "/foo/" },
-    "expected": {
+    "expected_obj": {
+      "pathname": "/foo/**"
+    },
+    "expected_match": {
       "input": { "pathname": "/foo/" },
       "pathname": { "input": "/foo/", "groups": { "0": "" } }
     }
@@ -621,7 +681,7 @@
   {
     "pattern": { "pathname": "/foo/**" },
     "input": { "pathname": "/foo/" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/" },
       "pathname": { "input": "/foo/", "groups": { "0": "" } }
     }
@@ -629,27 +689,36 @@
   {
     "pattern": { "pathname": "/foo/(.*)*" },
     "input": { "pathname": "/foobar" },
-    "expected": null
+    "expected_obj": {
+      "pathname": "/foo/**"
+    },
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/**" },
     "input": { "pathname": "/foobar" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/(.*)*" },
     "input": { "pathname": "/fo" },
-    "expected": null
+    "expected_obj": {
+      "pathname": "/foo/**"
+    },
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/**" },
     "input": { "pathname": "/fo" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo{/bar}" },
     "input": { "pathname": "/foo/bar" },
-    "expected": {
+    "expected_obj": {
+      "pathname": "/foo/bar"
+    },
+    "expected_match": {
       "input": { "pathname": "/foo/bar" },
       "pathname": { "input": "/foo/bar", "groups": {} }
     }
@@ -657,22 +726,31 @@
   {
     "pattern": { "pathname": "/foo{/bar}" },
     "input": { "pathname": "/foo/bar/baz" },
-    "expected": null
+    "expected_obj": {
+      "pathname": "/foo/bar"
+    },
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo{/bar}" },
     "input": { "pathname": "/foo" },
-    "expected": null
+    "expected_obj": {
+      "pathname": "/foo/bar"
+    },
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo{/bar}" },
     "input": { "pathname": "/foo/" },
-    "expected": null
+    "expected_obj": {
+      "pathname": "/foo/bar"
+    },
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo{/bar}?" },
     "input": { "pathname": "/foo/bar" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar" },
       "pathname": { "input": "/foo/bar", "groups": {} }
     }
@@ -680,12 +758,12 @@
   {
     "pattern": { "pathname": "/foo{/bar}?" },
     "input": { "pathname": "/foo/bar/baz" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo{/bar}?" },
     "input": { "pathname": "/foo" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo" },
       "pathname": { "input": "/foo", "groups": {} }
     }
@@ -693,12 +771,12 @@
   {
     "pattern": { "pathname": "/foo{/bar}?" },
     "input": { "pathname": "/foo/" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo{/bar}+" },
     "input": { "pathname": "/foo/bar" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar" },
       "pathname": { "input": "/foo/bar", "groups": {} }
     }
@@ -706,7 +784,7 @@
   {
     "pattern": { "pathname": "/foo{/bar}+" },
     "input": { "pathname": "/foo/bar/bar" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar/bar" },
       "pathname": { "input": "/foo/bar/bar", "groups": {} }
     }
@@ -714,22 +792,22 @@
   {
     "pattern": { "pathname": "/foo{/bar}+" },
     "input": { "pathname": "/foo/bar/baz" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo{/bar}+" },
     "input": { "pathname": "/foo" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo{/bar}+" },
     "input": { "pathname": "/foo/" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo{/bar}*" },
     "input": { "pathname": "/foo/bar" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar" },
       "pathname": { "input": "/foo/bar", "groups": {} }
     }
@@ -737,7 +815,7 @@
   {
     "pattern": { "pathname": "/foo{/bar}*" },
     "input": { "pathname": "/foo/bar/bar" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar/bar" },
       "pathname": { "input": "/foo/bar/bar", "groups": {} }
     }
@@ -745,12 +823,12 @@
   {
     "pattern": { "pathname": "/foo{/bar}*" },
     "input": { "pathname": "/foo/bar/baz" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo{/bar}*" },
     "input": { "pathname": "/foo" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo" },
       "pathname": { "input": "/foo", "groups": {} }
     }
@@ -758,7 +836,7 @@
   {
     "pattern": { "pathname": "/foo{/bar}*" },
     "input": { "pathname": "/foo/" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "protocol": "(café)" },
@@ -791,7 +869,7 @@
   {
     "pattern": { "protocol": ":café" },
     "input": { "protocol": "foo" },
-    "expected": {
+    "expected_match": {
       "input": { "protocol": "foo" },
       "protocol": { "input": "foo", "groups": { "café": "foo" } }
     }
@@ -799,7 +877,7 @@
   {
     "pattern": { "username": ":café" },
     "input": { "username": "foo" },
-    "expected": {
+    "expected_match": {
       "input": { "username": "foo" },
       "username": { "input": "foo", "groups": { "café": "foo" } }
     }
@@ -807,7 +885,7 @@
   {
     "pattern": { "password": ":café" },
     "input": { "password": "foo" },
-    "expected": {
+    "expected_match": {
       "input": { "password": "foo" },
       "password": { "input": "foo", "groups": { "café": "foo" } }
     }
@@ -815,7 +893,7 @@
   {
     "pattern": { "hostname": ":café" },
     "input": { "hostname": "foo" },
-    "expected": {
+    "expected_match": {
       "input": { "hostname": "foo" },
       "hostname": { "input": "foo", "groups": { "café": "foo" } }
     }
@@ -823,7 +901,7 @@
   {
     "pattern": { "pathname": "/:café" },
     "input": { "pathname": "/foo" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo" },
       "pathname": { "input": "/foo", "groups": { "café": "foo" } }
     }
@@ -831,7 +909,7 @@
   {
     "pattern": { "search": ":café" },
     "input": { "search": "foo" },
-    "expected": {
+    "expected_match": {
       "input": { "search": "foo" },
       "search": { "input": "foo", "groups": { "café": "foo" } }
     }
@@ -839,7 +917,7 @@
   {
     "pattern": { "hash": ":café" },
     "input": { "hash": "foo" },
-    "expected": {
+    "expected_match": {
       "input": { "hash": "foo" },
       "hash": { "input": "foo", "groups": { "café": "foo" } }
     }
@@ -847,7 +925,7 @@
   {
     "pattern": { "protocol": ":\u2118" },
     "input": { "protocol": "foo" },
-    "expected": {
+    "expected_match": {
       "input": { "protocol": "foo" },
       "protocol": { "input": "foo", "groups": { "\u2118": "foo" } }
     }
@@ -855,7 +933,7 @@
   {
     "pattern": { "username": ":\u2118" },
     "input": { "username": "foo" },
-    "expected": {
+    "expected_match": {
       "input": { "username": "foo" },
       "username": { "input": "foo", "groups": { "\u2118": "foo" } }
     }
@@ -863,7 +941,7 @@
   {
     "pattern": { "password": ":\u2118" },
     "input": { "password": "foo" },
-    "expected": {
+    "expected_match": {
       "input": { "password": "foo" },
       "password": { "input": "foo", "groups": { "\u2118": "foo" } }
     }
@@ -871,7 +949,7 @@
   {
     "pattern": { "hostname": ":\u2118" },
     "input": { "hostname": "foo" },
-    "expected": {
+    "expected_match": {
       "input": { "hostname": "foo" },
       "hostname": { "input": "foo", "groups": { "\u2118": "foo" } }
     }
@@ -879,7 +957,7 @@
   {
     "pattern": { "pathname": "/:\u2118" },
     "input": { "pathname": "/foo" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo" },
       "pathname": { "input": "/foo", "groups": { "\u2118": "foo" } }
     }
@@ -887,7 +965,7 @@
   {
     "pattern": { "search": ":\u2118" },
     "input": { "search": "foo" },
-    "expected": {
+    "expected_match": {
       "input": { "search": "foo" },
       "search": { "input": "foo", "groups": { "\u2118": "foo" } }
     }
@@ -895,7 +973,7 @@
   {
     "pattern": { "hash": ":\u2118" },
     "input": { "hash": "foo" },
-    "expected": {
+    "expected_match": {
       "input": { "hash": "foo" },
       "hash": { "input": "foo", "groups": { "\u2118": "foo" } }
     }
@@ -903,7 +981,7 @@
   {
     "pattern": { "protocol": ":\u3400" },
     "input": { "protocol": "foo" },
-    "expected": {
+    "expected_match": {
       "input": { "protocol": "foo" },
       "protocol": { "input": "foo", "groups": { "\u3400": "foo" } }
     }
@@ -911,7 +989,7 @@
   {
     "pattern": { "username": ":\u3400" },
     "input": { "username": "foo" },
-    "expected": {
+    "expected_match": {
       "input": { "username": "foo" },
       "username": { "input": "foo", "groups": { "\u3400": "foo" } }
     }
@@ -919,7 +997,7 @@
   {
     "pattern": { "password": ":\u3400" },
     "input": { "password": "foo" },
-    "expected": {
+    "expected_match": {
       "input": { "password": "foo" },
       "password": { "input": "foo", "groups": { "\u3400": "foo" } }
     }
@@ -927,7 +1005,7 @@
   {
     "pattern": { "hostname": ":\u3400" },
     "input": { "hostname": "foo" },
-    "expected": {
+    "expected_match": {
       "input": { "hostname": "foo" },
       "hostname": { "input": "foo", "groups": { "\u3400": "foo" } }
     }
@@ -935,7 +1013,7 @@
   {
     "pattern": { "pathname": "/:\u3400" },
     "input": { "pathname": "/foo" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo" },
       "pathname": { "input": "/foo", "groups": { "\u3400": "foo" } }
     }
@@ -943,7 +1021,7 @@
   {
     "pattern": { "search": ":\u3400" },
     "input": { "search": "foo" },
-    "expected": {
+    "expected_match": {
       "input": { "search": "foo" },
       "search": { "input": "foo", "groups": { "\u3400": "foo" } }
     }
@@ -951,7 +1029,7 @@
   {
     "pattern": { "hash": ":\u3400" },
     "input": { "hash": "foo" },
-    "expected": {
+    "expected_match": {
       "input": { "hash": "foo" },
       "hash": { "input": "foo", "groups": { "\u3400": "foo" } }
     }
@@ -959,12 +1037,18 @@
   {
     "pattern": { "protocol": "(.*)" },
     "input": { "protocol" : "café" },
-    "expected": null
+    "expected_obj": {
+      "protocol": "*"
+    },
+    "expected_match": null
   },
   {
     "pattern": { "protocol": "(.*)" },
     "input": { "protocol": "cafe" },
-    "expected": {
+    "expected_obj": {
+      "protocol": "*"
+    },
+    "expected_match": {
       "input": { "protocol": "cafe" },
       "protocol": { "input": "cafe", "groups": { "0": "cafe" }}
     }
@@ -972,7 +1056,7 @@
   {
     "pattern": { "protocol": "foo-bar" },
     "input": { "protocol": "foo-bar" },
-    "expected": {
+    "expected_match": {
       "input": { "protocol": "foo-bar" },
       "protocol": { "input": "foo-bar", "groups": {} }
     }
@@ -980,7 +1064,7 @@
   {
     "pattern": { "username": "caf%C3%A9" },
     "input": { "username" : "café" },
-    "expected": {
+    "expected_match": {
       "input": { "username" : "café" },
       "username": { "input": "caf%C3%A9", "groups": {}}
     }
@@ -988,7 +1072,10 @@
   {
     "pattern": { "username": "café" },
     "input": { "username" : "café" },
-    "expected": {
+    "expected_obj": {
+      "username": "caf%C3%A9"
+    },
+    "expected_match": {
       "input": { "username" : "café" },
       "username": { "input": "caf%C3%A9", "groups": {}}
     }
@@ -996,12 +1083,12 @@
   {
     "pattern": { "username": "caf%c3%a9" },
     "input": { "username" : "café" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "password": "caf%C3%A9" },
     "input": { "password" : "café" },
-    "expected": {
+    "expected_match": {
       "input": { "password" : "café" },
       "password": { "input": "caf%C3%A9", "groups": {}}
     }
@@ -1009,7 +1096,10 @@
   {
     "pattern": { "password": "café" },
     "input": { "password" : "café" },
-    "expected": {
+    "expected_obj": {
+      "password": "caf%C3%A9"
+    },
+    "expected_match": {
       "input": { "password" : "café" },
       "password": { "input": "caf%C3%A9", "groups": {}}
     }
@@ -1017,12 +1107,12 @@
   {
     "pattern": { "password": "caf%c3%a9" },
     "input": { "password" : "café" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "hostname": "xn--caf-dma.com" },
     "input": { "hostname" : "café.com" },
-    "expected": {
+    "expected_match": {
       "input": { "hostname" : "café.com" },
       "hostname": { "input": "xn--caf-dma.com", "groups": {}}
     }
@@ -1030,7 +1120,10 @@
   {
     "pattern": { "hostname": "café.com" },
     "input": { "hostname" : "café.com" },
-    "expected": {
+    "expected_obj": {
+      "hostname": "xn--caf-dma.com"
+    },
+    "expected_match": {
       "input": { "hostname" : "café.com" },
       "hostname": { "input": "xn--caf-dma.com", "groups": {}}
     }
@@ -1038,7 +1131,7 @@
   {
     "pattern": { "port": "" },
     "input": { "protocol": "http", "port": "80" },
-    "expected": {
+    "expected_match": {
       "input": { "protocol": "http", "port": "80" },
       "protocol": { "input": "http", "groups": { "0": "http" }},
       "port": { "input": "", "groups": {}}
@@ -1047,7 +1140,7 @@
   {
     "pattern": { "protocol": "http", "port": "80" },
     "input": { "protocol": "http", "port": "80" },
-    "expected": {
+    "expected_match": {
       "input": { "protocol": "http", "port": "80" },
       "protocol": { "input": "http", "groups": {}},
       "port": { "input": "", "groups": {}}
@@ -1056,7 +1149,7 @@
   {
     "pattern": { "protocol": "http", "port": "80{20}?" },
     "input": { "protocol": "http", "port": "80" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "protocol": "http", "port": "80 " },
@@ -1066,17 +1159,17 @@
   {
     "pattern": { "port": "80" },
     "input": { "protocol": "http", "port": "80" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "protocol": "http{s}?", "port": "80" },
     "input": { "protocol": "http", "port": "80" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "port": "80" },
     "input": { "port": "80" },
-    "expected": {
+    "expected_match": {
       "input": { "port": "80" },
       "port": { "input": "80", "groups": {}}
     }
@@ -1084,12 +1177,15 @@
   {
     "pattern": { "port": "(.*)" },
     "input": { "port": "invalid80" },
-    "expected": null
+    "expected_obj": {
+      "port": "*"
+    },
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/bar" },
     "input": { "pathname": "/foo/./bar" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/./bar" },
       "pathname": { "input": "/foo/bar", "groups": {}}
     }
@@ -1097,7 +1193,7 @@
   {
     "pattern": { "pathname": "/foo/baz" },
     "input": { "pathname": "/foo/bar/../baz" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/foo/bar/../baz" },
       "pathname": { "input": "/foo/baz", "groups": {}}
     }
@@ -1105,7 +1201,7 @@
   {
     "pattern": { "pathname": "/caf%C3%A9" },
     "input": { "pathname": "/café" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "/café" },
       "pathname": { "input": "/caf%C3%A9", "groups": {}}
     }
@@ -1113,7 +1209,10 @@
   {
     "pattern": { "pathname": "/café" },
     "input": { "pathname": "/café" },
-    "expected": {
+    "expected_obj": {
+      "pathname": "/caf%C3%A9"
+    },
+    "expected_match": {
       "input": { "pathname": "/café" },
       "pathname": { "input": "/caf%C3%A9", "groups": {}}
     }
@@ -1121,17 +1220,17 @@
   {
     "pattern": { "pathname": "/caf%c3%a9" },
     "input": { "pathname": "/café" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/bar" },
     "input": { "pathname": "foo/bar" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "/foo/bar" },
     "input": { "pathname": "foo/bar", "baseURL": "https://example.com" },
-    "expected": {
+    "expected_match": {
       "input": { "pathname": "foo/bar", "baseURL": "https://example.com" },
       "protocol": { "input": "https", "groups": { "0": "https" }},
       "hostname": { "input": "example.com", "groups": { "0": "example.com" }},
@@ -1141,7 +1240,10 @@
   {
     "pattern": { "pathname": "/foo/../bar" },
     "input": { "pathname": "/bar" },
-    "expected": {
+    "expected_obj": {
+      "pathname": "/bar"
+    },
+    "expected_match": {
       "input": { "pathname": "/bar" },
       "pathname": { "input": "/bar", "groups": {}}
     }
@@ -1149,7 +1251,10 @@
   {
     "pattern": { "pathname": "./foo/bar", "baseURL": "https://example.com" },
     "input": { "pathname": "foo/bar", "baseURL": "https://example.com" },
-    "expected": {
+    "expected_obj": {
+      "pathname": "/foo/bar"
+    },
+    "expected_match": {
       "input": { "pathname": "foo/bar", "baseURL": "https://example.com" },
       "exactly_empty_components": [ "username", "password", "port" ],
       "protocol": { "input": "https", "groups": {}},
@@ -1160,12 +1265,15 @@
   {
     "pattern": { "pathname": "foo/bar" },
     "input": "https://example.com/foo/bar",
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "pathname": "foo/bar", "baseURL": "https://example.com" },
     "input": "https://example.com/foo/bar",
-    "expected": {
+    "expected_obj": {
+      "pathname": "/foo/bar"
+    },
+    "expected_match": {
       "input": "https://example.com/foo/bar",
       "exactly_empty_components": [ "username", "password", "port" ],
       "protocol": { "input": "https", "groups": {}},
@@ -1176,7 +1284,10 @@
   {
     "pattern": { "pathname": ":name.html", "baseURL": "https://example.com" },
     "input": "https://example.com/foo.html",
-    "expected": {
+    "expected_obj": {
+      "pathname": "/:name.html"
+    },
+    "expected_match": {
       "input": "https://example.com/foo.html",
       "exactly_empty_components": [ "username", "password", "port" ],
       "protocol": { "input": "https", "groups": {}},
@@ -1187,7 +1298,7 @@
   {
     "pattern": { "search": "q=caf%C3%A9" },
     "input": { "search": "q=café" },
-    "expected": {
+    "expected_match": {
       "input": { "search": "q=café" },
       "search": { "input": "q=caf%C3%A9", "groups": {}}
     }
@@ -1195,7 +1306,10 @@
   {
     "pattern": { "search": "q=café" },
     "input": { "search": "q=café" },
-    "expected": {
+    "expected_obj": {
+      "search": "q=caf%C3%A9"
+    },
+    "expected_match": {
       "input": { "search": "q=café" },
       "search": { "input": "q=caf%C3%A9", "groups": {}}
     }
@@ -1203,12 +1317,12 @@
   {
     "pattern": { "search": "q=caf%c3%a9" },
     "input": { "search": "q=café" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "hash": "caf%C3%A9" },
     "input": { "hash": "café" },
-    "expected": {
+    "expected_match": {
       "input": { "hash": "café" },
       "hash": { "input": "caf%C3%A9", "groups": {}}
     }
@@ -1216,7 +1330,10 @@
   {
     "pattern": { "hash": "café" },
     "input": { "hash": "café" },
-    "expected": {
+    "expected_obj": {
+      "hash": "caf%C3%A9"
+    },
+    "expected_match": {
       "input": { "hash": "café" },
       "hash": { "input": "caf%C3%A9", "groups": {}}
     }
@@ -1224,12 +1341,12 @@
   {
     "pattern": { "hash": "caf%c3%a9" },
     "input": { "hash": "café" },
-    "expected": null
+    "expected_match": null
   },
   {
     "pattern": { "protocol": "about", "pathname": "(blank|sourcedoc)" },
     "input": "about:blank",
-    "expected": {
+    "expected_match": {
       "input": "about:blank",
       "protocol": { "input": "about", "groups": {}},
       "pathname": { "input": "blank", "groups": { "0": "blank" }}
@@ -1238,7 +1355,7 @@
   {
     "pattern": { "protocol": "data", "pathname": ":number([0-9]+)" },
     "input": "data:8675309",
-    "expected": {
+    "expected_match": {
       "input": "data:8675309",
       "protocol": { "input": "data", "groups": {}},
       "pathname": { "input": "8675309", "groups": { "number": "8675309" }}
diff --git a/urlpattern/urlpattern.https.any.js b/urlpattern/urlpattern.https.any.js
index 3d47f57..183e401 100644
--- a/urlpattern/urlpattern.https.any.js
+++ b/urlpattern/urlpattern.https.any.js
@@ -1,5 +1,16 @@
 // META: global=window,worker
 
+const kComponents = [
+  'protocol',
+  'username',
+  'password',
+  'hostname',
+  'password',
+  'pathname',
+  'search',
+  'hash',
+];
+
 function runTests(data) {
   for (let entry of data) {
     test(function() {
@@ -11,45 +22,86 @@
 
       const pattern = new URLPattern(entry.pattern);
 
+      // If the expected_obj property is not present we will automatically
+      // fill it with the most likely expected values.
+      entry.expected_obj = entry.expected_obj || {};
+
+      // The compiled URLPattern object should have a property for each
+      // component exposing the compiled pattern string.
+      for (let component of kComponents) {
+        // If the test case explicitly provides an expected pattern string,
+        // then use that.  This is necessary in cases where the original
+        // construction pattern gets canonicalized, etc.
+        let expected = entry.expected_obj[component];
+
+        // If there is no explicit expected pattern string, then compute
+        // the expected value based on the URLPattern constructor args.
+        if (!expected) {
+          // First determine if there is a baseURL present in the pattern
+          // input.  A baseURL can be the source for many component patterns.
+          let baseURL = null;
+          if (entry.pattern.baseURL)
+            baseURL = new URL(entry.pattern.baseURL);
+
+          // We automatically populate the expected pattern string using
+          // the following options in priority order:
+          //
+          //  1. If the original input explicitly provided a pattern, then
+          //     echo that back as the expected value.
+          //  2. If the baseURL exists and provides a component value then
+          //     use that for the expected pattern.  Note, the baseURL
+          //     does not provide search/hash component values.
+          //  3. Otherwise fall back on the default pattern of `*` for an
+          //     empty component pattern.
+          if (entry.pattern[component]) {
+            expected = entry.pattern[component];
+          } else if (baseURL &&
+                     component !== 'search' && component !== 'hash') {
+            let base_value = baseURL[component];
+            // Unfortunately the URL() protocol getter includes a trailing `:`
+            // that is not used by URLPattern.  Strip that off in necessary.
+            if (component === 'protocol')
+              base_value = base_value.substring(0, base_value.length - 1);
+            expected = base_value;
+          } else {
+            expected = '*';
+          }
+        }
+
+        // Finally, assert that the compiled object property matches the
+        // expected property.
+        assert_equals(pattern[component], expected,
+                      `compiled pattern property '${component}'`);
+      }
+
       // First, validate the test() method by converting the expected result to
       // a truthy value.
-      assert_equals(pattern.test(entry.input), !!entry.expected,
+      assert_equals(pattern.test(entry.input), !!entry.expected_match,
                     'test() result');
 
       // Next, start validating the exec() method.
       const result = pattern.exec(entry.input);
 
       // On a failed match exec() returns null.
-      if (!entry.expected) {
-        assert_equals(result, entry.expected, 'exec() failed match result');
+      if (!entry.expected_match) {
+        assert_equals(result, entry.expected_match, 'exec() failed match result');
         return;
       }
 
       // Next verify the result.input is correct.  This may be a structured
       // URLPatternInit dictionary object or a URL string.
-      if (typeof entry.expected.input === 'object') {
-        assert_object_equals(result.input, entry.expected.input,
+      if (typeof entry.expected_match.input === 'object') {
+        assert_object_equals(result.input, entry.expected_match.input,
                              'exec() result.input');
       } else {
-        assert_equals(result.input, entry.expected.input,
+        assert_equals(result.input, entry.expected_match.input,
                       'exec() result.input');
       }
 
       // Next we will compare the URLPatternComponentResult for each of these
       // expected components.
-      const component_list = [
-        'protocol',
-        'username',
-        'password',
-        'hostname',
-        'password',
-        'pathname',
-        'search',
-        'hash',
-      ];
-
-      for (let component of component_list) {
-        let expected_obj = entry.expected[component];
+      for (let component of kComponents) {
+        let expected_obj = entry.expected_match[component];
 
         // If the test expectations don't include a component object, then
         // we auto-generate one.  This is convenient for the many cases
@@ -63,8 +115,8 @@
           // string pattern does not.  The expectation object must list which
           // components should be empty instead of wildcards in
           // |exactly_empty_components|.
-          if (!entry.expected.exactly_empty_components ||
-              !entry.expected.exactly_empty_components.includes(component)) {
+          if (!entry.expected_match.exactly_empty_components ||
+              !entry.expected_match.exactly_empty_components.includes(component)) {
             expected_obj.groups['0'] = '';
           }
         }