[Web Platform Test] Just-in-time install for change-payment-method

Before this patch, the change-payment-method-manual.https.html Web
Platform Test (WPT) was installing a payment handler using the
PaymentInstruments API, which is planned to be removed.

This patch adds a payment method manifest and a web app manifest (in the
same file) for the payment handler under test, which allows it to be
installed just-in-time (JIT) when PaymentRequest.show() is called.

After this patch, the change-payment-method-manual.https.html WPT no
longer uses the PaymentInstruments API for manual payment handler
installation.

Bug: 1327265
Change-Id: Ie399d77a943167aa5a4ffaba034f3b6a00a068c2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4166042
Reviewed-by: Stephen McGruer <smcgruer@chromium.org>
Commit-Queue: Rouslan Solomakhin <rouslan@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1093161}
diff --git a/payment-handler/change-payment-method-manual-manifest.json b/payment-handler/change-payment-method-manual-manifest.json
new file mode 100644
index 0000000..8f33def
--- /dev/null
+++ b/payment-handler/change-payment-method-manual-manifest.json
@@ -0,0 +1,15 @@
+{
+  "default_applications": ["change-payment-method-manual-manifest.json"],
+  "name": "Test Payment Handler",
+  "icons": [
+    {
+      "src": "/images/rgrg-256x256.png",
+      "sizes": "256x256",
+      "type": "image/png"
+    }
+  ],
+  "serviceworker": {
+    "src": "app-change-payment-method.js",
+    "scope": "payment-app/"
+  }
+}
diff --git a/payment-handler/change-payment-method-manual.https.html b/payment-handler/change-payment-method-manual.https.html
index 1640420..15165b9 100644
--- a/payment-handler/change-payment-method-manual.https.html
+++ b/payment-handler/change-payment-method-manual.https.html
@@ -5,166 +5,152 @@
   rel="help"
   href="https://w3c.github.io/payment-handler/#changepaymentmethod-method"
 />
-<link rel="manifest" href="/payment-handler/manifest.json" />
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/resources/testdriver.js"></script>
 <script src="/resources/testdriver-vendor.js"></script>
-<script src="register-and-activate-service-worker.js"></script>
 <p>If the payment sheet is shown, please authorize the mock payment.</p>
 <script>
