Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert "Remove shipping related things from payment-request (#28830)" #44409

Merged
merged 3 commits into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@

function checkRedaction(billingAddress) {
assert_true(
billingAddress instanceof PaymentAddress,
"Expected instance of PaymentAddress"
billingAddress instanceof ContactAddress,
"Expected instance of ContactAddress"
);
for (const item of ["organization", "phone", "recipient"]) {
assert_equals(
Expand Down Expand Up @@ -134,7 +134,7 @@ <h2>Request billing address</h2>
<button onclick="requestBillingAddress()">
When billing address is
requested,`PaymentMethodChangeEvent.methodData.billingAddress` is a
`PaymentAddress`.
`ContactAddress`.
</button>
</li>
<li><button onclick="done()">Done!</button></li>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<!doctype html>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this directory be renamed to ContactAddress?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch!

<meta charset="utf8">
<link rel="help" href="https://www.w3.org/TR/payment-request/#ContactAddress-interface">
<title>
PaymentResponse.prototype.shippingAddress
</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../payment-response/helpers.js"></script>
<script>
const options = { requestShipping: true };
function runManualTest(button, expected = {}) {
button.disabled = true;
promise_test(async () => {
const { response } = await getPaymentRequestResponse(options);
await response.complete();
assert_idl_attribute(response, "shippingAddress");
const { shippingAddress: addr } = response;
assert_true(
addr instanceof ContactAddress,
"Expect instance of ContactAddress"
);
// An [ISO3166] alpha-2 code. The canonical form is upper case.
const { country } = addr;
assert_equals(country.length, 2, "Expected length is 2");
assert_true(/^[A-Z]{2}$/.test(country), "Canonical form is upper case");
assert_true(
addr.addressLine instanceof Array,
"Expected addressLine to be an array"
);
assert_throws_js(
TypeError,
() => {
addr.addressLine.push("this must throw");
},
"Array must be frozen"
);
for (let [attr, expectedValue] of Object.entries(expected)) {
assert_idl_attribute(addr, attr);
const msg = `Expected ContactAddress.${attr} to equal ${expectedValue}.`;
//.toString() flattens array addressLine,
//.toLowerCase() because case can't be enforced for some attributes
const actualValue = addr[attr].toString().toLowerCase();
expectedValue = expectedValue.toString().toLowerCase();
assert_equals(actualValue, expectedValue, msg);
}
// Check toJSON result
for (let [prop, jsonValue] of Object.entries(addr.toJSON())) {
const actualValue = jsonValue.toString().toLowerCase();
const expectedValue = expected[prop].toString().toLowerCase();
const msg = `Expected JSON ${prop} to be ${expectedValue}`;
assert_equals(actualValue, expectedValue, msg);
}
}, button.textContent.trim());
done();
}
</script>
<h2>ContactAddress interface</h2>
<p>
Click on each button in sequence from top to bottom without refreshing the page.
Each button will bring up the Payment Request UI window.
</p>
<p>
When prompted, please enter addresses as follows...
</p>
<ol>
<li>
<button onclick="
const expectedAddress = {
country: 'AU',
regionCode: 'QLD',
addressLine: '55 test st',
city: 'Chapel Hill',
dependentLocality: '',
postalCode: '6095',
region: 'QLD',
sortingCode: '',
organization: 'w3c',
recipient: 'web platform test',
phone: '+61733780000',
};
runManualTest(this, expectedAddress);">
If the requestShipping member is true, then shippingAddress's ContactAddress must match the expected values.
</button>
Please use:
<dl>
<dt>Recipient:</dt>
<dd>web platform test</dd>
<dt>Address line:</dt>
<dd>55 test st</dd>
<dt>Country</dt>
<dd>Australia</dd>
<dt>City</dt>
<dd>Chapel Hill</dd>
<dd>State/Region</dd>
<dd>Queensland</dd>
<dt>postal code </dt>
<dd>6095</dd>
<dt>organization</dt>
<dd>w3c</dd>
<dt>Phone number</dt>
<dd>+61 7 3378 0000</dd>
</dl>
</li>
</ol>
<small>
If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>.
</small>
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
<!doctype html>
<meta charset="utf8">
<link rel="help" href="https://www.w3.org/TR/payment-request/#updatewith()-method">
<link rel="help" href="https://github.com/w3c/payment-request/pull/591">
<title>
PaymentRequestUpdateEvent.updateWith() needs to be called immediately
</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
setup({ explicit_done: true, explicit_timeout: true });
const applePay = Object.freeze({
rsolomakhin marked this conversation as resolved.
Show resolved Hide resolved
supportedMethods: "https://apple.com/apple-pay",
data: {
version: 3,
merchantIdentifier: "merchant.com.example",
countryCode: "US",
merchantCapabilities: ["supports3DS"],
supportedNetworks: ["visa"],
}
});
const validMethod = Object.freeze({ supportedMethods: "basic-card" });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if it makes a difference, but Chrome has deprecated "basic-card". We only support payment handlers now, such as "https://google.com/pay".

We also deprecated manual install of payment handlers. Only just-in-time install is supported.

What that means for our tests is that we setup a JIT-installable payment handler at some local URL and then point our tests to it as the supportedMethods.

I think it's OK to re-land these tests as-is, but let's keep in mind that validMethod may need to change before it works in Chrome correctly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, let's add https://google.com/pay. Our team can follow up later to pass the correct method-specific-data to make Google Pay work in tests, too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that would be great!

const validMethods = Object.freeze([validMethod, applePay]);
const validAmount = Object.freeze({ currency: "USD", value: "5.00" });
const validTotal = Object.freeze({
label: "label",
amount: validAmount,
});
const validShippingOptionA = Object.freeze({
id: "a-shipping-option",
label: "A shipping option",
amount: validAmount,
selected: true,
});
const validShippingOptionB = Object.freeze({
id: "b-shipping-option",
label: "B shipping option",
amount: validAmount,
});
const validDetails = Object.freeze({
total: validTotal,
shippingOptions: [validShippingOptionA, validShippingOptionB],
});
const validOptions = Object.freeze({
requestShipping: true,
});

function testImmediateUpdate({ textContent: testName }) {
promise_test(async t => {
const request = new PaymentRequest(
validMethods,
validDetails,
validOptions
);
const eventPromise = new Promise((resolve, reject) => {
request.addEventListener(
"shippingaddresschange",
ev => {
// Forces updateWith() to be run in the next event loop tick so that
// [[waitForUpdate]] is already true when it runs.
t.step_timeout(() => {
try {
ev.updateWith(validDetails);
resolve(); // This is bad.
} catch (err) {
reject(err); // this is good.
}
});
},
{ once: true }
);
});
const acceptPromise = request.show();
await promise_rejects_dom(
t,
"InvalidStateError",
eventPromise,
"The event loop already spun, so [[waitForUpdate]] is now true"
);
const response = await acceptPromise;
await response.complete();
}, testName.trim());
}

function testSubsequentUpdateWithCalls({ textContent: testName }) {
promise_test(async t => {
const request = new PaymentRequest(
validMethods,
validDetails,
validOptions
);
const eventPromise = new Promise((resolve, reject) => {
request.addEventListener("shippingaddresschange", async ev => {
const p = Promise.resolve(validDetails);
ev.updateWith(p);
await p;
try {
ev.updateWith(validDetails);
resolve(); // this is bad, we should never get to here.
} catch (err) {
reject(err); // this is good!
}
});
});
const responsePromise = request.show();
await promise_rejects_dom(
t,
"InvalidStateError",
eventPromise,
"Expected eventPromise to have rejected, because updateWith() was a called twice"
);
const response = await responsePromise;
await response.complete();
}, testName.trim());
}

function testRecycleEvents({ textContent: testName }) {
promise_test(async t => {
const request = new PaymentRequest(
validMethods,
validDetails,
validOptions
);

// Register both listeners.
const addressChangedPromise = new Promise(resolve => {
request.addEventListener("shippingaddresschange", resolve, {
once: true,
});
});

const optionChangedPromise = new Promise(resolve => {
request.addEventListener("shippingoptionchange", resolve, {
once: true,
});
});

const responsePromise = request.show();

// Let's wait for the address to change.
const addressChangeEvent = await addressChangedPromise;

// Sets [[waitingForUpdate]] to true.
addressChangeEvent.updateWith(validDetails);

// Let's wait for the shippingOption.
const optionChangeEvent = await optionChangedPromise;

// Here, we try to be sneaky, and reuse the addressChangeEvent to perform the update.
// However, addressChangeEvent [[waitingForUpdate]] is true, so it throws.
assert_throws_dom(
"InvalidStateError",
() => {
addressChangeEvent.updateWith(validDetails);
},
"addressChangeEvent [[waitingForUpdate]] is true, so it must throw"
);

// But optionChangeEvent is still usable tho, so...
optionChangeEvent.updateWith(validDetails);

assert_throws_dom(
"InvalidStateError",
() => {
optionChangeEvent.updateWith(validDetails);
},
"optionChangeEvent [[waitingForUpdate]] is true, so it must throw"
);

const response = await responsePromise;
await response.complete();
}, testName.trim());
}
</script>
<h2>updateWith() method</h2>
<p>
Click on each button in sequence from top to bottom without refreshing the page.
Each button will bring up the Payment Request UI window.
</p>
<p>
When the payment sheet is shown, select a different shipping address once. Then pay.
</p>
<ol>
<li id="test-0">
<button onclick="testImmediateUpdate(this);">
updateWith() must be called immediately, otherwise must throw an InvalidStateError.
</button>
</li>
<li id="test-1">
<button onclick="testSubsequentUpdateWithCalls(this);">
Once the event has performed an update, subsequent calls to updateWith() must throw InvalidStateError.
</button>
</li>
<li id="test-2">
<button onclick="testRecycleEvents(this);">
Recycling events must not be possible.
</button> When the payment sheet is shown, select a different shipping address once, then change shipping option once. Then pay.
</li>
<li>
<button onclick="done();">Done!</button>
</li>
</ol>
<small>
If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>.
</small>
Loading