mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-11-14 13:44:41 +08:00
d18642fb3e
Summary: This provides a new API for `ContenteditableExtension`s. Instead of manually manipulating the raw DOM and `Selection` objects, there's a new `Editor` interface that encapsulates it and provides helper methods. You can now do: editor.select(someNode).createLink("foo").collapseToEnd() Now raw methods like `execCommand` ONLY show up in the `Editor` interface as well as most of the raw `Selection` APIs. There are also more integration tests :) Another major goal was cleaning up the contenteditable file itself. To that end: 1. The DOMNormalizer got pulled out into its own extension 1. The TabManager is now its own extension 1. Url wrangling got moved into the FloatingToolbar control 1. There is now the concept of a `ContenteditableService`, which are tightly-couple blocks of code separated out into logical units. These are dependent on the core state, innerState, and props and are not full extensions. 1. `MouseService` now handles all the click event logic 1. `ClipboardService` was modified to the new service architecture Test Plan: script/grunt run-integration-tests Reviewers: drew, juan, bengotow Reviewed By: juan, bengotow Differential Revision: https://phab.nylas.com/D2367
104 lines
3.6 KiB
JavaScript
104 lines
3.6 KiB
JavaScript
class ContenteditableTestHarness {
|
|
|
|
constructor(client) {
|
|
this.client = client;
|
|
}
|
|
|
|
init() {
|
|
return this.client.execute(() => {
|
|
ce = document.querySelector(".contenteditable")
|
|
ce.innerHTML = ""
|
|
ce.focus()
|
|
})
|
|
}
|
|
|
|
test({keys, expectedHTML, expectedSelectionResolver}) {
|
|
return this.client.keys(keys).then(()=>{
|
|
return this.expectHTML(expectedHTML)
|
|
}).then(()=>{
|
|
return this.expectSelection(expectedSelectionResolver)
|
|
})
|
|
}
|
|
|
|
expectHTML(expectedHTML) {
|
|
return this.client.execute(() => {
|
|
return document.querySelector(".contenteditable").innerHTML
|
|
}).then(({value})=>{
|
|
expect(value).toBe(expectedHTML)
|
|
})
|
|
}
|
|
|
|
expectSelection(callback) {
|
|
// Since `execute` fires parameters to Selenium via REST API, we can
|
|
// only pass strings. We serialize the callback so we can run it in
|
|
// the correct execution environment of the window instead of the
|
|
// Selenium wrapper.
|
|
return this.client.execute((callbackStr) => {
|
|
eval(`callback=${callbackStr}`);
|
|
ce = document.querySelector(".contenteditable")
|
|
expectSel = callback(ce)
|
|
|
|
anchorNode = expectSel.anchorNode || expectSel.node || "No anchorNode found"
|
|
focusNode = expectSel.focusNode || expectSel.node || "No focusNode found"
|
|
anchorOffset = expectSel.anchorOffset || expectSel.offset || 0
|
|
focusOffset = expectSel.focusOffset || expectSel.offset || 0
|
|
|
|
nodeData = (node) => {
|
|
if(node.nodeType === Node.TEXT_NODE) {
|
|
return node.data
|
|
} else {
|
|
return node.outerHTML
|
|
}
|
|
}
|
|
|
|
selection = document.getSelection()
|
|
|
|
return {
|
|
anchorNodeMatch: selection.anchorNode === anchorNode,
|
|
focusNodeMatch: selection.focusNode === focusNode,
|
|
anchorOffsetMatch: selection.anchorOffset === anchorOffset,
|
|
focusOffsetMatch: selection.focusOffset === focusOffset,
|
|
expectedAnchorNode: nodeData(anchorNode),
|
|
expectedFocusNode: nodeData(focusNode),
|
|
expectedAnchorOffset: anchorOffset,
|
|
expectedFocusOffset: focusOffset,
|
|
actualAnchorNode: nodeData(selection.anchorNode),
|
|
actualFocusNode: nodeData(selection.focusNode),
|
|
actualAnchorOffset: selection.anchorOffset,
|
|
actualFocusOffset: selection.focusOffset,
|
|
}
|
|
|
|
}, callback.toString()).then(({value}) => {
|
|
matchInfo = value
|
|
|
|
allMatched = true;
|
|
if (!matchInfo.anchorNodeMatch) {
|
|
console.errorColor("\nAnchor nodes don't match")
|
|
console.errorColor(`Expected: "${matchInfo.actualAnchorNode}" to be "${matchInfo.expectedAnchorNode}"`);
|
|
allMatched = false;
|
|
}
|
|
if (!matchInfo.focusNodeMatch) {
|
|
console.errorColor("\nFocus nodes don't match")
|
|
console.errorColor(`Expected: "${matchInfo.actualFocusNode}" to be "${matchInfo.expectedFocusNode}"`);
|
|
allMatched = false;
|
|
}
|
|
if (!matchInfo.anchorOffsetMatch) {
|
|
console.errorColor("\nAnchor offsets don't match")
|
|
console.errorColor(`Expected: ${matchInfo.actualAnchorOffset} to be ${matchInfo.expectedAnchorOffset}`);
|
|
allMatched = false;
|
|
}
|
|
if (!matchInfo.focusOffsetMatch) {
|
|
console.errorColor("\nFocus offsets don't match")
|
|
console.errorColor(`Expected: ${matchInfo.actualFocusOffset} to be ${matchInfo.expectedFocusOffset}`);
|
|
allMatched = false;
|
|
}
|
|
|
|
outMsgDescription = "matched. See discrepancies above"
|
|
if (allMatched) { outMsg = outMsgDescription
|
|
} else { outMsg = "Selection" }
|
|
// "Expected Selection to be matched. See discrepancies above"
|
|
expect(outMsg).toBe(outMsgDescription);
|
|
})
|
|
}
|
|
}
|
|
module.exports = ContenteditableTestHarness
|