mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-01-27 18:37:53 +08:00
test(contenteditable): add in contenteditable list specs
This commit is contained in:
parent
40e143e3f2
commit
ced66902d5
6 changed files with 206 additions and 76 deletions
|
@ -14,6 +14,7 @@ exports.safeExec = function(command, options, callback) {
|
|||
// The default is 200KB.
|
||||
options.maxBuffer = 1024 * 1024;
|
||||
|
||||
options.stdio = "inherit"
|
||||
var child = childProcess.exec(command, options, function(error, stdout, stderr) {
|
||||
if (error && !options.ignoreStderr)
|
||||
process.exit(error.code || 1);
|
||||
|
@ -31,9 +32,8 @@ exports.safeSpawn = function(command, args, options, callback) {
|
|||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
options.stdio = "inherit"
|
||||
var child = childProcess.spawn(command, args, options);
|
||||
child.stderr.pipe(process.stderr);
|
||||
child.stdout.pipe(process.stdout);
|
||||
child.on('error', function(error) {
|
||||
console.error('Command \'' + command + '\' failed: ' + error.message);
|
||||
});
|
||||
|
|
29
spec_integration/bootstrap.js
vendored
29
spec_integration/bootstrap.js
vendored
|
@ -2,11 +2,38 @@
|
|||
// argv[1] = jasmine
|
||||
// argv[2] = JASMINE_CONFIG_PATH=./config.json
|
||||
// argv[3] = NYLAS_ROOT_PATH=/path/to/nylas/root
|
||||
|
||||
var babelOptions = require('../static/babelrc.json');
|
||||
require('babel-core/register')(babelOptions);
|
||||
|
||||
var chalk = require('chalk')
|
||||
var util = require('util')
|
||||
|
||||
console.errorColor = function(err){
|
||||
if (typeof err === "string") {
|
||||
console.error(chalk.red(err));
|
||||
} else {
|
||||
console.error(chalk.red(util.inspect(err)));
|
||||
}
|
||||
}
|
||||
|
||||
console.inspect = function(val) {
|
||||
console.log(util.inspect(val, true, depth=7, colorize=true));
|
||||
}
|
||||
|
||||
jasmine.NYLAS_ROOT_PATH = process.argv[3].split("NYLAS_ROOT_PATH=")[1]
|
||||
jasmine.UNIT_TEST_TIMEOUT = 120*1000;
|
||||
jasmine.BOOT_TIMEOUT = 30*1000;
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 30*1000
|
||||
|
||||
Promise = require('bluebird')
|
||||
Promise.config({
|
||||
warnings: true,
|
||||
longStackTraces: true,
|
||||
cancellation: true
|
||||
})
|
||||
|
||||
process.on("unhandledRejection", function(reason, promise) {
|
||||
if (reason.stack) { console.errorColor(reason.stack); }
|
||||
console.errorColor(promise);
|
||||
});
|
||||
|
||||
|
|
|
@ -1,18 +1,14 @@
|
|||
import Promise from 'bluebird'
|
||||
import {N1Launcher} from './integration-helper'
|
||||
import ContenteditableTestHarness from './contenteditable-test-harness.es6'
|
||||
|
||||
fdescribe('Contenteditable Integration Spec', function() {
|
||||
beforeAll((done)=>{
|
||||
console.log("----------- BEFORE ALL");
|
||||
// Boot in dev mode with no arguments
|
||||
this.app = new N1Launcher(["--dev"]);
|
||||
this.app.popoutComposerWindowReady().finally(done);
|
||||
});
|
||||
|
||||
beforeEach((done) => {
|
||||
console.log("----------- BEFORE EACH");
|
||||
this.ce = new ContenteditableTestHarness(this.app.client, expect)
|
||||
this.ce = new ContenteditableTestHarness(this.app.client)
|
||||
this.ce.init().finally(done);
|
||||
});
|
||||
|
||||
|
@ -24,51 +20,120 @@ fdescribe('Contenteditable Integration Spec', function() {
|
|||
}
|
||||
});
|
||||
|
||||
fit("Creates ordered lists", (done)=> {
|
||||
console.log("RUNNING KEYS");
|
||||
this.app.client.keys(["1", ".", "Space"]).then(()=>{
|
||||
console.log("DONE FIRING KEYS");
|
||||
e1 = this.ce.expectHTML("<ol><li>WOOO</li></ol>")
|
||||
e2 = this.ce.expectSelection((dom) => {
|
||||
return {node: dom.querySelectorAll("li")[0]}
|
||||
})
|
||||
return Promise.all(e1,e2)
|
||||
}).catch((err)=>{ console.log("XXXX ERROR"); console.log(err); }).finally(done)
|
||||
|
||||
|
||||
describe('Manipulating Lists', () => {
|
||||
it("Creates ordered lists", (done)=> {
|
||||
this.ce.test({
|
||||
keys: ["1", ".", "Space"],
|
||||
expectedHTML: "<ol><li></li></ol>",
|
||||
expectedSelectionResolver: (dom) => {
|
||||
return {node: dom.querySelectorAll('li')[0]} }
|
||||
}).then(done).catch(done.fail)
|
||||
});
|
||||
|
||||
it('Undoes ordered list creation with backspace', (done) => {
|
||||
this.ce.test({
|
||||
keys: ["1", ".", "Space", "Back space"],
|
||||
expectedHTML: "1. <br>",
|
||||
expectedSelectionResolver: (dom) => {
|
||||
return {node: dom.childNodes[0], offset: 3} }
|
||||
}).then(done).catch(done.fail)
|
||||
});
|
||||
|
||||
it("Creates unordered lists with star", (done) => {
|
||||
this.ce.test({
|
||||
keys: ['*', 'Space'],
|
||||
expectedHTML: "<ul><li></li></ul>",
|
||||
expectedSelectionResolver: (dom) => {
|
||||
return {node: dom.querySelectorAll("li")[0] } }
|
||||
}).then(done).catch(done.fail)
|
||||
});
|
||||
|
||||
it("Undoes unordered list creation with backspace", (done) => {
|
||||
this.ce.test({
|
||||
keys: ['*', 'Space', 'Back space'],
|
||||
expectedHTML: "* <br>",
|
||||
expectedSelectionResolver: (dom) => {
|
||||
return {node: dom.childNodes[0], offset: 2} }
|
||||
}).then(done).catch(done.fail)
|
||||
});
|
||||
|
||||
it("Creates unordered lists with dash", (done) => {
|
||||
this.ce.test({
|
||||
keys: ['-', 'Space'],
|
||||
expectedHTML: "<ul><li></li></ul>",
|
||||
expectedSelectionResolver: (dom) => {
|
||||
return {node: dom.querySelectorAll("li")[0] } }
|
||||
}).then(done).catch(done.fail)
|
||||
});
|
||||
|
||||
it("Undoes unordered list creation with backspace", (done) => {
|
||||
this.ce.test({
|
||||
keys: ['-', 'Space', 'Back space'],
|
||||
expectedHTML: "- <br>",
|
||||
expectedSelectionResolver: (dom) => {
|
||||
return {node: dom.childNodes[0], offset: 2} }
|
||||
}).then(done).catch(done.fail)
|
||||
});
|
||||
|
||||
// it("create a single item then delete it with backspace", (done) => {
|
||||
// this.ce.test({
|
||||
// keys: ['-', 'Space', 'a', 'Left arrow', 'Back space'],
|
||||
// expectedHTML: "<span style=\"line-height: 1.4;\">a</span><br>",
|
||||
// expectedSelectionResolver: (dom) => {
|
||||
// return {node: dom.childNodes[0], offset: 0} }
|
||||
// }).then(done).catch(done.fail)
|
||||
// });
|
||||
//
|
||||
// it("create a single item then delete it with tab", (done) => {
|
||||
// this.ce.test({
|
||||
// keys: ['-', 'Space', 'a', 'Shift', 'Tab'],
|
||||
// expectedHTML: "<span style=\"line-height: 1.4;\">a</span><br>",
|
||||
// expectedSelectionResolver: (dom) => {
|
||||
// return {node: dom.childNodes[0], offset: 1} }
|
||||
// }).then(done).catch(done.fail)
|
||||
// });
|
||||
|
||||
});
|
||||
|
||||
it("has main window visible", (done)=> {
|
||||
this.app.client.isWindowVisible()
|
||||
.then((result)=>{ expect(result).toBe(true) })
|
||||
.finally(done)
|
||||
});
|
||||
|
||||
it("has main window focused", (done)=> {
|
||||
this.app.client.isWindowFocused()
|
||||
.then((result)=>{ expect(result).toBe(true) })
|
||||
.finally(done)
|
||||
});
|
||||
|
||||
it("isn't minimized", (done)=> {
|
||||
this.app.client.isWindowMinimized()
|
||||
.then((result)=>{ expect(result).toBe(false) })
|
||||
.finally(done)
|
||||
});
|
||||
describe('Ensuring popout composer window works', () => {
|
||||
it("has main window visible", (done)=> {
|
||||
this.app.client.isWindowVisible()
|
||||
.then((result)=>{ expect(result).toBe(true) })
|
||||
.then(done).catch(done.fail)
|
||||
});
|
||||
|
||||
it("doesn't have the dev tools open", (done)=> {
|
||||
this.app.client.isWindowDevToolsOpened()
|
||||
.then((result)=>{ expect(result).toBe(false) })
|
||||
.finally(done)
|
||||
});
|
||||
it("has main window focused", (done)=> {
|
||||
this.app.client.isWindowFocused()
|
||||
.then((result)=>{ expect(result).toBe(true) })
|
||||
.then(done).catch(done.fail)
|
||||
});
|
||||
|
||||
it("has width", (done)=> {
|
||||
this.app.client.getWindowWidth()
|
||||
.then((result)=>{ expect(result).toBeGreaterThan(0) })
|
||||
.finally(done)
|
||||
});
|
||||
it("isn't minimized", (done)=> {
|
||||
this.app.client.isWindowMinimized()
|
||||
.then((result)=>{ expect(result).toBe(false) })
|
||||
.then(done).catch(done.fail)
|
||||
});
|
||||
|
||||
it("has height", (done)=> {
|
||||
this.app.client.getWindowHeight()
|
||||
.then((result)=>{ expect(result).toBeGreaterThan(0) })
|
||||
.finally(done)
|
||||
it("doesn't have the dev tools open", (done)=> {
|
||||
this.app.client.isWindowDevToolsOpened()
|
||||
.then((result)=>{ expect(result).toBe(false) })
|
||||
.then(done).catch(done.fail)
|
||||
});
|
||||
|
||||
it("has width", (done)=> {
|
||||
this.app.client.getWindowWidth()
|
||||
.then((result)=>{ expect(result).toBeGreaterThan(0) })
|
||||
.then(done).catch(done.fail)
|
||||
});
|
||||
|
||||
it("has height", (done)=> {
|
||||
this.app.client.getWindowHeight()
|
||||
.then((result)=>{ expect(result).toBeGreaterThan(0) })
|
||||
.then(done).catch(done.fail)
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,43 +1,40 @@
|
|||
import Promise from 'bluebird'
|
||||
|
||||
class ContenteditableTestHarness {
|
||||
|
||||
constructor(client, expect) {
|
||||
this.expect = expect
|
||||
constructor(client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
init() {
|
||||
console.log("INIT TEST HARNESS");
|
||||
return this.client.execute(() => {
|
||||
ce = document.querySelector(".contenteditable")
|
||||
ce.innerHTML = ""
|
||||
ce.focus()
|
||||
}).then(({value})=>{
|
||||
console.log(value);
|
||||
})
|
||||
}
|
||||
|
||||
test({keys, expectedHTML, expectedSelectionResolver}) {
|
||||
return this.client.keys(keys).then(()=>{
|
||||
return this.expectHTML(expectedHTML)
|
||||
}).then(()=>{
|
||||
return this.expectSelection(expectedSelectionResolver)
|
||||
})
|
||||
}
|
||||
|
||||
expectHTML(expectedHTML) {
|
||||
console.log("EXPECTING HTML");
|
||||
console.log(expectedHTML);
|
||||
return this.client.execute((expect, arg2) => {
|
||||
console.log(expect);
|
||||
console.log(arg2);
|
||||
ce = document.querySelector(".contenteditable")
|
||||
expect(ce.innerHTML).toBe(expectedHTML)
|
||||
return ce.innerHTML
|
||||
}, this.expect, "FOOO").then(({value})=>{
|
||||
console.log("GOT HTML VALUE");
|
||||
console.log(value);
|
||||
}).catch((err)=>{
|
||||
console.log("XXXXXXXXXX GOT ERROR")
|
||||
console.log(err);
|
||||
return this.client.execute(() => {
|
||||
return document.querySelector(".contenteditable").innerHTML
|
||||
}).then(({value})=>{
|
||||
expect(value).toBe(expectedHTML)
|
||||
})
|
||||
}
|
||||
|
||||
expectSelection(callback) {
|
||||
return this.client.execute(() => {
|
||||
// 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)
|
||||
|
||||
|
@ -48,10 +45,51 @@ class ContenteditableTestHarness {
|
|||
|
||||
selection = document.getSelection()
|
||||
|
||||
this.expect(selection.anchorNode).toBe(anchorNode)
|
||||
this.expect(selection.focusNode).toBe(focusNode)
|
||||
this.expect(selection.anchorOffset).toBe(anchorOffset)
|
||||
this.expect(selection.focusOffset).toBe(focusOffset)
|
||||
return {
|
||||
anchorNodeMatch: selection.anchorNode === anchorNode,
|
||||
focusNodeMatch: selection.focusNode === focusNode,
|
||||
anchorOffsetMatch: selection.anchorOffset === anchorOffset,
|
||||
focusOffsetMatch: selection.focusOffset === focusOffset,
|
||||
expectedAnchorNode: anchorNode.outerHTML,
|
||||
expectedFocusNode: focusNode.outerHTML,
|
||||
expectedAnchorOffset: anchorOffset,
|
||||
expectedFocusOffset: focusOffset,
|
||||
actualAnchorNode: selection.anchorNode.outerHTML,
|
||||
actualFocusNode: selection.focusNode.outerHTML,
|
||||
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);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import path from 'path'
|
||||
import Promise from 'bluebird'
|
||||
import {Application} from 'spectron';
|
||||
|
||||
class N1Launcher extends Application {
|
||||
|
@ -46,7 +45,7 @@ class N1Launcher extends Application {
|
|||
return NylasEnv.getLoadSettings().windowType;
|
||||
}).then(({value})=>{
|
||||
if(value === "composer") {
|
||||
return client.isWindowVisible()
|
||||
return client.isExisting(".contenteditable")
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
"bluebird": "^3.0.5",
|
||||
"babel-core": "^5.8.21",
|
||||
"jasmine": "^2.3.2",
|
||||
"spectron": "^0.34.1"
|
||||
"spectron": "^0.34.1",
|
||||
"chalk": "^1.1"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue