File: interestfor-show-delay.tentative.html

package info (click to toggle)
firefox 144.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,637,504 kB
  • sloc: cpp: 7,576,692; javascript: 6,430,831; ansic: 3,748,119; python: 1,398,978; xml: 628,810; asm: 438,679; java: 186,194; sh: 63,212; makefile: 19,159; objc: 13,086; perl: 12,986; yacc: 4,583; cs: 3,846; pascal: 3,448; lex: 1,720; ruby: 1,003; exp: 762; php: 436; lisp: 258; awk: 247; sql: 66; sed: 53; csh: 10
file content (208 lines) | stat: -rw-r--r-- 10,591 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
<!DOCTYPE html>
<meta charset="utf-8" />
<link rel="author" href="mailto:masonf@chromium.org">
<link rel="help" href="https://open-ui.org/components/interest-invokers.explainer">
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="resources/invoker-utils.js"></script>

<meta name=variant content=?layout=plain&method=hover>
<meta name=variant content=?layout=nested&method=hover>
<meta name=variant content=?layout=nested-offset&method=hover>
<meta name=variant content=?layout=plain&method=focus>
<meta name=variant content=?layout=nested&method=focus>
<meta name=variant content=?layout=nested-offset&method=focus>

<body>
<script>
// The invokerLayout and invokerMethod parameters are provided by variants, to
// effectively split this (slow) test into multiple runs.
const urlParams = new URLSearchParams(window.location.search);
invokerLayout = urlParams.get('layout');
invokerMethod = urlParams.get('method');
description = `layout ${invokerLayout}, method ${invokerMethod}`;

// NOTE about testing methodology:
// This test uses popovers as an invoker target, and checks whether they are
// triggered *after* the appropriate hover/focus delay. The delay used for
// testing is kept low, to avoid this test taking too long, but that means that
// sometimes on a slow bot/client, the hover delay can elapse before we are able
// to check the popover status. And that can make this test flaky. To avoid
// that, the msSinceMouseOver() function is used to check that not-too-much time
// has passed, and if it has, the test is simply skipped. Because of this
// methodology, many/most of these tests will pass on browsers that do not
// implement `interestfor`. See the `interestfor-basic-delays` test.

const invokerShowDelayMs = 100; // The CSS delay setting.
const hoverFocusWaitTimeMs = 200; // How long to wait to cover the delay for sure.

async function makePopoverAndInvoker(test, invokerLayout, showdelayMs) {
  // This ensures the tests in this file don't succeed sometimes, due to the above NOTE.
  assert_true(HTMLAnchorElement.prototype.hasOwnProperty('interestForElement'),'interestfor is not supported');
  if (showdelayMs === undefined) {
    showdelayMs = invokerShowDelayMs;
  }
  let {popover, invoker, unrelated} = await createPopoverAndInvokerForHoverTests(test, showdelayMs, /*hidedelayMs*/10000000);
  invoker.setAttribute('class','invoker');
  const originalInvoker = invoker;
  const reassignInvokerTargetFn = (p) => {originalInvoker.interestForElement = p};
  switch (invokerLayout) {
    case 'plain':
      // Invoker is just a button.
      invoker.textContent = 'Invoker';
      break;
    case 'nested':
      // Invoker is just a button containing a div.
      const child1 = invoker.appendChild(document.createElement('div'));
      child1.textContent = 'Invoker';
      break;
    case 'nested-offset':
      // Invoker is a child of the invoking button, and is not contained within
      // the bounds of the interestfor element.
      invoker.textContent = 'Invoker';
      // Reassign invoker to the child:
      invoker = invoker.appendChild(document.createElement('div'));
      invoker.textContent = 'Invoker child';
      invoker.tabIndex = 0;
      invoker.setAttribute('style','position:relative; top:300px; left:300px;');
      break;
    default:
      assert_unreached(`Invalid invokerLayout ${invokerLayout}`);
  }
  test.add_cleanup(() => {
    originalInvoker.remove();
  });
  assert_false(popover.matches(':popover-open'),'Popover should start out closed');
  return {popover,invoker,unrelated,reassignInvokerTargetFn};
}

promise_test(async (t) => {
  const {popover,invoker} = await makePopoverAndInvoker(t,invokerLayout);
  const token = await mouseOverOrFocusAndRecord(t,invokerMethod,invoker);
  let showing = popover.matches(':popover-open');
  // See NOTE above.
  if (msSinceMouseOver(token) < invokerShowDelayMs)
    assert_false(showing,'interest should not be shown immediately');
  await waitForHoverTime(hoverFocusWaitTimeMs);
  assert_true(msSinceMouseOver(token) >= hoverFocusWaitTimeMs,'waitForHoverTime should wait the specified time');
  assert_true(popover.matches(':popover-open'),'interest should be shown after the delay');
  assert_true(hoverFocusWaitTimeMs > invokerShowDelayMs,'invokerShowDelayMs is the CSS setting, hoverFocusWaitTimeMs should be longer than that');
},`interestfor fires after a delay, ${description}`);

promise_test(async (t) => {
  const {popover,invoker} = await makePopoverAndInvoker(t,invokerLayout);
  assert_false(popover.matches(':popover-open'));
  invoker.click(); // Click the invoker
  assert_false(popover.matches(':popover-open'),'Clicking the invoker should not "show interest"');
},`interestfor should not trigger via mouse click, ${description}`);

