diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /devtools/client/debugger/test/mochitest/browser_dbg-preview.js | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/client/debugger/test/mochitest/browser_dbg-preview.js')
-rw-r--r-- | devtools/client/debugger/test/mochitest/browser_dbg-preview.js | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-preview.js b/devtools/client/debugger/test/mochitest/browser_dbg-preview.js new file mode 100644 index 0000000000..532854548c --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-preview.js @@ -0,0 +1,333 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */ + +// Test hovering on an object, which will show a popup and on a +// simple value, which will show a tooltip. + +"use strict"; + +// Showing/hiding the preview tooltip can be slow as we wait for CodeMirror scroll... +requestLongerTimeout(2); + +add_task(async function () { + const dbg = await initDebugger("doc-preview.html", "preview.js"); + + await testPreviews(dbg, "testInline", [ + { line: 17, column: 16, expression: "prop", result: 2 }, + ]); + + await selectSource(dbg, "preview.js"); + await testBucketedArray(dbg); + + await testPreviews(dbg, "empties", [ + { line: 6, column: 9, expression: "a", result: '""' }, + { line: 7, column: 9, expression: "b", result: "false" }, + { line: 8, column: 9, expression: "c", result: "undefined" }, + { line: 9, column: 9, expression: "d", result: "null" }, + ]); + + await testPreviews(dbg, "objects", [ + { line: 27, column: 10, expression: "empty", result: "Object" }, + { line: 28, column: 22, expression: "foo", result: 1 }, + ]); + + await testPreviews(dbg, "smalls", [ + { line: 14, column: 9, expression: "a", result: '"..."' }, + { line: 15, column: 9, expression: "b", result: "true" }, + { line: 16, column: 9, expression: "c", result: "1" }, + { + line: 17, + column: 9, + expression: "d", + fields: [["length", "0"]], + }, + ]); + + await testPreviews(dbg, "classPreview", [ + { line: 50, column: 20, expression: "x", result: 1 }, + { line: 50, column: 29, expression: "#privateVar", result: 2 }, + { + line: 50, + column: 47, + expression: "#privateStatic", + fields: [ + ["first", `"a"`], + ["second", `"b"`], + ], + }, + { + line: 51, + column: 26, + expression: "this", + fields: [ + ["x", "1"], + ["#privateVar", "2"], + ], + }, + { line: 51, column: 39, expression: "#privateVar", result: 2 }, + ]); + + await testPreviews(dbg, "multipleTokens", [ + { line: 81, column: 4, expression: "foo", result: "Object" }, + { line: 81, column: 11, expression: "blip", result: "Object" }, + { line: 82, column: 8, expression: "bar", result: "Object" }, + { line: 84, column: 16, expression: "boom", result: `0` }, + ]); + + await testHoveringInvalidTargetTokens(dbg); + + info( + "Check that closing the preview tooltip doesn't release the underlying object actor" + ); + invokeInTab("classPreview"); + await waitForPaused(dbg); + info("Display popup a first time and hide it"); + await assertPreviews(dbg, [ + { + line: 60, + column: 7, + expression: "y", + fields: [["hello", "{…}"]], + }, + ]); + + info("Display the popup again and try to expand a property"); + const { element: popupEl, tokenEl } = await tryHovering( + dbg, + 60, + 7, + "previewPopup" + ); + const nodes = popupEl.querySelectorAll(".preview-popup .node"); + const initialNodesLength = nodes.length; + nodes[1].querySelector(".arrow").click(); + await waitFor( + () => + popupEl.querySelectorAll(".preview-popup .node").length > + initialNodesLength + ); + ok(true, `"hello" was expanded`); + await closePreviewForToken(dbg, tokenEl, "popup"); + await resume(dbg); + + await testMovingFromATokenToAnother(dbg); +}); + +async function testPreviews(dbg, fnName, previews) { + invokeInTab(fnName); + await waitForPaused(dbg); + + await assertPreviews(dbg, previews); + await resume(dbg); + + info(`Ran tests for ${fnName}`); +} + +async function testHoveringInvalidTargetTokens(dbg) { + // Test hovering tokens for which we shouldn't have a preview popup displayed + invokeInTab("invalidTargets"); + await waitForPaused(dbg); + // CodeMirror refreshes after inline previews are displayed, so wait until they're rendered. + await waitForInlinePreviews(dbg); + + await assertNoPreviews(dbg, `"a"`, 69, 4); + await assertNoPreviews(dbg, `false`, 70, 4); + await assertNoPreviews(dbg, `undefined`, 71, 4); + await assertNoPreviews(dbg, `null`, 72, 4); + await assertNoPreviews(dbg, `42`, 73, 4); + await assertNoPreviews(dbg, `const`, 74, 4); + + // checking inline preview widget + // Move the cursor to the top left corner to have a clean state + resetCursorPositionToTopLeftCorner(dbg); + + const inlinePreviewEl = findElementWithSelector( + dbg, + ".CodeMirror-code .CodeMirror-widget" + ); + is(inlinePreviewEl.innerText, `myVar:"foo"`, "got expected inline preview"); + + const racePromise = Promise.any([ + waitForElement(dbg, "previewPopup"), + wait(500).then(() => "TIMEOUT"), + ]); + // Hover over the inline preview element + hoverToken(inlinePreviewEl); + const raceResult = await racePromise; + is(raceResult, "TIMEOUT", "No popup was displayed over the inline preview"); + + await resume(dbg); +} + +async function assertNoPreviews(dbg, expression, line, column) { + // Move the cursor to the top left corner to have a clean state + resetCursorPositionToTopLeftCorner(dbg); + + // Hover the token + const result = await Promise.race([ + tryHoverTokenAtLine(dbg, expression, line, column, "previewPopup"), + wait(500).then(() => "TIMEOUT"), + ]); + is(result, "TIMEOUT", `No popup was displayed when hovering "${expression}"`); +} + +function resetCursorPositionToTopLeftCorner(dbg) { + EventUtils.synthesizeMouse( + findElement(dbg, "codeMirror"), + 0, + 0, + { + type: "mousemove", + }, + dbg.win + ); +} + +async function testMovingFromATokenToAnother(dbg) { + info( + "Check that moving the mouse to another token when popup is displayed updates highlighted token and popup position" + ); + invokeInTab("classPreview"); + await waitForPaused(dbg); + + info("Hover token `Foo` in `Foo.#privateStatic` expression"); + const fooTokenEl = getTokenElAtLine(dbg, "Foo", 50, 44); + const cm = getCM(dbg); + const onScrolled = waitForScrolling(cm); + cm.scrollIntoView({ line: 49, ch: 0 }, 0); + await onScrolled; + const { element: fooPopupEl } = await tryHoverToken(dbg, fooTokenEl, "popup"); + ok(!!fooPopupEl, "popup is displayed"); + ok( + fooTokenEl.classList.contains("preview-token"), + "`Foo` token is highlighted" + ); + + // store original position + const originalPopupPosition = fooPopupEl.getBoundingClientRect().x; + + info( + "Move mouse over the `#privateStatic` token in `Foo.#privateStatic` expression" + ); + const privateStaticTokenEl = getTokenElAtLine(dbg, "#privateStatic", 50, 48); + + // The sequence of event to trigger the bug this is covering isn't easily reproducible + // by firing a few chosen events (because of React async rendering), so we are going to + // mimick moving the mouse from the `Foo` to `#privateStatic` in a given amount of time + + // So get all the different token quads to compute their center + const fooTokenQuad = fooTokenEl.getBoxQuads()[0]; + const privateStaticTokenQuad = privateStaticTokenEl.getBoxQuads()[0]; + const fooXCenter = + fooTokenQuad.p1.x + (fooTokenQuad.p2.x - fooTokenQuad.p1.x) / 2; + const fooYCenter = + fooTokenQuad.p1.y + (fooTokenQuad.p3.y - fooTokenQuad.p1.y) / 2; + const privateStaticXCenter = + privateStaticTokenQuad.p1.x + + (privateStaticTokenQuad.p2.x - privateStaticTokenQuad.p1.x) / 2; + const privateStaticYCenter = + privateStaticTokenQuad.p1.y + + (privateStaticTokenQuad.p3.y - privateStaticTokenQuad.p1.y) / 2; + + // we can then compute the distance to cover between the two token centers + const xDistance = privateStaticXCenter - fooXCenter; + const yDistance = privateStaticYCenter - fooYCenter; + const movementDuration = 50; + const xIncrements = xDistance / movementDuration; + const yIncrements = yDistance / movementDuration; + + // Finally, we're going to fire a mouseover event every ms + info("Move mousecursor between the `Foo` token to the `#privateStatic` one"); + for (let i = 0; i < movementDuration; i++) { + const x = fooXCenter + (yDistance + i * xIncrements); + const y = fooYCenter + (yDistance + i * yIncrements); + EventUtils.synthesizeMouseAtPoint( + x, + y, + { + type: "mouseover", + }, + fooTokenEl.ownerGlobal + ); + await wait(1); + } + + info("Wait for the popup to display the data for `#privateStatic`"); + await waitFor(() => { + const popup = findElement(dbg, "popup"); + if (!popup) { + return false; + } + // for `Foo`, the header text content is "Foo", so when it's "Object", we know the + // popup was updated + return ( + popup.querySelector(".preview-popup .node .objectBox")?.textContent === + "Object" + ); + }); + ok(true, "Popup is displayed for #privateStatic"); + + ok( + !fooTokenEl.classList.contains("preview-token"), + "`Foo` token is not highlighted anymore" + ); + ok( + privateStaticTokenEl.classList.contains("preview-token"), + "`#privateStatic` token is highlighted" + ); + + const privateStaticPopupEl = await waitForElement(dbg, "popup"); + const newPopupPosition = privateStaticPopupEl.getBoundingClientRect().x; + isnot( + Math.round(newPopupPosition), + Math.round(originalPopupPosition), + `Popup position was updated` + ); + + await resume(dbg); +} + +async function testBucketedArray(dbg) { + invokeInTab("largeArray"); + await waitForPaused(dbg); + const { element: popupEl, tokenEl } = await tryHovering( + dbg, + 34, + 10, + "previewPopup" + ); + + info("Wait for top level node to expand and child nodes to load"); + await waitUntil( + () => popupEl.querySelectorAll(".preview-popup .node").length > 1 + ); + + const oiNodes = Array.from(popupEl.querySelectorAll(".preview-popup .node")); + + const displayedPropertyNames = oiNodes.map( + oiNode => oiNode.querySelector(".object-label")?.textContent + ); + Assert.deepEqual(displayedPropertyNames, [ + null, // No property name is displayed for the root node + "[0…99]", + "[100…100]", + "length", + "<prototype>", + ]); + const node = oiNodes.find( + oiNode => oiNode.querySelector(".object-label")?.textContent === "length" + ); + if (!node) { + ok(false, `The "length" property is not displayed in the popup`); + } else { + is( + node.querySelector(".objectBox").textContent, + "101", + `The "length" property has the expected value` + ); + } + await closePreviewForToken(dbg, tokenEl, "popup"); + + await resume(dbg); +} |