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

Add test for wheel event groups #37445

Merged

Conversation

dlrobertson
Copy link
Member

Add tests for groups of wheel events.

@dlrobertson
Copy link
Member Author

Test Blink WebKit Gecko
Wheel events are grouped and are not sent to a child element
Wheel events are not bound to a scroll frame
Wheel event groups beyond the origin bound have the same target

As stated in w3c/uievents#338 the WebKit failure in my local tests is due to a webdriver exception, but follows the expected behavior with manual tests.

.scroll(40, 2, 0, 20, {origin: firstSpacer})
.scroll(40, 2, 0, 100, {origin: firstSpacer})
.pause(1)
.scroll(40, 2, 0, 50, {origin: firstSpacer})
Copy link
Contributor

Choose a reason for hiding this comment

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

In my understanding, firstSpacer is now scrolled out after the previous .scroll call. Does this call emulate user input in any browsers?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, this emulates what happens for several small wheel scrolls that start on firstSpacer that happen in quick succession. I'll add a test that doesn't add an origin for additional coverage.

Copy link
Member Author

Choose a reason for hiding this comment

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

Added a test that does what this test did without an element as an origin. It intermittently fails on WebKit, but the behavior is always the same for a manual test.

Comment on lines 136 to 206
assert_equals(second_spacer_wheel_event, false, "Wheel event should not be fired to the second spacer");
assert_equals(inner_wheel_event, false, "Wheel event should not be fired to inner div");
Copy link
Contributor

Choose a reason for hiding this comment

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

At least under current UI Events spec, one of these should be true if the last .scroll call emulates a wheel operation on secondSpacer or innerDiv because both of them are in same scrollable element.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is also my understanding of the current spec. This is what I was trying to describe in w3c/uievents#338, but I'm not sure if I described it well. For both manual and emulated tests, the current spec behavior does not seem to happen on WebKit or Blink.

Note that a event sent to secondSpacer when the scroll moves beyond the bounds of firstSpacer would cause user scrolls to be interrupted in the case that secondSpacer has a event listener that uses preventDefault. We get a steady stream of bug reports for this in the Gecko APZ component. So maybe it makes the most sense to fire the wheel event at the parent that contains both elements instead of firing it at the initial target and relying on the event bubbling up.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry for the delay to reply, but I've still not reached the conclusion yet.

Could you rewrite these tests to collect Event.target of wheel events with window.addEventListener into an array and compare each array item with what you expect? Then, what's expected here becomes easier to understand. I.e.,

let wheelEventTargets = [];
window.addEventListener("wheel", event => {
  wheelEventTargets.push(event.target);
}, {passive: true});
...
function runTest() {
  wheelEventTargets = [];
  ...
  assert_equal(
    document.getElementById("..."),
    wheelEventTargets[0],
    "..."
  );
  assert_equal(
    document.getElementById("..."),
    wheelEventTargets[1],
    "..."
  );
}

(I guess and assume that the number of wheel events is not different between browsers in what this test does.)

Copy link
Member Author

Choose a reason for hiding this comment

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

Sorry for the delay to reply, but I've still not reached the conclusion yet.

No problem! This is a bit of a complex topic that (I think) doesn't currently follow the spec.

Could you rewrite these tests to collect Event.target of wheel events with window.addEventListener into an array and compare each array item with what you expect?

Thanks for the feedback! Yeah, that makes sense... I think I can rework the test to work like this. Some of the way the tests are architected is an artifact of me trying to figure out how non-firefox browsers handle wheel event group targetting.

Copy link
Member Author

@dlrobertson dlrobertson left a comment

Choose a reason for hiding this comment

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

Thanks for the review! Hopefully this version makes it easier to reason about... if it doesn't, let me know and I can think about altering things to make it more readable.

I can also create a example for manual testing if it would help.

testInfo.scrollingElement.scrollTo({top: 0, left: 0, behavior: "instant"});

wheelEventTargetsEqual(testInfo.targetElement,
"All wheel events in the group have the same element");
Copy link
Member Author

Choose a reason for hiding this comment

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

Could you rewrite these tests to collect Event.target of wheel events with window.addEventListener into an array and compare each array item with what you expect?

Went about doing this in a slightly different manner. All the wheel event targets for a given test should be in the same wheel event group, so they all should be the same. As a result, we don't need to check wheelEventTargets[0] == expectedTarget, we can just iterate through all the wheel event targets we collected in the testcase and ensure they're the expected target.

targetElement: secondInnerSpacer,
title: 'Wheel event groups beyond the origin bound have the same target',
},
];
Copy link
Member Author

Choose a reason for hiding this comment

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

Used a array of test info instead of separate test definitions to make it easier to reason about the test cases.

@dlrobertson dlrobertson force-pushed the wheel-event-groups branch 2 times, most recently from 9bca6f9 to ce7b93f Compare January 18, 2023 02:20
Copy link
Member Author

@dlrobertson dlrobertson left a comment