promise_test(async (t) => {
  const longerHoverDelay = hoverFocusWaitTimeMs*2;
  const {popover,invoker} = await makePopoverAndInvoker(t,invokerLayout,longerHoverDelay);
  const token = await mouseOverOrFocusAndRecord(t,invokerMethod,invoker);
  await waitForHoverTime(hoverFocusWaitTimeMs);
  showing = popover.matches(':popover-open');
  if (msSinceMouseOver(token) >= longerHoverDelay)
    return; // The WPT runner was too slow.
  assert_false(showing,'interestfor should respect CSS setting');
},`interestfor interest-show-delay is respected, ${description}`);

promise_test(async (t) => {
  const longerHoverDelay = hoverFocusWaitTimeMs*4;
  const {popover,invoker,unrelated} = await makePopoverAndInvoker(t,invokerLayout,longerHoverDelay);
  const token = await mouseOverOrFocusAndRecord(t,invokerMethod,invoker);
  await waitForHoverTime(hoverFocusWaitTimeMs);
  showing1 = popover.matches(':popover-open');
  await hoverOrFocus(invokerMethod,unrelated);
  if (msSinceMouseOver(token) >= longerHoverDelay)
    return; // The WPT runner was too slow.
  await waitForHoverTime(longerHoverDelay);
  showing2 = popover.matches(':popover-open');
  assert_false(showing1,'interest shouldn\'t be shown immediately');
  assert_false(showing2,'because target was de-hovered/de-focused before the delay elapsed, interest should never be shown');
},`Show delay is cancelled if hover/focus changes, ${description}`);

promise_test(async (t) => {
  const {popover,invoker} = await makePopoverAndInvoker(t,invokerLayout);
  popover.showPopover();
  assert_true(popover.matches(':popover-open'));
  let gotInterest = false;
  popover.addEventListener('interest',() => (gotInterest=true),{once:true});
  await hoverOrFocus(invokerMethod,invoker);
  const stillOpen = popover.matches(':popover-open');
  await waitForHoverTime(hoverFocusWaitTimeMs);
  assert_true(popover.matches(':popover-open'),'popover should stay showing after delay');
  assert_true(stillOpen,'popover should have been open before the delay also');
  assert_true(gotInterest,'interest event should still be fired');
},`interestfor does not close an already-open popover, ${description}`);

promise_test(async (t) => {
  const {popover,invoker} = await makePopoverAndInvoker(t,invokerLayout);
  popover.remove(); // Remove from the document
  let gotInterest = false;
  popover.addEventListener('interest',() => (gotInterest=true),{once:true});
  await hoverOrFocus(invokerMethod,invoker);
  await waitForHoverTime(hoverFocusWaitTimeMs);
  assert_false(gotInterest,'interest should not be shown if the target is removed');
  // Now put it back in the document and make sure it doesn't trigger.
  document.body.appendChild(popover);
  await waitForHoverTime(hoverFocusWaitTimeMs);
  assert_false(gotInterest,'interest should not be shown even when returned to the document');
},`interestfor does nothing when the target is moved out of the document, ${description}`);

promise_test(async (t) => {
  const {popover,invoker,reassignInvokerTargetFn} = await makePopoverAndInvoker(t,invokerLayout);
  const popover2 = document.createElement('div');
  popover2.popover = 'auto';
  document.body.appendChild(popover2);
  t.add_cleanup(() => popover2.remove());
  const token = await mouseOverOrFocusAndRecord(t,invokerMethod,invoker);
  let eitherShowing = popover.matches(':popover-open') || popover2.matches(':popover-open');
  reassignInvokerTargetFn(popover2);
  // See NOTE above.
  if (msSinceMouseOver(token) >= invokerShowDelayMs)
    return; // The WPT runner was too slow.
  assert_false(eitherShowing,'interest should should not be shown immediately');
  await waitForHoverTime(hoverFocusWaitTimeMs);
  assert_false(popover.matches(':popover-open'),'old target should not receive interest, since interestfor was reassigned');
  assert_false(popover2.matches(':popover-open'),'new target should not receive interest, since interestfor was reassigned');
},`interestfor does nothing when the target changes, ${description}`);

promise_test(async (t) => {
  const {popover,invoker,unrelated} = await makePopoverAndInvoker(t,invokerLayout,/*showdelayMs*/0);
  const invoker2 = document.createElement('button');
  invoker2.interestForElement = popover;
  document.body.appendChild(invoker2);
  t.add_cleanup(() => invoker2.remove());
  invoker2.setAttribute('style',`
    interest-show-delay: 0s;
    interest-hide-delay: 10000s;
    position:fixed;
    top:300px;
  `);
  invoker2.innerText = 'Invoker 2';
  let events = [];
  popover.addEventListener('interest',() => events.push('interest'));
  popover.addEventListener('loseinterest',() => events.push('lose interest'));
  popover.addEventListener('beforetoggle',(e) => events.push(e.newState));
  assert_array_equals(events,[]);
  await hoverOrFocus(invokerMethod,invoker);
  assert_array_equals(events,['interest','open']);
  // Different invoker for same target - should first (immediately) lose interest on old invoker.
  await hoverOrFocus(invokerMethod,invoker2);
  assert_array_equals(events,['interest','open','lose interest','closed','interest','open']);
  // Lose interest delays are long, so nothing happens here.
  events = [];
  await hoverOrFocus(invokerMethod,unrelated);
  assert_array_equals(events,[]);
  // Back to the same invoker - shouldn't re-fire events.
  await hoverOrFocus(invokerMethod,invoker2);
  assert_array_equals(events,[]);
  assert_true(popover.matches(':popover-open'));
  popover.hidePopover();
},`moving hover/focus between two invokers for the same target does the right thing, ${description}`);
</script>