blob: 8f715b53c24bc8fc7ea35292a5ca1c64984029e0 [file] [log] [blame]
<!doctype html>
<html>
<head>
<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
<script src="../debugger/resources/log-active-stack-trace.js"></script>
<script>
function subtreeModifiedTest() {
document.getElementById("subtree-modified-test").append(document.createElement("p"));
}
function attributeModifiedTest() {
document.getElementById("attribute-modified-test").setAttribute("display", "none");
}
function nodeRemovedDirectTest() {
let node = document.getElementById("node-removed-direct-test");
let parent = node.parentNode;
node.remove();
parent.append(node);
}
function nodeRemovedAncestorTest() {
let node = document.getElementById("node-removed-ancestor-test");
let parent = node.parentNode;
let grandparent = parent.parentNode;
parent.remove(node);
grandparent.append(parent);
}
function test()
{
const subtreeModifiedTestId = "subtree-modified-test";
const attributeModifiedTestId = "attribute-modified-test";
const nodeRemovedDirectTestId = "node-removed-direct-test";
const nodeRemovedAncestorTestId = "node-removed-ancestor-test";
const multipleBreakpointsTestId = "multiple-breakpoints-test";
let debuggerPausedListener = null;
let suite = InspectorTest.createAsyncSuite("DOMBreakpoints");
function teardown(resolve) {
let breakpoints = WI.domDebuggerManager.domBreakpoints;
for (let breakpoint of breakpoints)
WI.domDebuggerManager.removeDOMBreakpoint(breakpoint);
InspectorTest.assert(!WI.domDebuggerManager.domBreakpoints.length);
WI.debuggerManager.removeEventListener(WI.DebuggerManager.Event.Paused, debuggerPausedListener);
debuggerPausedListener = null;
resolve();
}
function rejectOnPause() {
return new Promise((resolve, reject) => {
InspectorTest.assert(!debuggerPausedListener);
debuggerPausedListener = WI.debuggerManager.addEventListener(WI.DebuggerManager.Event.Paused, (event) => {
WI.debuggerManager.resume();
InspectorTest.fail("Should not pause.");
reject();
});
});
}
function awaitEvaluateInPage(expression) {
return new Promise((resolve, reject) => {
InspectorTest.log("Wait for evaluate in page to return.");
InspectorTest.evaluateInPage(expression, (error) => {
if (error)
reject(error);
resolve();
});
});
}
function awaitQuerySelector(selector) {
return new Promise((resolve, reject) => {
WI.domManager.requestDocument((documentNode) => {
if (!documentNode) {
reject();
return;
}
WI.domManager.querySelector(documentNode.id, selector, (nodeId) => {
if (!nodeId) {
InspectorTest.fail("Selector returned no nodes.", selector);
reject();
return;
}
let node = WI.domManager.nodeForId(nodeId);
InspectorTest.assert(node, "Missing node for id.", nodeId);
if (!node) {
reject();
return;
}
resolve(node);
});
});
});
}
function addBreakpointForElementIdentifier(elementIdentifier, type, disabled) {
return new Promise((resolve, reject) => {
awaitQuerySelector("#" + elementIdentifier)
.then((node) => {
WI.domDebuggerManager.awaitEvent(WI.DOMDebuggerManager.Event.DOMBreakpointAdded)
.then((event) => {
let breakpoint = event.data.breakpoint;
InspectorTest.pass(`Added '${type}' breakpoint.`);
resolve(event);
});
WI.domDebuggerManager.addDOMBreakpoint(new WI.DOMBreakpoint(node, type, {disabled}));
});
});
}
function addTestsForBreakpointType({name, elementIdentifier, type, expression, insertion, shouldMatch}) {
suite.addTestCase({
name: `DOMBreakpoints.${name}.BreakpointEnabled`,
description: "Check that debugger pauses for breakpoint type.",
teardown,
test(resolve, reject) {
addBreakpointForElementIdentifier(elementIdentifier, type)
.then((event) => {
let breakpoint = event.data.breakpoint;
InspectorTest.expectEqual(breakpoint.type, type, "Breakpoint should have expected type.");
InspectorTest.log("Call DOM operation.");
InspectorTest.evaluateInPage(expression);
return WI.debuggerManager.awaitEvent(WI.DebuggerManager.Event.Paused);
})
.then(() => {
let targetData = WI.debuggerManager.dataForTarget(WI.debuggerManager.activeCallFrame.target);
InspectorTest.log("PAUSED:");
InspectorTest.expectEqual(targetData.pauseReason, WI.DebuggerManager.PauseReason.DOM, "Pause reason should be DOM.");
let pauseData = targetData.pauseData;
InspectorTest.expectEqual(pauseData.type, type, `Pause type should be '${type}'.`);
InspectorTest.expectEqual(pauseData.nodeId, domNodeIdentifier, `Pause nodeId should be expected value.`);
if (insertion !== undefined)
InspectorTest.expectEqual(insertion, target.pauseData.insertion, `Pause insertion should be '${insertion}'.`);
if (pauseData.targetNodeId) {
if (shouldMatch)
InspectorTest.expectEqual(pauseData.targetNodeId, pauseData.nodeId, "Pause targetNodeId should match nodeId.");
else
InspectorTest.expectNotEqual(pauseData.targetNodeId, pauseData.nodeId, "Pause targetNodeId should not match nodeId.");
}
logActiveStackTrace();
return WI.debuggerManager.resume();
})
.then(resolve, reject);
}
});
suite.addTestCase({
name: `DOMBreakpoints.${name}.BreakpointDisabled`,
description: "Check that debugger does not pause for disabled breakpoint.",
teardown,
test(resolve, reject) {
const disabled = true;
addBreakpointForElementIdentifier(elementIdentifier, type, disabled)
.then(() => Promise.race([awaitEvaluateInPage(expression), rejectOnPause()]))
.then(() => {
InspectorTest.pass("Should not pause for disabled breakpoint.");
resolve();
})
.catch(reject);
}
});
suite.addTestCase({
name: `DOMBreakpoints.${name}.DebuggerDisabled`,
description: "Check that debugger does not pause when the debugger is disabled.",
teardown,
test(resolve, reject) {
addBreakpointForElementIdentifier(elementIdentifier, type)
.then(() => DebuggerAgent.setBreakpointsActive(false))
.then(() => Promise.race([awaitEvaluateInPage(expression), rejectOnPause()]))
.then(() => DebuggerAgent.setBreakpointsActive(true))
.then(() => {
InspectorTest.pass("Should not pause for disabled breakpoint.");
resolve();
})
.catch(reject);
}
});
suite.addTestCase({
name: `DOMBreakpoints.${name}.RemoveBreakpoint`,
description: "Check that debugger does not pause for removed breakpoint.",
teardown,
test(resolve, reject) {
addBreakpointForElementIdentifier(elementIdentifier, type)
.then((event) => {
let promise = WI.domDebuggerManager.awaitEvent(WI.DOMDebuggerManager.Event.DOMBreakpointRemoved);
let breakpoint = event.data.breakpoint;
InspectorTest.log("Remove breakpoint.");
WI.domDebuggerManager.removeDOMBreakpoint(breakpoint);
return promise;
})
.then(() => Promise.race([awaitEvaluateInPage(expression), rejectOnPause()]))
.then(() => {
InspectorTest.pass("Should not pause for removed breakpoint.");
resolve();
})
.catch(reject);
}
});
}
suite.addTestCase({
name: "BasicBreakpoint",
description: "Check that breakpoint is in a valid initial state.",
teardown,
test(resolve, reject) {
addBreakpointForElementIdentifier(subtreeModifiedTestId, WI.DOMBreakpoint.Type.SubtreeModified)
.then((event) => {
let breakpoint = event.data.breakpoint;
InspectorTest.expectFalse(breakpoint.disabled, "Breakpoint should not be disabled.");
InspectorTest.expectThat(breakpoint.domNodeIdentifier, "Breakpoint should have node identifier.");
InspectorTest.assert(WI.networkManager.mainFrame, "Missing main frame.");
let documentURL = WI.networkManager.mainFrame.url;
InspectorTest.expectEqual(breakpoint.url, documentURL, "Breakpoint URL should match document URL.");
})
.then(resolve, reject);
}
});
addTestsForBreakpointType({
name: "SubtreeModified",
elementIdentifier: subtreeModifiedTestId,
type: WI.DOMBreakpoint.Type.SubtreeModified,
expression: "subtreeModifiedTest()",
insertion: true,
shouldMatch: true,
});
addTestsForBreakpointType({
name: "AttributeModified",
elementIdentifier: attributeModifiedTestId,
type: WI.DOMBreakpoint.Type.AttributeModified,
expression: "attributeModifiedTest()",
});
addTestsForBreakpointType({
name: "NodeRemovedSelf",
elementIdentifier: nodeRemovedDirectTestId,
type: WI.DOMBreakpoint.Type.NodeRemoved,
expression: "nodeRemovedDirectTest()",
});
addTestsForBreakpointType({
name: "NodeRemovedAncestor",
elementIdentifier: nodeRemovedAncestorTestId,
type: WI.DOMBreakpoint.Type.NodeRemoved,
expression: "nodeRemovedAncestorTest()",
shouldMatch: true,
});
suite.addTestCase({
name: "RemoveAllBreakpointsForNode",
description: "Check that debugger does not pause for removed breakpoints on node.",
teardown,
test(resolve, reject) {
let breakpointTypes = Object.values(WI.DOMBreakpoint.Type);
let breakpointPromises = breakpointTypes.map(type => addBreakpointForElementIdentifier(multipleBreakpointsTestId, type));
Promise.all(breakpointPromises)
.then((values) => {
let breakpoints = values.map(event => event.data.breakpoint);
if (!breakpoints.length) {
InspectorTest.fail("No breakpoints added.");
reject();
return;
}
let node = WI.domManager.nodeForId(breakpoints[0].domNodeIdentifier);
if (!node) {
InspectorTest.fail("No DOM node associated with breakpoint.");
reject();
return;
}
let breakpointsRemoved = 0;
function breakpointRemoved() {
if (++breakpointsRemoved === breakpoints.length) {
InspectorTest.pass(`Removed ${breakpointsRemoved} breakpoints.`);
let nodeBreakpoints = WI.domDebuggerManager.domBreakpointsForNode(node);
InspectorTest.expectThat(!nodeBreakpoints.length, "DOM node should have no breakpoints.");
WI.domDebuggerManager.removeEventListener(WI.DOMDebuggerManager.Event.DOMBreakpointRemoved, breakpointRemoved);
resolve();
}
}
WI.domDebuggerManager.addEventListener(WI.DOMDebuggerManager.Event.DOMBreakpointRemoved, breakpointRemoved);
WI.domDebuggerManager.removeDOMBreakpointsForNode(node);
})
.then(resolve, reject);
}
});
suite.addTestCase({
name: "SetBreakpointWithInvalidNodeId",
description: "Check that setting a breakpoint for a nonexistant node returns an error.",
teardown,
test(resolve, reject) {
InspectorTest.log("Attempting to set breakpoint.");
DOMDebuggerAgent.setDOMBreakpoint(0, WI.DOMBreakpoint.Type.SubtreeModified, (error) => {
InspectorTest.log("Protocol result: " + error);
InspectorTest.expectThat(error, "Protocol should return an error.")
resolve();
});
}
});
suite.addTestCase({
name: "SetBreakpointWithInvalidType",
description: "Check that setting a breakpoint with an invalid type returns an error.",
teardown,
test(resolve, reject) {
awaitQuerySelector("body")
.then((node) => {
InspectorTest.log("Attempting to set breakpoint.");
DOMDebuggerAgent.setDOMBreakpoint(node.id, "custom-breakpoint-type", (error) => {
InspectorTest.log("Protocol result: " + error);
InspectorTest.expectThat(error, "Protocol should return an error.")
resolve();
});
});
}
});
suite.addTestCase({
name: "RemoveBreakpointWithInvalidNodeId",
description: "Check that removing a breakpoint for a nonexistant node returns an error.",
teardown,
test(resolve, reject) {
InspectorTest.log("Attempting to remove breakpoint.");
DOMDebuggerAgent.removeDOMBreakpoint(0, WI.DOMBreakpoint.Type.SubtreeModified, (error) => {
InspectorTest.log("Protocol result: " + error);
InspectorTest.expectThat(error, "Protocol should return an error.")
resolve();
});
}
});
suite.addTestCase({
name: "RemoveBreakpointWithInvalidType",
description: "Check that removing a breakpoint with an invalid type returns an error.",
teardown,
test(resolve, reject) {
awaitQuerySelector("body")
.then((node) => {
InspectorTest.log("Attempting to remove breakpoint.");
DOMDebuggerAgent.removeDOMBreakpoint(node.id, "custom-breakpoint-type", (error) => {
InspectorTest.log("Protocol result: " + error);
InspectorTest.expectThat(error, "Protocol should return an error.")
resolve();
});
});
}
});
suite.runTestCasesAndFinish();
}
</script>
</head>
<body onload="runTest()">
<p>Tests for DOM breakpoints.</p>
<div id="test-container" style="display: none">
<div id="subtree-modified-test"></div>
<div id="attribute-modified-test"></div>
<div id="node-removed-direct-test"></div>
<div><div id="node-removed-ancestor-test"></div></div>
<div id="multiple-breakpoints-test"></div>
</div>
</body>
</html>