Choose a reason for hiding this comment

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

I also wanted to add a test for a moved element, but that turns out to be a bit more complicated and the tests get super flaky, so I'm not really sure what the correct behavior is.

"All wheel events in the group have the same target");

document.removeEventListener("wheel", onEventRemoveInitialTarget, {passive: true});
}, "Remove the initial wheel event target.");
Copy link
Member Author

Choose a reason for hiding this comment

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

Added a simple test that removes the initial target on the first wheel event. Note that I assumed chromes behavior for this test because it was easier to test. I'm unsure if this is the right behavior.

@dlrobertson
Copy link
Member Author

I also wanted to add a test for a moved element, but that turns out to be a bit more complicated and the tests get super flaky, so I'm not really sure what the correct behavior is.

Reorganized the tests and rewrote the removal/move initial target tests to greatly improve their reliability. All tests pass very reliably for chrome and firefox (with the bug 1168182 patch) for me on the main box I'm using to work on this. I'll try to run the tests on WebKit and maybe other OSes over the next few days.

@dlrobertson
Copy link
Member Author

dlrobertson commented Jan 27, 2023

Added dom/events/scrolling/wheel-event-groups-target-elements.html which ensures the text is not the event target for the wheel event group. Blink handles this correctly, but I'm currently unable to test on WebKit. This is the last failing test (that I know of 😆) with D163484 for Gecko.

@dlrobertson dlrobertson force-pushed the wheel-event-groups branch 2 times, most recently from aad9b4d to 18e23c3 Compare January 27, 2023 18:39
@dlrobertson
Copy link
Member Author

CC: @smaug----


// The first wheel event should be targetted at the moved element.
assert_equals(wheelEventTargets.shift(), initialTarget,
"Initial wheel event is has the moved element as the target");
Copy link
Contributor

Choose a reason for hiding this comment

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

"is has"

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks

@dlrobertson dlrobertson force-pushed the wheel-event-groups branch 2 times, most recently from 3271944 to 2effcbd Compare February 8, 2023 13:47
Copy link
Member Author

@dlrobertson dlrobertson left a comment

Choose a reason for hiding this comment

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

Thanks for the review! Added new test that exercises the display: none case.


// The first wheel event should be targetted at the moved element.
assert_equals(wheelEventTargets.shift(), initialTarget,
"Initial wheel event is has the moved element as the target");
Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks

wheelEventTargets.forEach((wheelEventTarget) => {
assert_not_equals(wheelEventTarget, initialTarget,
"All following wheel events are not targetting the removed element");
});
Copy link
Member Author

Choose a reason for hiding this comment

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

Note that browser engines do different things in the event target mutation test cases. E.g. Gecko and WebKit will essentially fallback to the non wheel event groups event target after the original event target is mutated in some way, while Blink will only retarget once. I greatly weakened this assertion, so that all engines should pass. We might want to circle back at some point once there is some sort of consensus.

e.target.style.height = '10px';
}
}
window.addEventListener("wheel", resizeInitialElement, {passive: true});
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder, passive value could change the behavior due to optimization in some browsers. Did you check the false case?

Could be nicer to check both passive: true and passive: false cases in variants.

scrollY: 2,
scrollingElement: document.scrollingElement,
targetElement: firstRootSpacer,
title: 'Wheel events are grouped and are not targetted at following elements.',
Copy link
Contributor

Choose a reason for hiding this comment

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

FYI: using "should" or "shouldn't" make the expectation clearer for everybody.

// Wait some extra time to ensure nothing from the prior test impacts this one.
// At a minimum this should be greater than 500ms to ensure that the wheel events
// in this test are in a new wheel event group
await new Promise(resolve => step_timeout(resolve, 1000));
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this really required in some browsers? It's odd to work a wheel transaction across browsing contexts.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, I was also confused by this. Spent some time investigating this and the issue was partially solved by using variants. It turns out these tests also need to wait for the compositor to be ready waitForCompositorReady().

Comment on lines 132 to 135
wheelEventTargets.forEach((wheelEventTarget) => {
assert_equals(wheelEventTarget, testInfo.targetElement,
"All wheel events in the group have the same target");
});
Copy link
Contributor

Choose a reason for hiding this comment

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

It's unclear which index's result is wrong. Please update the assertion comment to let developers know the failure point.


await waitForCompositorCommit();

wheelEventTargets = [];
Copy link
Contributor

Choose a reason for hiding this comment

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

Why don't you define the variable at this line?

