blob: 0b129ac99e12e6e4cd53dc5f293af3dec72a8ffb [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 nodeRemovedTest() {
let node = document.getElementById("node-removed-test");
let parent = node.parentNode;
node.remove();
parent.append(node);
}
function test()
{
const subtreeModifiedTestId = "subtree-modified-test";
const attributeModifiedTestId = "attribute-modified-test";
const nodeRemovedTestId = "node-removed-test";
const multipleBreakpointsTestId = "multiple-breakpoints-test";
let suite = InspectorTest.createAsyncSuite("DOMBreakpoints");
function teardown(resolve) {
let breakpoints = WI.domDebuggerManager.domBreakpoints;
for (let breakpoint of breakpoints)
WI.domDebuggerManager.removeDOMBreakpoint(breakpoint);
resolve();
}
function rejectOnPause() {
return new Promise((resolve, reject) => {
WI.debuggerManager.awaitEvent(WI.DebuggerManager.Event.Paused)
.then((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.domTreeManager.requestDocument((documentNode) => {
if (!documentNode) {
reject();
return;
}
WI.domTreeManager.querySelector(documentNode.id, selector, (nodeId) => {
if (!nodeId) {
InspectorTest.fail("Selector returned no nodes.", selector);
reject();
return;
}
let node = WI.domTreeManager.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 addSimpleTestCase({name, elementIdentifier, type, expression}) {
suite.addTestCase({
name: `BreakOn${name}`,
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.");
logActiveStackTrace();
return WI.debuggerManager.resume();
})
.then(resolve, 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.frameResourceManager.mainFrame, "Missing main frame.");
let documentURL = WI.frameResourceManager.mainFrame.url;
InspectorTest.expectEqual(breakpoint.url, documentURL, "Breakpoint URL should match document URL.");
})
.then(resolve, reject);
}
});
addSimpleTestCase({
name: "SubtreeModified",
elementIdentifier: subtreeModifiedTestId,
type: WI.DOMBreakpoint.Type.SubtreeModified,
expression: "subtreeModifiedTest()",
});
addSimpleTestCase({
name: "AttributeModified",
elementIdentifier: attributeModifiedTestId,
type: WI.DOMBreakpoint.Type.AttributeModified,
expression: "attributeModifiedTest()",
});
addSimpleTestCase({
name: "NodeRemoved",
elementIdentifier: nodeRemovedTestId,
type: WI.DOMBreakpoint.Type.NodeRemoved,
expression: "nodeRemovedTest()",
});
suite.addTestCase({
name: "ShouldNotPauseOnDisabledBreakpoint",
description: "Check that debugger does not pause for disabled breakpoint.",
teardown,
test(resolve, reject) {
const disabled = true;
addBreakpointForElementIdentifier(subtreeModifiedTestId, WI.DOMBreakpoint.Type.SubtreeModified, disabled)
.then(() => Promise.race([awaitEvaluateInPage("modifySubtreeTest()"), rejectOnPause()]))
.then(() => {
InspectorTest.pass("Should not pause for disabled breakpoint.");
resolve();
})
.catch(reject);
}
});
suite.addTestCase({
name: "RemoveBreakpoint",
description: "Check that debugger does not pause for removed breakpoint.",
teardown,
test(resolve, reject) {
addBreakpointForElementIdentifier(subtreeModifiedTestId, WI.DOMBreakpoint.Type.SubtreeModified)
.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("modifySubtreeTest()"), rejectOnPause()]))
.then(() => {
InspectorTest.pass("Should not pause for removed breakpoint.");
resolve();
})
.catch(reject);
}
});
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.domTreeManager.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-test"></div>
<div id="multiple-breakpoints-test"></div>
</div>
</body>
</html>