-  async function runTests(registration) {
-    const methodName = window.location.origin + '/payment-handler/payment-app/';
-    await registration.paymentManager.instruments.clear();
-    await registration.paymentManager.instruments.set('instrument-key', {
-      name: 'Instrument Name',
-      method: methodName,
-    });
+const methodName = window.location.origin
+    + '/payment-handler/change-payment-method-manual-manifest.json';
 
-    promise_test(async (t) => {
-      const request = new PaymentRequest([{supportedMethods: methodName}], {
-        total: {label: 'Total', amount: {currency: 'USD', value: '0.01'}},
-      });
-      // Intentionally do not respond to the 'paymentmethodchange' event.
-      const response = await test_driver.bless('showing a payment sheet', () =>
-        request.show()
-      );
-      const complete_promise = response.complete('success');
-
-      assert_equals(response.details.changePaymentMethodReturned, null);
-
-      return complete_promise;
-    }, 'If updateWith(details) is not run, changePaymentMethod() returns null.');
-
-    promise_test(async (t) => {
-      const request = new PaymentRequest([{supportedMethods: methodName}], {
-        total: {label: 'Total', amount: {currency: 'USD', value: '0.01'}},
-      });
-      request.addEventListener('paymentmethodchange', (event) => {
-        assert_equals(event.methodName, methodName);
-        assert_equals(event.methodDetails.country, 'US');
-        event.updateWith(Promise.reject('Error'));
-      });
-      const response_promise = test_driver.bless(
-        'showing a payment sheet',
-        () => request.show()
-      );
-
-      return promise_rejects_dom(t, 'AbortError', response_promise);
-    }, 'If updateWith(details) is rejected, abort the PaymentRequest.show().');
-
-    promise_test(async (t) => {
-      const request = new PaymentRequest([{supportedMethods: methodName}], {
-        total: {label: 'Total', amount: {currency: 'USD', value: '0.01'}},
-      });
-      request.addEventListener('paymentmethodchange', (event) => {
-        assert_equals(event.methodName, methodName);
-        assert_equals(event.methodDetails.country, 'US');
-        event.updateWith(
-          new Promise(() => {
-            throw 'Error for test';
-          })
-        );
-      });
-      const response_promise = test_driver.bless(
-        'showing a payment sheet',
-        () => request.show()
-      );
-
-      return promise_rejects_dom(t, 'AbortError', response_promise);
-    }, 'If updateWith(details) throws inside of the promise, abort the PaymentRequest.show().');
-
-    promise_test(async (t) => {
-      const request = new PaymentRequest([{supportedMethods: methodName}], {
-        total: {label: 'Total', amount: {currency: 'USD', value: '0.01'}},
-      });
-      request.addEventListener('paymentmethodchange', (event) => {
-        assert_equals(event.methodName, methodName);
-        assert_equals(event.methodDetails.country, 'US');
-        event.updateWith({
-          total: {label: 'Total', amount: {currency: 'GBP', value: '0.02'}},
-          error: 'Error for test',
-          modifiers: [
-            {
-              supportedMethods: methodName,
-              data: {soup: 'potato'},
-              total: {
-                label: 'Modified total',
-                amount: {currency: 'EUR', value: '0.03'},
-              },
-              additionalDisplayItems: [
-                {
-                  label: 'Modified display item',
-                  amount: {currency: 'INR', value: '0.06'},
-                },
-              ],
-            },
-            {
-              supportedMethods: methodName + '2',
-              data: {soup: 'tomato'},
-              total: {
-                label: 'Modified total #2',
-                amount: {currency: 'CHF', value: '0.07'},
-              },
-              additionalDisplayItems: [
-                {
-                  label: 'Modified display item #2',
-                  amount: {currency: 'CAD', value: '0.08'},
-                },
-              ],
-            },
-          ],
-          paymentMethodErrors: {country: 'Unsupported country'},
-          displayItems: [
-            {
-              label: 'Display item',
-              amount: {currency: 'CNY', value: '0.04'},
-            },
-          ],
-          shippingOptions: [
-            {
-              label: 'Shipping option',
-              id: 'id',
-              amount: {currency: 'JPY', value: '0.05'},
-            },
-          ],
-        });
-      });
-      const response = await test_driver.bless('showing a payment sheet', () =>
-        request.show()
-      );
-      const complete_promise = response.complete('success');
-      const changePaymentMethodReturned =
-        response.details.changePaymentMethodReturned;
-
-      assert_equals(changePaymentMethodReturned.total.currency, 'GBP');
-      assert_equals(changePaymentMethodReturned.total.value, '0.02');
-      assert_equals(changePaymentMethodReturned.total.label, undefined);
-      assert_equals(changePaymentMethodReturned.error, 'Error for test');
-      assert_equals(changePaymentMethodReturned.modifiers.length, 1);
-      assert_equals(changePaymentMethodReturned.displayItems, undefined);
-      assert_equals(changePaymentMethodReturned.shippingOptions, undefined);
-      assert_equals(
-        changePaymentMethodReturned.paymentMethodErrors.country,
-        'Unsupported country'
-      );
-
-      const modifier = changePaymentMethodReturned.modifiers[0];
-
-      assert_equals(modifier.supportedMethods, methodName);
-      assert_equals(modifier.data.soup, 'potato');
-      assert_equals(modifier.total.label, '');
-      assert_equals(modifier.total.amount.currency, 'EUR');
-      assert_equals(modifier.total.amount.value, '0.03');
-      assert_equals(modifier.additionalDisplayItems, undefined);
-
-      return complete_promise;
-    }, 'The changePaymentMethod() returns some details from the "paymentmethodchange" event\'s updateWith(details) call.');
-  }
-
-  registerAndActiveServiceWorker(
-    'app-change-payment-method.js',
-    'payment-app/',
-    runTests
+promise_test(async (t) => {
+  const request = new PaymentRequest([{supportedMethods: methodName}], {
+    total: {label: 'Total', amount: {currency: 'USD', value: '0.01'}},
+  });
+  // Intentionally do not respond to the 'paymentmethodchange' event.
+  const response = await test_driver.bless('showing a payment sheet', () =>
+    request.show()
   );
+  const complete_promise = response.complete('success');
+
+  assert_equals(response.details.changePaymentMethodReturned, null);
+
+  return complete_promise;
+}, 'If updateWith(details) is not run, changePaymentMethod() returns null.');
+
+promise_test(async (t) => {
+  const request = new PaymentRequest([{supportedMethods: methodName}], {
+    total: {label: 'Total', amount: {currency: 'USD', value: '0.01'}},
+  });
+  request.addEventListener('paymentmethodchange', (event) => {
+    assert_equals(event.methodName, methodName);
+    assert_equals(event.methodDetails.country, 'US');
+    event.updateWith(Promise.reject('Error'));
+  });
+  const response_promise = test_driver.bless(
+    'showing a payment sheet',
+    () => request.show()
+  );
+
+  return promise_rejects_dom(t, 'AbortError', response_promise);
+}, 'If updateWith(details) is rejected, abort the PaymentRequest.show().');
+
+promise_test(async (t) => {
+  const request = new PaymentRequest([{supportedMethods: methodName}], {
+    total: {label: 'Total', amount: {currency: 'USD', value: '0.01'}},
+  });
+  request.addEventListener('paymentmethodchange', (event) => {
+    assert_equals(event.methodName, methodName);
+    assert_equals(event.methodDetails.country, 'US');
+    event.updateWith(
+      new Promise(() => {
+        throw 'Error for test';
+      })
+    );
+  });
+  const response_promise = test_driver.bless(
+    'showing a payment sheet',
+    () => request.show()
+  );
+
+  return promise_rejects_dom(t, 'AbortError', response_promise);
+}, 'If updateWith(details) throws inside of the promise, abort the PaymentRequest.show().');
+
+promise_test(async (t) => {
+  const request = new PaymentRequest([{supportedMethods: methodName}], {
+    total: {label: 'Total', amount: {currency: 'USD', value: '0.01'}},
+  });
+  request.addEventListener('paymentmethodchange', (event) => {
+    assert_equals(event.methodName, methodName);
+    assert_equals(event.methodDetails.country, 'US');
+    event.updateWith({
+      total: {label: 'Total', amount: {currency: 'GBP', value: '0.02'}},
+      error: 'Error for test',
+      modifiers: [
+        {
+          supportedMethods: methodName,
+          data: {soup: 'potato'},
+          total: {
+            label: 'Modified total',
+            amount: {currency: 'EUR', value: '0.03'},
+          },
+          additionalDisplayItems: [
+            {
+              label: 'Modified display item',
+              amount: {currency: 'INR', value: '0.06'},
+            },
+          ],
+        },
+        {
+          supportedMethods: methodName + '2',
+          data: {soup: 'tomato'},
+          total: {
+            label: 'Modified total #2',
+            amount: {currency: 'CHF', value: '0.07'},
+          },
+          additionalDisplayItems: [
+            {
+              label: 'Modified display item #2',
+              amount: {currency: 'CAD', value: '0.08'},
+            },
+          ],
+        },
+      ],
+      paymentMethodErrors: {country: 'Unsupported country'},
+      displayItems: [
+        {
+          label: 'Display item',
+          amount: {currency: 'CNY', value: '0.04'},
+        },
+      ],
+      shippingOptions: [
+        {
+          label: 'Shipping option',
+          id: 'id',
+          amount: {currency: 'JPY', value: '0.05'},
+        },
+      ],
+    });
+  });
+  const response = await test_driver.bless('showing a payment sheet', () =>
+    request.show()
+  );
+  const complete_promise = response.complete('success');
+  const changePaymentMethodReturned =
+    response.details.changePaymentMethodReturned;
+
+  assert_equals(changePaymentMethodReturned.total.currency, 'GBP');
+  assert_equals(changePaymentMethodReturned.total.value, '0.02');
+  assert_equals(changePaymentMethodReturned.total.label, undefined);
+  assert_equals(changePaymentMethodReturned.error, 'Error for test');
+  assert_equals(changePaymentMethodReturned.modifiers.length, 1);
+  assert_equals(changePaymentMethodReturned.displayItems, undefined);
+  assert_equals(changePaymentMethodReturned.shippingOptions, undefined);
+  assert_equals(
+    changePaymentMethodReturned.paymentMethodErrors.country,
+    'Unsupported country'
+  );
+
+  const modifier = changePaymentMethodReturned.modifiers[0];
+
+  assert_equals(modifier.supportedMethods, methodName);
+  assert_equals(modifier.data.soup, 'potato');
+  assert_equals(modifier.total.label, '');
+  assert_equals(modifier.total.amount.currency, 'EUR');
+  assert_equals(modifier.total.amount.value, '0.03');
+  assert_equals(modifier.additionalDisplayItems, undefined);
+
+  return complete_promise;
+}, 'The changePaymentMethod() returns some details from the "paymentmethodchange" event\'s updateWith(details) call.');
 </script>