Comment on lines 37 to 38
function runTest() {
promise_test(async (t) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder, if you do:

promise_test(async t => {
  await new Promise(resolve => addEventListener("load", resolve, {once:true});
  ...

Then, you can reduce one indent level of the test body.

Comment on lines 71 to 74
wheelEventTargets.forEach((wheelEventTarget) => {
assert_equals(wheelEventTarget, firstRootSpacer,
"All following wheel events in the group have the same target");
});
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto, please update the assertion message to let developers know which index failed.

Comment on lines 81 to 84
wheelEventTargets.forEach((wheelEventTarget) => {
assert_not_equals(wheelEventTarget, initialTarget,
"All following wheel events are not targetting the moved element");
});
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto please change the assertion message for each.

Comment on lines 82 to 83
assert_not_equals(wheelEventTarget, initialTarget,
"All following wheel events are not targetting the removed element");
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto.

Comment on lines 78 to 79
assert_equals(wheelEventTarget, initialTarget,
"All wheel events should target the initial element");
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto update the assertion message for each.

Comment on lines 74 to 77
await waitForAnimationEnd(() => { return document.scrollingElement.scrollTop; });
await waitForCompositorCommit();

wheelEventTargets.forEach((wheelEventTarget) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder, scroll top should be checked whether it's still 0 or not.

Copy link
Member Author

Choose a reason for hiding this comment

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

Probably worthwhile

Copy link
Contributor

@smaug---- smaug---- left a comment

Choose a reason for hiding this comment

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

We might want to put all the filed under tentative/ just because there is no spec for this. But other than that looks good

@dlrobertson
Copy link
Member Author

We might want to put all the filed under tentative/ just because there is no spec for this. But other than that looks good

Renamed all tests to <test-name>.tentative.html. Good suggestion

Comment on lines 3 to 6
<head>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
Copy link
Contributor

Choose a reason for hiding this comment

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

Please always add <meta charset="utf-8"> to skip maybe expensive auto-detection of the browsers.

And shouldn't this test have <meta name="timeout" content="long"> for avoiding unexpected timeout during the test?

Comment on lines 3 to 6
<head>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto.

Comment on lines 3 to 6
<head>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto.

Comment on lines 3 to 6
<head>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto.

Comment on lines 3 to 6
<head>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto.

@whimboo
Copy link
Contributor

whimboo commented Nov 28, 2023

Hi @dlrobertson. Do you plan to update this PR? It might be nice to have those tests for the upcoming work for widget level events in Marionette. Thanks.

@dlrobertson dlrobertson force-pushed the wheel-event-groups branch 3 times, most recently from 2691a25 to b4d5721 Compare December 4, 2023 17:25
@dlrobertson
Copy link
Member Author

@whimboo Updated and resolved all comments. Thanks for the ping!

@zcorpan
Copy link
Member

zcorpan commented Dec 5, 2023

Re tentative, now there is a spec since w3c/uievents#338 was merged.

@dlrobertson
Copy link
Member Author

Re tentative, now there is a spec since w3c/uievents#338 was merged.

👍 Thanks for the note about this. Forgot that I'd need to update this.

Also it looks like chrome no longer passes the tests reliably. I'll have to dig into the tests to figure out what changed.

@dlrobertson
Copy link
Member Author

@zcorpan Actually to what extent should I rename things? For example, AFAIK it isn't explicitly specified what should happen when the initial target of the wheel transaction is removed, but I have a test for that in this PR.

@zcorpan
Copy link
Member

zcorpan commented Dec 6, 2023

Keep tentative for any tests where there's no specified correct behavior, and file a spec issue. Bonus points for pointing to the spec issue in the test (with <link rel=help>).

@whimboo
Copy link
Contributor

whimboo commented Apr 9, 2024

Hi @dlrobertson. Do you have an update for this PR?

@whimboo
Copy link
Contributor

whimboo commented Jun 4, 2024

@masayuki-nakano could you please take another look. It looks like that after the last push it was missed to request review again. Thanks.

await waitForCompositorCommit();

initialTarget.addEventListener("wheel", () => {
assert_true(false, "wheel event should never be fired after the target is removed")
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand this test. Looks like that you add this event listener after all events are fired. Don't you need to do this in the first wheel event listener, removeInitialElement?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch! Updated to set this in the first wheel event listener.

Copy link
Contributor

@masayuki-nakano masayuki-nakano left a comment

Choose a reason for hiding this comment

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

Okay, looks fine although I've not checked actual changes strictly because it seems that it's impossible to get the diff from previous version :-(

Well, I'd be happy if you'd add <meta charset="utf-8"> (to skip running encode detection) to all tests for quicker run in CI, but up to you.

@dlrobertson
Copy link
Member Author

Awesome! I'll update it asap

Add tests for groups of wheel events.
@dlrobertson
Copy link
Member Author

Updated!

@whimboo
Copy link
Contributor

whimboo commented Aug 8, 2024

@dlrobertson I assume that this PR doesn't need another review, so what is the merge blocked on?

@dlrobertson dlrobertson merged commit ee15c2a into web-platform-tests:master Aug 11, 2024
19 checks passed
@dlrobertson
Copy link
Member Author

Merged! Just wasn't sure if I was supposed to merge it, or if I was supposed to wait for a reviewer to merge it. Thanks for the ping!

@dlrobertson dlrobertson deleted the wheel-event-groups branch August 13, 2024 07:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants