browser_dbg-preview-moving-token.js (5722B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */ 4 5 // Test the debugger preview popup when moving from one token to another. 6 7 "use strict"; 8 9 add_task(async function () { 10 const dbg = await initDebugger("doc-preview.html", "preview.js"); 11 12 await selectSource(dbg, "preview.js"); 13 14 info( 15 "Check that moving the mouse to another token when popup is displayed updates highlighted token and popup position" 16 ); 17 invokeInTab("classPreview"); 18 await waitForPaused(dbg); 19 20 // Scroll one line *fore* the hovered expression to guarantee showing the line entirely. 21 // It may not scroll if the line 50 is partially visible. 22 // 23 // Also ensure scrolling so that the scrolled line is at the top/start of the viewport 24 // so that the preview popup can be shown at the bottom of the token and not on its right. 25 await scrollEditorIntoView(dbg, 49, 0, "start"); 26 27 // Wait for all the updates to the document to complete to make all 28 // token elements have been rendered 29 await waitForDocumentLoadComplete(dbg); 30 31 info("Hover token `Foo` in `Foo.#privateStatic` expression"); 32 const fooTokenEl = await getTokenElAtLine(dbg, "Foo", 50, 44); 33 const { element: fooPopupEl } = await tryHoverToken(dbg, fooTokenEl, "popup"); 34 ok(!!fooPopupEl, "popup is displayed"); 35 ok( 36 fooTokenEl.classList.contains("preview-token"), 37 "`Foo` token is highlighted" 38 ); 39 40 // store original position 41 const originalPopupPosition = fooPopupEl.getBoundingClientRect().x; 42 43 info( 44 "Move mouse over the `#privateStatic` token in `Foo.#privateStatic` expression" 45 ); 46 const privateStaticTokenEl = await getTokenElAtLine( 47 dbg, 48 "#privateStatic", 49 50, 50 48 51 ); 52 53 // The sequence of event to trigger the bug this is covering isn't easily reproducible 54 // by firing a few chosen events (because of React async rendering), so we are going to 55 // mimick moving the mouse from the `Foo` to `#privateStatic` in a given amount of time 56 57 // So get all the different token quads to compute their center 58 const fooTokenQuad = fooTokenEl.getBoxQuads()[0]; 59 const privateStaticTokenQuad = privateStaticTokenEl.getBoxQuads()[0]; 60 const fooXCenter = 61 fooTokenQuad.p1.x + (fooTokenQuad.p2.x - fooTokenQuad.p1.x) / 2; 62 const fooYCenter = 63 fooTokenQuad.p1.y + (fooTokenQuad.p3.y - fooTokenQuad.p1.y) / 2; 64 const privateStaticXCenter = 65 privateStaticTokenQuad.p1.x + 66 (privateStaticTokenQuad.p2.x - privateStaticTokenQuad.p1.x) / 2; 67 const privateStaticYCenter = 68 privateStaticTokenQuad.p1.y + 69 (privateStaticTokenQuad.p3.y - privateStaticTokenQuad.p1.y) / 2; 70 71 // we can then compute the distance to cover between the two token centers 72 const xDistance = privateStaticXCenter - fooXCenter; 73 const yDistance = privateStaticYCenter - fooYCenter; 74 const movementDuration = 50; 75 const xIncrements = xDistance / movementDuration; 76 const yIncrements = yDistance / movementDuration; 77 78 // Finally, we're going to fire a mouseover event every ms 79 info("Move mousecursor between the `Foo` token to the `#privateStatic` one"); 80 for (let i = 0; i < movementDuration; i++) { 81 const x = fooXCenter + (yDistance + i * xIncrements); 82 const y = fooYCenter + (yDistance + i * yIncrements); 83 EventUtils.synthesizeMouseAtPoint( 84 x, 85 y, 86 { 87 type: "mouseover", 88 }, 89 fooTokenEl.ownerGlobal 90 ); 91 await wait(1); 92 } 93 94 info("Wait for the popup to display the data for `#privateStatic`"); 95 await waitFor(() => { 96 const popup = findElement(dbg, "popup"); 97 if (!popup) { 98 return false; 99 } 100 // for `Foo`, the header text content is "Foo", so when it's "Object", we know the 101 // popup was updated 102 return ( 103 popup.querySelector(".preview-popup .node .objectBox")?.textContent === 104 "Object" 105 ); 106 }); 107 ok(true, "Popup is displayed for #privateStatic"); 108 109 ok( 110 !fooTokenEl.classList.contains("preview-token"), 111 "`Foo` token is not highlighted anymore" 112 ); 113 ok( 114 privateStaticTokenEl.classList.contains("preview-token"), 115 "`#privateStatic` token is highlighted" 116 ); 117 118 const privateStaticPopupEl = await waitForElement(dbg, "popup"); 119 const newPopupPosition = privateStaticPopupEl.getBoundingClientRect().x; 120 isnot( 121 Math.round(newPopupPosition), 122 Math.round(originalPopupPosition), 123 `Popup position was updated` 124 ); 125 126 // Move many times between a token, its gap and then outside to hide it many times 127 // to highlight any potential race condition. 128 for (let i = 0; i < 10; i++) { 129 info( 130 `Move out by passing over the gap, before going on the right of the token to hide the preview (try #${i + 1})` 131 ); 132 EventUtils.synthesizeMouseAtCenter( 133 privateStaticPopupEl.querySelector(".gap"), 134 { type: "mousemove" }, 135 privateStaticPopupEl.ownerGlobal 136 ); 137 EventUtils.synthesizeMouseAtPoint( 138 privateStaticTokenQuad.p2.x + 100, 139 privateStaticYCenter, 140 { 141 type: "mousemove", 142 }, 143 fooTokenEl.ownerGlobal 144 ); 145 info("Wait for popup to be hidden when going right"); 146 await waitUntil(() => findElement(dbg, "popup") == null); 147 148 info("Move back in the center of the token to show the preview again"); 149 EventUtils.synthesizeMouseAtPoint( 150 privateStaticXCenter, 151 privateStaticYCenter, 152 { 153 type: "mousemove", 154 }, 155 fooTokenEl.ownerGlobal 156 ); 157 info("Wait for popup to be shown on private field again"); 158 await waitUntil(() => !!findElement(dbg, "popup")); 159 } 160 161 await closePreviewForToken(dbg, privateStaticTokenEl, "popup"); 162 await resume(dbg); 163 });