add tests from origin/develop

This commit is contained in:
webzwo0i 2020-08-30 14:19:09 +02:00
parent 6a3e4c69b8
commit 2df2d7d60a
71 changed files with 18931 additions and 4032 deletions

View file

@ -1,11 +1,9 @@
var helper = {};
(function(){
var $iframeContainer, $iframe, jsLibraries = {};
var $iframe, jsLibraries = {};
helper.init = function(cb){
$iframeContainer = $("#iframe-container");
$.get('/static/js/jquery.js').done(function(code){
// make sure we don't override existing jquery
jsLibraries["jquery"] = "if(typeof $ === 'undefined') {\n" + code + "\n}";
@ -52,10 +50,49 @@ var helper = {};
return win.$;
}
helper.clearCookies = function(){
window.document.cookie = "";
helper.clearSessionCookies = function(){
// Expire cookies, so author and language are changed after reloading the pad.
// See https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie#Example_4_Reset_the_previous_cookie
window.document.cookie = 'token=;expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
window.document.cookie = 'language=;expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
}
// Can only happen when the iframe exists, so we're doing it separately from other cookies
helper.clearPadPrefCookie = function(){
helper.padChrome$.document.cookie = 'prefsHttp=;expires=Thu, 01 Jan 1970 00:00:00 GMT';
}
// Overwrite all prefs in pad cookie. Assumes http, not https.
//
// `helper.padChrome$.document.cookie` (the iframe) and `window.document.cookie`
// seem to have independent cookies, UNLESS we put path=/ here (which we don't).
// I don't fully understand it, but this function seems to properly simulate
// padCookie.setPref in the client code
helper.setPadPrefCookie = function(prefs){
helper.padChrome$.document.cookie = ("prefsHttp=" + escape(JSON.stringify(prefs)) + ";expires=Thu, 01 Jan 3000 00:00:00 GMT");
}
// Functionality for knowing what key event type is required for tests
var evtType = "keydown";
// if it's IE require keypress
if(window.navigator.userAgent.indexOf("MSIE") > -1){
evtType = "keypress";
}
// Edge also requires keypress.
if(window.navigator.userAgent.indexOf("Edge") > -1){
evtType = "keypress";
}
// Opera also requires keypress.
if(window.navigator.userAgent.indexOf("OPR") > -1){
evtType = "keypress";
}
helper.evtType = evtType;
// @todo needs fixing asap
// newPad occasionally timeouts, might be a problem with ready/onload code during page setup
// This ensures that tests run regardless of this problem
helper.retry = 0
helper.newPad = function(cb, padName){
//build opts object
var opts = {clearCookies: true}
@ -65,38 +102,56 @@ var helper = {};
opts = _.defaults(cb, opts);
}
// if opts.params is set we manipulate the URL to include URL parameters IE ?foo=Bah.
if(opts.params){
var encodedParams = "?" + $.param(opts.params);
}
//clear cookies
if(opts.clearCookies){
helper.clearCookies();
helper.clearSessionCookies();
}
if(!padName)
padName = "FRONTEND_TEST_" + helper.randomString(20);
$iframe = $("<iframe src='/p/" + padName + "'></iframe>");
$iframe = $("<iframe src='/p/" + padName + (encodedParams || '') + "'></iframe>");
// needed for retry
let origPadName = padName;
//clean up inner iframe references
helper.padChrome$ = helper.padOuter$ = helper.padInner$ = null;
//clean up iframes properly to prevent IE from memoryleaking
$iframeContainer.find("iframe").purgeFrame().done(function(){
$iframeContainer.append($iframe);
$iframe.one('load', function(){
helper.waitFor(function(){
return !$iframe.contents().find("#editorloadingbox").is(":visible");
}, 50000).done(function(){
helper.padChrome$ = getFrameJQuery( $('#iframe-container iframe'));
helper.padOuter$ = getFrameJQuery(helper.padChrome$('iframe[name="ace_outer"]'));
helper.padInner$ = getFrameJQuery( helper.padOuter$('iframe[name="ace_inner"]'));
//remove old iframe
$("#iframe-container iframe").remove();
//set new iframe
$("#iframe-container").append($iframe);
$iframe.one('load', function(){
helper.padChrome$ = getFrameJQuery($('#iframe-container iframe'));
if (opts.clearCookies) {
helper.clearPadPrefCookie();
}
if (opts.padPrefs) {
helper.setPadPrefCookie(opts.padPrefs);
}
helper.waitFor(function(){
return !$iframe.contents().find("#editorloadingbox").is(":visible");
}, 10000).done(function(){
helper.padOuter$ = getFrameJQuery(helper.padChrome$('iframe[name="ace_outer"]'));
helper.padInner$ = getFrameJQuery( helper.padOuter$('iframe[name="ace_inner"]'));
//disable all animations, this makes tests faster and easier
helper.padChrome$.fx.off = true;
helper.padOuter$.fx.off = true;
helper.padInner$.fx.off = true;
//disable all animations, this makes tests faster and easier
helper.padChrome$.fx.off = true;
helper.padOuter$.fx.off = true;
helper.padInner$.fx.off = true;
opts.cb();
}).fail(function(){
opts.cb();
}).fail(function(){
if (helper.retry > 3) {
throw new Error("Pad never loaded");
});
}
helper.retry++;
helper.newPad(cb,origPadName);
});
});
@ -104,7 +159,7 @@ var helper = {};
}
helper.waitFor = function(conditionFunc, _timeoutTime, _intervalTime){
var timeoutTime = _timeoutTime || 1000;
var timeoutTime = _timeoutTime || 1900;
var intervalTime = _intervalTime || 10;
var deferred = $.Deferred();
@ -216,4 +271,4 @@ var helper = {};
_it(name, func);
}
})()
})()

View file

@ -14,11 +14,10 @@
<script src="lib/underscore.js"></script>
<script src="lib/mocha.js"></script>
<script> mocha.setup('bdd') </script>
<script> mocha.setup({ui: 'bdd', ignoreLeaks: true}) </script>
<script src="lib/expect.js"></script>
<script src="lib/sendkeys.js"></script>
<script src="lib/jquery.iframe.js"></script>
<script src="helper.js"></script>
<script src="specs_list.js"></script>

View file

@ -65,7 +65,7 @@
var name = $flags[i]
, assertion = new Assertion(this.obj, name, this)
if ('function' == typeof Assertion.prototype[name]) {
// clone the function, make sure we dont touch the prot reference
var old = this[name];
@ -148,7 +148,7 @@
if ('object' == typeof fn && not) {
// in the presence of a matcher, ensure the `not` only applies to
// the matching.
this.flags.not = false;
this.flags.not = false;
}
var name = this.obj.name || 'fn';
@ -219,7 +219,7 @@
};
/**
* Assert within start to finish (inclusive).
* Assert within start to finish (inclusive).
*
* @param {Number} start
* @param {Number} finish
@ -298,7 +298,7 @@
, function(){ return 'expected ' + i(this.obj) + ' to be above ' + n });
return this;
};
/**
* Assert string value matches _regexp_.
*
@ -359,13 +359,13 @@
} catch (e) {
hasProp = undefined !== this.obj[name]
}
this.assert(
hasProp
, function(){ return 'expected ' + i(this.obj) + ' to have a property ' + i(name) }
, function(){ return 'expected ' + i(this.obj) + ' to not have a property ' + i(name) });
}
if (undefined !== val) {
this.assert(
val === this.obj[name]
@ -537,7 +537,7 @@
return html;
}
};
// Returns true if object is a DOM element.
var isDOMElement = function (object) {
if (typeof HTMLElement === 'object') {
@ -843,9 +843,9 @@
expect.eql = function eql (actual, expected) {
// 7.1. All identical values are equivalent, as determined by ===.
if (actual === expected) {
if (actual === expected) {
return true;
} else if ('undefined' != typeof Buffer
} else if ('undefined' != typeof Buffer
&& Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) {
if (actual.length != expected.length) return false;

View file

@ -1,40 +0,0 @@
//copied from http://stackoverflow.com/questions/8407946/is-it-possible-to-use-iframes-in-ie-without-memory-leaks
(function($) {
$.fn.purgeFrame = function() {
var deferred;
var browser = bowser;
if (browser.msie && parseFloat(browser.version, 10) < 9) {
deferred = purge(this);
} else {
this.remove();
deferred = $.Deferred();
deferred.resolve();
}
return deferred;
};
function purge($frame) {
var sem = $frame.length
, deferred = $.Deferred();
$frame.load(function() {
var frame = this;
frame.contentWindow.document.innerHTML = '';
sem -= 1;
if (sem <= 0) {
$frame.remove();
deferred.resolve();
}
});
$frame.attr('src', 'about:blank');
if ($frame.length === 0) {
deferred.resolve();
}
return deferred.promise();
}
})(jQuery);

File diff suppressed because one or more lines are too long

View file

@ -305,11 +305,11 @@ var START_TO_END = 1;
var END_TO_END = 2;
var END_TO_START = 3;
// from the Mozilla documentation, for range.compareBoundaryPoints(how, sourceRange)
// -1, 0, or 1, indicating whether the corresponding boundary-point of range is respectively before, equal to, or after the corresponding boundary-point of sourceRange.
// -1, 0, or 1, indicating whether the corresponding boundary-point of range is respectively before, equal to, or after the corresponding boundary-point of sourceRange.
// * Range.END_TO_END compares the end boundary-point of sourceRange to the end boundary-point of range.
// * Range.END_TO_START compares the end boundary-point of sourceRange to the start boundary-point of range.
// * Range.START_TO_END compares the start boundary-point of sourceRange to the end boundary-point of range.
// * Range.START_TO_START compares the start boundary-point of sourceRange to the start boundary-point of range.
// * Range.START_TO_START compares the start boundary-point of sourceRange to the start boundary-point of range.
function w3cstart(rng, constraint){
if (rng.compareBoundaryPoints (START_TO_START, constraint) <= 0) return 0; // at or before the beginning
if (rng.compareBoundaryPoints (END_TO_START, constraint) >= 0) return constraint.toString().length;

View file

@ -1,11 +1,14 @@
html {
height: 100%;
}
}
body {
padding: 0px;
margin: 0px;
height: 100%;
display: flex;
flex-direction: row;
overflow: hidden;
}
#console {
@ -13,34 +16,34 @@ body {
}
#iframe-container {
width: 50%;
width: 80%;
min-width: 820px;
height: 100%;
}
#iframe-container iframe {
height: 100%;
position:absolute;
min-width:500px;
max-width:800px;
left:50%;
width:100%;
}
#mocha {
font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
border-right: 2px solid #999;
width: 50%;
flex: 1 auto;
height: 100%;
position: absolute;
overflow: auto;
float:left;
width:20%;
font-size:80%;
}
#mocha #report {
margin-top: 50px;
margin: 0;
padding: 0;
margin-top: 10px;
}
#mocha ul, #mocha li {
#mocha li {
margin: 0;
padding: 0;
}
@ -60,7 +63,7 @@ body {
}
#mocha h1 a:visited
{
{
color: #00E;
}
@ -76,11 +79,11 @@ body {
}
#mocha .suite {
margin-left: 15px;
margin-left: 0px;
}
#mocha .test {
margin-left: 15px;
margin-left: 5px;
}
#mocha .test:hover h2::after {
@ -175,6 +178,10 @@ body {
-webkit-box-shadow: 0 1px 3px #eee;
}
#report ul {
padding: 0;
}
#report.pass .test.fail {
display: none;
}
@ -191,17 +198,21 @@ body {
}
#stats {
position: fixed;
top: 15px;
right: 52%;
padding: 10px;
font-size: 12px;
margin: 0;
color: #888;
text-align: right;
}
#stats .progress {
#mocha-stats {
height: 80px;
}
#mocha-stats .progress {
float: right;
padding-top: 0;
margin-right:5px;
}
#stats em {
@ -229,3 +240,7 @@ code .init { color: #2F6FAD }
code .string { color: #5890AD }
code .keyword { color: #8A6343 }
code .number { color: #2F6FAD }
ul{
padding-left:5px;
}

View file

@ -1,28 +1,68 @@
$(function(){
function Base(runner) {
var self = this
, stats = this.stats = { suites: 0, tests: 0, passes: 0, pending: 0, failures: 0 }
, failures = this.failures = [];
function stringifyException(exception){
var err = exception.stack || exception.toString();
// FF / Opera do not add the message
if (!~err.indexOf(exception.message)) {
err = exception.message + '\n' + err;
}
// <=IE7 stringifies to [Object Error]. Since it can be overloaded, we
// check for the result of the stringifying.
if ('[object Error]' == err) err = exception.message;
// Safari doesn't give you a stack. Let's at least provide a source line.
if (!exception.stack && exception.sourceURL && exception.line !== undefined) {
err += "\n(" + exception.sourceURL + ":" + exception.line + ")";
}
return err;
}
function CustomRunner(runner) {
var stats = { suites: 0, tests: 0, passes: 0, pending: 0, failures: 0 };
if (!runner) return;
this.runner = runner;
runner.on('start', function(){
stats.start = new Date;
});
runner.on('suite', function(suite){
stats.suites = stats.suites || 0;
suite.root || stats.suites++;
if (suite.root) return;
append(suite.title);
level++;
});
runner.on('test end', function(test){
stats.tests = stats.tests || 0;
runner.on('suite end', function(suite){
if (suite.root) return;
level--;
if(level == 0) {
append("");
}
});
// Scroll down test display after each test
let mochaEl = $('#mocha')[0];
runner.on('test', function(){
mochaEl.scrollTop = mochaEl.scrollHeight;
});
// max time a test is allowed to run
// TODO this should be lowered once timeslider_revision.js is faster
var killTimeout;
runner.on('test end', function(){
stats.tests++;
});
runner.on('pass', function(test){
stats.passes = stats.passes || 0;
if(killTimeout) clearTimeout(killTimeout);
killTimeout = setTimeout(function(){
append("FINISHED - [red]no test started since 3 minutes, tests stopped[clear]");
}, 60000 * 3);
var medium = test.slow() / 2;
test.speed = test.duration > test.slow()
@ -32,43 +72,31 @@ $(function(){
: 'fast';
stats.passes++;
append("->","[green]PASSED[clear] :", test.title," ",test.duration,"ms");
});
runner.on('fail', function(test, err){
stats.failures = stats.failures || 0;
if(killTimeout) clearTimeout(killTimeout);
killTimeout = setTimeout(function(){
append("FINISHED - [red]no test started since 3 minutes, tests stopped[clear]");
}, 60000 * 3);
stats.failures++;
test.err = err;
failures.push(test);
append("->","[red]FAILED[clear] :", test.title, stringifyException(test.err));
});
runner.on('end', function(){
stats.end = new Date;
stats.duration = new Date - stats.start;
});
runner.on('pending', function(test){
if(killTimeout) clearTimeout(killTimeout);
killTimeout = setTimeout(function(){
append("FINISHED - [red]no test started since 3 minutes, tests stopped[clear]");
}, 60000 * 3);
runner.on('pending', function(){
stats.pending++;
append("->","[yellow]PENDING[clear]:", test.title);
});
}
/*
This reporter wraps the original html reporter plus reports plain text into a hidden div.
This allows the webdriver client to pick up the test results
*/
var WebdriverAndHtmlReporter = function(html_reporter){
return function(runner){
Base.call(this, runner);
// Scroll down test display after each test
mocha = $('#mocha')[0];
runner.on('test', function(){
mocha.scrollTop = mocha.scrollHeight;
});
//initalize the html reporter first
html_reporter(runner);
var $console = $("#console");
var $console = $("#console");
var level = 0;
var append = function(){
var text = Array.prototype.join.apply(arguments, [" "]);
@ -97,68 +125,23 @@ $(function(){
$console.text(oldText + newText + "\\n");
}
runner.on('suite', function(suite){
if (suite.root) return;
append(suite.title);
level++;
});
runner.on('suite end', function(suite){
if (suite.root) return;
level--;
if(level == 0) {
append("");
}
});
var stringifyException = function(exception){
var err = exception.stack || exception.toString();
// FF / Opera do not add the message
if (!~err.indexOf(exception.message)) {
err = exception.message + '\n' + err;
}
// <=IE7 stringifies to [Object Error]. Since it can be overloaded, we
// check for the result of the stringifying.
if ('[object Error]' == err) err = exception.message;
// Safari doesn't give you a stack. Let's at least provide a source line.
if (!exception.stack && exception.sourceURL && exception.line !== undefined) {
err += "\n(" + exception.sourceURL + ":" + exception.line + ")";
}
return err;
}
var killTimeout;
runner.on('test end', function(test){
if ('passed' == test.state) {
append("->","[green]PASSED[clear] :", test.title);
} else if (test.pending) {
append("->","[yellow]PENDING[clear]:", test.title);
} else {
append("->","[red]FAILED[clear] :", test.title, stringifyException(test.err));
}
if(killTimeout) clearTimeout(killTimeout);
killTimeout = setTimeout(function(){
append("FINISHED - [red]no test started since 3 minutes, tests stopped[clear]");
}, 60000 * 3);
});
var total = runner.total;
runner.on('end', function(){
if(stats.tests >= total){
var minutes = Math.floor(stats.duration / 1000 / 60);
var seconds = Math.round((stats.duration / 1000) % 60);
append("FINISHED -", stats.passes, "tests passed,", stats.failures, "tests failed, duration: " + minutes + ":" + seconds);
stats.end = new Date;
stats.duration = stats.end - stats.start;
var minutes = Math.floor(stats.duration / 1000 / 60);
var seconds = Math.round((stats.duration / 1000) % 60) // chrome < 57 does not like this .toString().padStart("2","0");
if(stats.tests === total){
append("FINISHED -", stats.passes, "tests passed,", stats.failures, "tests failed,", stats.pending," pending, duration: " + minutes + ":" + seconds);
} else if (stats.tests > total) {
append("FINISHED - but more tests than planned returned", stats.passes, "tests passed,", stats.failures, "tests failed,", stats.pending," pending, duration: " + minutes + ":" + seconds);
append(total,"tests, but",stats.tests,"returned. There is probably a problem with your async code or error handling, see https://github.com/mochajs/mocha/issues/1327");
}
else {
append("FINISHED - but not all tests returned", stats.passes, "tests passed,", stats.failures, "tests failed,", stats.pending, "tests pending, duration: " + minutes + ":" + seconds);
append(total,"tests, but only",stats.tests,"returned. Check for failed before/beforeEach-hooks (no `test end` is called for them and subsequent tests of the same suite are skipped), see https://github.com/mochajs/mocha/pull/1043");
}
});
}
}
//http://stackoverflow.com/questions/1403888/get-url-parameter-with-jquery
@ -170,7 +153,7 @@ $(function(){
//get the list of specs and filter it if requested
var specs = specs_list.slice();
//inject spec scripts into the dom
var $body = $('body');
$.each(specs, function(i, spec){
@ -189,10 +172,7 @@ $(function(){
mocha.grep(grep);
}
mocha.ignoreLeaks();
mocha.reporter(WebdriverAndHtmlReporter(mocha._reporter));
mocha.run();
var runner = mocha.run();
CustomRunner(runner)
});
});
});

View file

@ -8,12 +8,12 @@ describe("All the alphabet works n stuff", function(){
});
it("when you enter any char it appears right", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe
var firstTextElement = inner$("div").first();
// simulate key presses to delete content
firstTextElement.sendkeys('{selectall}'); // select all
firstTextElement.sendkeys('{del}'); // clear the first line

View file

@ -6,22 +6,22 @@ describe("bold button", function(){
});
it("makes text bold on click", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first();
//select this text element
$firstTextElement.sendkeys('{selectall}');
//get the bold button and click it
var $boldButton = chrome$(".buttonicon-bold");
$boldButton.click();
//ace creates a new dom element when you press a button, so just get the first text element again
var $newFirstTextElement = inner$("div").first();
// is there a <b> element now?
var isBold = $newFirstTextElement.find("b").length === 1;
@ -44,13 +44,7 @@ describe("bold button", function(){
//select this text element
$firstTextElement.sendkeys('{selectall}');
if(inner$(window)[0].bowser.modernIE){ // if it's IE
var evtType = "keypress";
}else{
var evtType = "keydown";
}
var e = inner$.Event(evtType);
var e = inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key
e.which = 66; // b
inner$("#innerdocbody").trigger(e);

View file

@ -198,9 +198,9 @@ console.log(inner$);
/*
it("Creates N rows, changes height of rows, updates UI by caret key events", function(done){
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var chrome$ = helper.padChrome$;
var numberOfRows = 50;
//ace creates a new dom element when you press a keystroke, so just get the first text element again
var $newFirstTextElement = inner$("div").first();
var originalDivHeight = inner$("div").first().css("height");
@ -211,7 +211,7 @@ console.log(inner$);
}).done(function(){ // Once the DOM has registered the items
inner$("div").each(function(index){ // Randomize the item heights (replicates images / headings etc)
var random = Math.floor(Math.random() * (50)) + 20;
$(this).css("height", random+"px");
$(this).css("height", random+"px");
});
console.log(caretPosition(inner$));
@ -253,7 +253,7 @@ console.log(inner$);
keyEvent(inner$, 33, false, false); // doesn't work
i++;
}
// Does scrolling back up the pad with the up arrow show the correct contents?
helper.waitFor(function(){ // Wait for the new position to be in place
try{
@ -280,7 +280,7 @@ console.log(inner$);
helper.waitFor(function(){ // Wait for the new position to be in place
return isScrolledIntoView(inner$("div:nth-child(1)"), inner$); // Wait for the DOM to scroll into place
}).done(function(){ // Once the DOM has registered the items
expect(true).to.be(true);
expect(true).to.be(true);
done();
});
*/
@ -297,20 +297,15 @@ function prepareDocument(n, target){ // generates a random document with random
}
function keyEvent(target, charCode, ctrl, shift){ // sends a charCode to the window
if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE
var evtType = "keypress";
}else{
var evtType = "keydown";
}
var e = target.Event(evtType);
console.log(e);
var e = target.Event(helper.evtType);
if(ctrl){
e.ctrlKey = true; // Control key
}
if(shift){
e.shiftKey = true; // Shift Key
}
e.which = charCode;
e.which = charCode;
e.keyCode = charCode;
target("#innerdocbody").trigger(e);
}
@ -339,6 +334,5 @@ function caretPosition($){
var pos = doc.getSelection();
pos.y = pos.anchorNode.parentElement.offsetTop;
pos.x = pos.anchorNode.parentElement.offsetLeft;
console.log(pos);
return pos;
}

View file

@ -0,0 +1,104 @@
describe("change user color", function(){
//create a new pad before each test run
beforeEach(function(cb){
helper.newPad(cb);
this.timeout(60000);
});
it("Color picker matches original color and remembers the user color after a refresh", function(done) {
this.timeout(60000);
var chrome$ = helper.padChrome$;
//click on the settings button to make settings visible
var $userButton = chrome$(".buttonicon-showusers");
$userButton.click();
var $userSwatch = chrome$("#myswatch");
$userSwatch.click();
var fb = chrome$.farbtastic('#colorpicker')
var $colorPickerSave = chrome$("#mycolorpickersave");
var $colorPickerPreview = chrome$("#mycolorpickerpreview");
// Same color represented in two different ways
const testColorHash = '#abcdef'
const testColorRGB = 'rgb(171, 205, 239)'
// Check that the color picker matches the automatically assigned random color on the swatch.
// NOTE: This has a tiny chance of creating a false positive for passing in the
// off-chance the randomly assigned color is the same as the test color.
expect($colorPickerPreview.css('background-color')).to.be($userSwatch.css('background-color'))
// The swatch updates as the test color is picked.
fb.setColor(testColorHash)
expect($colorPickerPreview.css('background-color')).to.be(testColorRGB)
$colorPickerSave.click();
expect($userSwatch.css('background-color')).to.be(testColorRGB)
setTimeout(function(){ //give it a second to save the color on the server side
helper.newPad({ // get a new pad, but don't clear the cookies
clearCookies: false
, cb: function(){
var chrome$ = helper.padChrome$;
//click on the settings button to make settings visible
var $userButton = chrome$(".buttonicon-showusers");
$userButton.click();
var $userSwatch = chrome$("#myswatch");
$userSwatch.click();
var $colorPickerPreview = chrome$("#mycolorpickerpreview");
expect($colorPickerPreview.css('background-color')).to.be(testColorRGB)
expect($userSwatch.css('background-color')).to.be(testColorRGB)
done();
}
});
}, 1000);
});
it("Own user color is shown when you enter a chat", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var $colorOption = helper.padChrome$('#options-colorscheck');
if (!$colorOption.is(':checked')) {
$colorOption.click();
}
//click on the settings button to make settings visible
var $userButton = chrome$(".buttonicon-showusers");
$userButton.click();
var $userSwatch = chrome$("#myswatch");
$userSwatch.click();
var fb = chrome$.farbtastic('#colorpicker')
var $colorPickerSave = chrome$("#mycolorpickersave");
// Same color represented in two different ways
const testColorHash = '#abcdef'
const testColorRGB = 'rgb(171, 205, 239)'
fb.setColor(testColorHash)
$colorPickerSave.click();
//click on the chat button to make chat visible
var $chatButton = chrome$("#chaticon");
$chatButton.click();
var $chatInput = chrome$("#chatinput");
$chatInput.sendkeys('O hi'); // simulate a keypress of typing user
$chatInput.sendkeys('{enter}'); // simulate a keypress of enter actually does evt.which = 10 not 13
//check if chat shows up
helper.waitFor(function(){
return chrome$("#chattext").children("p").length !== 0; // wait until the chat message shows up
}).done(function(){
var $firstChatMessage = chrome$("#chattext").children("p");
expect($firstChatMessage.css('background-color')).to.be(testColorRGB); // expect the first chat message to be of the user's color
done();
});
});
});

View file

@ -12,7 +12,7 @@ describe("change username value", function(){
//click on the settings button to make settings visible
var $userButton = chrome$(".buttonicon-showusers");
$userButton.click();
var $usernameInput = chrome$("#myusernameedit");
$usernameInput.click();
@ -45,7 +45,7 @@ describe("change username value", function(){
//click on the settings button to make settings visible
var $userButton = chrome$(".buttonicon-showusers");
$userButton.click();
var $usernameInput = chrome$("#myusernameedit");
$usernameInput.click();

View file

@ -6,8 +6,8 @@ describe("Chat messages and UI", function(){
});
it("opens chat, sends a message and makes sure it exists on the page", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var chatValue = "JohnMcLear";
//click on the chat button to make chat visible
@ -39,8 +39,8 @@ describe("Chat messages and UI", function(){
});
it("makes sure that an empty message can't be sent", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
//click on the chat button to make chat visible
var $chatButton = chrome$("#chaticon");
@ -65,8 +65,8 @@ describe("Chat messages and UI", function(){
});
it("makes chat stick to right side of the screen", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
//click on the settings button to make settings visible
var $settingsButton = chrome$(".buttonicon-settings");
@ -75,56 +75,96 @@ describe("Chat messages and UI", function(){
//get the chat selector
var $stickychatCheckbox = chrome$("#options-stickychat");
//select chat always on screen and fire change event
$stickychatCheckbox.attr('selected','selected');
$stickychatCheckbox.change();
$stickychatCheckbox.click();
//select chat always on screen
if (!$stickychatCheckbox.is(':checked')) {
$stickychatCheckbox.click();
}
//check if chat changed to get the stickychat Class
var $chatbox = chrome$("#chatbox");
var hasStickyChatClass = $chatbox.hasClass("stickyChat");
expect(hasStickyChatClass).to.be(true);
// due to animation, we need to make some timeout...
setTimeout(function() {
//check if chat changed to get the stickychat Class
var $chatbox = chrome$("#chatbox");
var hasStickyChatClass = $chatbox.hasClass("stickyChat");
expect(hasStickyChatClass).to.be(true);
//select chat always on screen and fire change event
$stickychatCheckbox.attr('selected','selected');
$stickychatCheckbox.change();
$stickychatCheckbox.click();
// select chat always on screen and fire change event
$stickychatCheckbox.click();
setTimeout(function() {
//check if chat changed to remove the stickychat Class
var hasStickyChatClass = $chatbox.hasClass("stickyChat");
expect(hasStickyChatClass).to.be(false);
done();
}, 10)
}, 10)
//check if chat changed to remove the stickychat Class
var hasStickyChatClass = $chatbox.hasClass("stickyChat");
expect(hasStickyChatClass).to.be(false);
done();
});
it("makes chat stick to right side of the screen then makes it one step smaller", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
//click on the settings button to make settings visible
var $settingsButton = chrome$(".buttonicon-settings");
$settingsButton.click();
// open chat
chrome$('#chaticon').click();
//get the chat selector
var $stickychatCheckbox = chrome$("#options-stickychat");
// select chat always on screen from chatbox
chrome$('.stick-to-screen-btn').click();
//select chat always on screen and fire change event
$stickychatCheckbox.attr('selected','selected');
$stickychatCheckbox.change();
$stickychatCheckbox.click();
// due to animation, we need to make some timeout...
setTimeout(function() {
//check if chat changed to get the stickychat Class
var $chatbox = chrome$("#chatbox");
var hasStickyChatClass = $chatbox.hasClass("stickyChat");
expect(hasStickyChatClass).to.be(true);
//check if chat changed to get the stickychat Class
var $chatbox = chrome$("#chatbox");
var hasStickyChatClass = $chatbox.hasClass("stickyChat");
expect(hasStickyChatClass).to.be(true);
// select chat always on screen and fire change event
chrome$('#titlecross').click();
//select chat always on screen and fire change event
chrome$('#titlecross').click();
setTimeout(function() {
//check if chat changed to remove the stickychat Class
var hasStickyChatClass = $chatbox.hasClass("stickyChat");
expect(hasStickyChatClass).to.be(false);
//check if chat changed to remove the stickychat Class
var hasStickyChatClass = $chatbox.hasClass("stickyChat");
expect(hasStickyChatClass).to.be(false);
done();
done();
}, 10)
}, 10)
});
xit("Checks showChat=false URL Parameter hides chat then when removed it shows chat", function(done) {
this.timeout(60000);
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
setTimeout(function(){ //give it a second to save the username on the server side
helper.newPad({ // get a new pad, but don't clear the cookies
clearCookies: false,
params:{
showChat: "false"
}, cb: function(){
var chrome$ = helper.padChrome$;
var chaticon = chrome$("#chaticon");
// chat should be hidden.
expect(chaticon.is(":visible")).to.be(false);
setTimeout(function(){ //give it a second to save the username on the server side
helper.newPad({ // get a new pad, but don't clear the cookies
clearCookies: false
, cb: function(){
var chrome$ = helper.padChrome$;
var chaticon = chrome$("#chaticon");
// chat should be visible.
expect(chaticon.is(":visible")).to.be(true);
done();
}
});
}, 1000);
}
});
}, 1000);
});
});

View file

@ -1,21 +1,21 @@
describe("chat-load-messages", function(){
var padName;
it("creates a pad", function(done) {
padName = helper.newPad(done);
this.timeout(60000);
});
it("adds a lot of messages", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var chatButton = chrome$("#chaticon");
chatButton.click();
var chatInput = chrome$("#chatinput");
var chatText = chrome$("#chattext");
this.timeout(60000);
var messages = 140;
for(var i=1; i <= messages; i++) {
var num = ''+i;
@ -33,7 +33,7 @@ describe("chat-load-messages", function(){
helper.newPad(done, padName);
});
});
it("checks initial message count", function(done) {
var chatText;
var expectedCount = 101;
@ -48,7 +48,7 @@ describe("chat-load-messages", function(){
done();
});
});
it("loads more messages", function(done) {
var expectedCount = 122;
var chrome$ = helper.padChrome$;
@ -56,7 +56,7 @@ describe("chat-load-messages", function(){
chatButton.click();
var chatText = chrome$("#chattext");
var loadMsgBtn = chrome$("#chatloadmessagesbutton");
loadMsgBtn.click();
helper.waitFor(function(){
return chatText.children("p").length == expectedCount;
@ -65,7 +65,7 @@ describe("chat-load-messages", function(){
done();
});
});
it("checks for button vanishing", function(done) {
var expectedDisplay = 'none';
var chrome$ = helper.padChrome$;

View file

@ -6,8 +6,8 @@ describe("clear authorship colors button", function(){
});
it("makes text clear authorship colors", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
// override the confirm dialogue functioon
helper.padChrome$.window.confirm = function(){
@ -19,7 +19,7 @@ describe("clear authorship colors button", function(){
// Get the original text
var originalText = inner$("div").first().text();
// Set some new text
var sentText = "Hello";
@ -39,7 +39,6 @@ describe("clear authorship colors button", function(){
$clearauthorshipcolorsButton.click();
// does the first divs span include an author class?
console.log(inner$("div span").first().attr("class"));
var hasAuthorClass = inner$("div span").first().attr("class").indexOf("author") !== -1;
//expect(hasAuthorClass).to.be(false);
@ -47,13 +46,88 @@ describe("clear authorship colors button", function(){
var hasAuthorClass = inner$("div").first().attr("class").indexOf("author") !== -1;
expect(hasAuthorClass).to.be(false);
setTimeout(function(){
helper.waitFor(function(){
var disconnectVisible = chrome$("div.disconnected").attr("class").indexOf("visible") === -1
expect(disconnectVisible).to.be(true);
},1000);
return (disconnectVisible === true)
});
var disconnectVisible = chrome$("div.disconnected").attr("class").indexOf("visible") === -1
expect(disconnectVisible).to.be(true);
done();
});
});
it("makes text clear authorship colors and checks it can't be undone", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
// override the confirm dialogue functioon
helper.padChrome$.window.confirm = function(){
return true;
}
//get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first();
// Get the original text
var originalText = inner$("div").first().text();
// Set some new text
var sentText = "Hello";
//select this text element
$firstTextElement.sendkeys('{selectall}');
$firstTextElement.sendkeys(sentText);
$firstTextElement.sendkeys('{rightarrow}');
helper.waitFor(function(){
return inner$("div span").first().attr("class").indexOf("author") !== -1; // wait until we have the full value available
}).done(function(){
//IE hates you if you don't give focus to the inner frame bevore you do a clearAuthorship
inner$("div").first().focus();
//get the clear authorship colors button and click it
var $clearauthorshipcolorsButton = chrome$(".buttonicon-clearauthorship");
$clearauthorshipcolorsButton.click();
// does the first divs span include an author class?
var hasAuthorClass = inner$("div span").first().attr("class").indexOf("author") !== -1;
//expect(hasAuthorClass).to.be(false);
// does the first div include an author class?
var hasAuthorClass = inner$("div").first().attr("class").indexOf("author") !== -1;
expect(hasAuthorClass).to.be(false);
var e = inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key
e.which = 90; // z
inner$("#innerdocbody").trigger(e); // shouldn't od anything
// does the first div include an author class?
hasAuthorClass = inner$("div").first().attr("class").indexOf("author") !== -1;
expect(hasAuthorClass).to.be(false);
// get undo and redo buttons
var $undoButton = chrome$(".buttonicon-undo");
// click the button
$undoButton.click(); // shouldn't do anything
hasAuthorClass = inner$("div").first().attr("class").indexOf("author") !== -1;
expect(hasAuthorClass).to.be(false);
helper.waitFor(function(){
var disconnectVisible = chrome$("div.disconnected").attr("class").indexOf("visible") === -1
return (disconnectVisible === true)
});
var disconnectVisible = chrome$("div.disconnected").attr("class").indexOf("visible") === -1
expect(disconnectVisible).to.be(true);
done();
});
});
});

View file

@ -6,12 +6,12 @@ describe("delete keystroke", function(){
});
it("makes text delete", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first();
// get the original length of this element
var elementLength = $firstTextElement.text().length;
@ -25,7 +25,7 @@ describe("delete keystroke", function(){
//ace creates a new dom element when you press a keystroke, so just get the first text element again
var $newFirstTextElement = inner$("div").first();
// get the new length of this element
var newElementLength = $newFirstTextElement.text().length;

View file

@ -16,11 +16,11 @@ describe("embed links", function(){
var $embediFrame = $(embedCode);
//read and check the frame attributes
var width = $embediFrame.attr("width");
var height = $embediFrame.attr("height");
var name = $embediFrame.attr("name");
expect(width).to.be('600');
expect(height).to.be('400');
var width = $embediFrame.attr("width");
var height = $embediFrame.attr("height");
var name = $embediFrame.attr("name");
expect(width).to.be('100%');
expect(height).to.be('600');
expect(name).to.be(readonly ? "embed_readonly" : "embed_readwrite");
//parse the url
@ -43,7 +43,7 @@ describe("embed links", function(){
} else {
expect(url).to.be(helper.padChrome$.window.location.href);
}
//check if all parts of the url are like expected
expect(params).to.eql(expectedParams);
}
@ -57,7 +57,7 @@ describe("embed links", function(){
describe("the share link", function(){
it("is the actual pad url", function(done){
var chrome$ = helper.padChrome$;
var chrome$ = helper.padChrome$;
//open share dropdown
chrome$(".buttonicon-embed").click();
@ -73,14 +73,14 @@ describe("embed links", function(){
describe("the embed as iframe code", function(){
it("is an iframe with the the correct url parameters and correct size", function(done){
var chrome$ = helper.padChrome$;
var chrome$ = helper.padChrome$;
//open share dropdown
chrome$(".buttonicon-embed").click();
//get the link of the share field + the actual pad url and compare them
var embedCode = chrome$("#embedinput").val();
checkiFrameCode(embedCode, false)
done();
@ -96,7 +96,7 @@ describe("embed links", function(){
describe("the share link", function(){
it("shows a read only url", function(done){
var chrome$ = helper.padChrome$;
var chrome$ = helper.padChrome$;
//open share dropdown
chrome$(".buttonicon-embed").click();
@ -114,7 +114,7 @@ describe("embed links", function(){
describe("the embed as iframe code", function(){
it("is an iframe with the the correct url parameters and correct size", function(done){
var chrome$ = helper.padChrome$;
var chrome$ = helper.padChrome$;
//open share dropdown
chrome$(".buttonicon-embed").click();
@ -125,9 +125,9 @@ describe("embed links", function(){
//get the link of the share field + the actual pad url and compare them
var embedCode = chrome$("#embedinput").val();
checkiFrameCode(embedCode, true);
done();
});
});

View file

@ -6,12 +6,12 @@ describe("enter keystroke", function(){
});
it("creates a new line & puts cursor onto a new line", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first();
// get the original string value minus the last char
var originalTextValue = $firstTextElement.text();
@ -20,7 +20,7 @@ describe("enter keystroke", function(){
//ace creates a new dom element when you press a keystroke, so just get the first text element again
var $newFirstTextElement = inner$("div").first();
helper.waitFor(function(){
return inner$("div").first().text() === "";
}).done(function(){

View file

@ -5,26 +5,27 @@ describe("font select", function(){
this.timeout(60000);
});
it("makes text monospace", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
it("makes text RobotoMono", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
//click on the settings button to make settings visible
var $settingsButton = chrome$(".buttonicon-settings");
$settingsButton.click();
//get the font menu and monospace option
//get the font menu and RobotoMono option
var $viewfontmenu = chrome$("#viewfontmenu");
var $monospaceoption = $viewfontmenu.find("[value=monospace]");
var $RobotoMonooption = $viewfontmenu.find("[value=RobotoMono]");
//select monospace and fire change event
$monospaceoption.attr('selected','selected');
$viewfontmenu.val("monospace");
//select RobotoMono and fire change event
// $RobotoMonooption.attr('selected','selected');
// commenting out above will break safari test
$viewfontmenu.val("RobotoMono");
$viewfontmenu.change();
//check if font changed to monospace
//check if font changed to RobotoMono
var fontFamily = inner$("body").css("font-family").toLowerCase();
var containsStr = fontFamily.indexOf("monospace");
var containsStr = fontFamily.indexOf("robotomono");
expect(containsStr).to.not.be(-1);
done();

View file

@ -20,7 +20,7 @@ describe("the test helper", function(){
});
it("gives me 3 jquery instances of chrome, outer and inner", function(done){
this.timeout(5000);
this.timeout(10000);
helper.newPad(function(){
//check if the jquery selectors have the desired elements
@ -36,17 +36,106 @@ describe("the test helper", function(){
done();
});
});
// Make sure the cookies are cleared, and make sure that the cookie
// clearing has taken effect at this point in the code. It has been
// observed that the former can happen without the latter if there
// isn't a timeout (within `newPad`) after clearing the cookies.
// However this doesn't seem to always be easily replicated, so this
// timeout may or may end up in the code. None the less, we test here
// to catch it if the bug comes up again.
it("clears cookies", function(done) {
this.timeout(60000);
// set cookies far into the future to make sure they're not expired yet
window.document.cookie = 'token=foo;expires=Thu, 01 Jan 3030 00:00:00 GMT; path=/';
window.document.cookie = 'language=bar;expires=Thu, 01 Jan 3030 00:00:00 GMT; path=/';
expect(window.document.cookie).to.contain('token=foo');
expect(window.document.cookie).to.contain('language=bar');
helper.newPad(function(){
// helper function seems to have cleared cookies
// NOTE: this doesn't yet mean it's proven to have taken effect by this point in execution
var firstCookie = window.document.cookie
expect(firstCookie).to.not.contain('token=foo');
expect(firstCookie).to.not.contain('language=bar');
var chrome$ = helper.padChrome$;
// click on the settings button to make settings visible
var $userButton = chrome$(".buttonicon-showusers");
$userButton.click();
var $usernameInput = chrome$("#myusernameedit");
$usernameInput.click();
$usernameInput.val('John McLear');
$usernameInput.blur();
// Before refreshing, make sure the name is there
expect($usernameInput.val()).to.be('John McLear');
// Now that we have a chrome, we can set a pad cookie, so we can confirm it gets wiped as well
chrome$.document.cookie = 'prefsHtml=baz;expires=Thu, 01 Jan 3030 00:00:00 GMT';
expect(chrome$.document.cookie).to.contain('prefsHtml=baz');
// Cookies are weird. Because it's attached to chrome$ (as helper.setPadCookies does), AND we
// didn't put path=/, we shouldn't expect it to be visible on window.document.cookie. Let's just
// be sure.
expect(window.document.cookie).to.not.contain('prefsHtml=baz');
setTimeout(function(){ //give it a second to save the username on the server side
helper.newPad(function(){ // get a new pad, let it clear the cookies
var chrome$ = helper.padChrome$;
// helper function seems to have cleared cookies
// NOTE: this doesn't yet mean cookies were cleared effectively.
// We still need to test below that we're in a new session
expect(window.document.cookie).to.not.contain('token=foo');
expect(window.document.cookie).to.not.contain('language=bar');
expect(chrome$.document.cookie).to.contain('prefsHtml=baz');
expect(window.document.cookie).to.not.contain('prefsHtml=baz');
expect(window.document.cookie).to.not.be(firstCookie);
// click on the settings button to make settings visible
var $userButton = chrome$(".buttonicon-showusers");
$userButton.click();
// confirm that the session was actually cleared
var $usernameInput = chrome$("#myusernameedit");
expect($usernameInput.val()).to.be('');
done();
});
}, 1000);
});
});
it("sets pad prefs cookie", function(done) {
this.timeout(60000);
helper.newPad({
padPrefs: {foo:"bar"},
cb: function(){
var chrome$ = helper.padChrome$;
expect(chrome$.document.cookie).to.contain('prefsHttp=%7B%22');
expect(chrome$.document.cookie).to.contain('foo%22%3A%22bar');
done();
}
});
});
});
describe("the waitFor method", function(){
it("takes a timeout and waits long enough", function(done){
this.timeout(2000);
var startTime = new Date().getTime();
var startTime = Date.now();
helper.waitFor(function(){
return false;
}, 1500).fail(function(){
var duration = new Date().getTime() - startTime;
var duration = Date.now() - startTime;
expect(duration).to.be.greaterThan(1400);
done();
});
@ -136,7 +225,15 @@ describe("the test helper", function(){
helper.selectLines($startLine, $endLine, startOffset, endOffset);
var selection = inner$.document.getSelection();
expect(cleanText(selection.toString())).to.be("ort lines to t");
/*
* replace() is required here because Firefox keeps the line breaks.
*
* I'm not sure this is ideal behavior of getSelection() where the text
* is not consistent between browsers but that's the situation so that's
* how I'm covering it in this test.
*/
expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm,""))).to.be("ort lines to t");
done();
});
@ -154,7 +251,15 @@ describe("the test helper", function(){
helper.selectLines($startLine, $endLine, startOffset, endOffset);
var selection = inner$.document.getSelection();
expect(cleanText(selection.toString())).to.be("ort lines to test");
/*
* replace() is required here because Firefox keeps the line breaks.
*
* I'm not sure this is ideal behavior of getSelection() where the text
* is not consistent between browsers but that's the situation so that's
* how I'm covering it in this test.
*/
expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm,""))).to.be("ort lines to test");
done();
});
@ -172,7 +277,15 @@ describe("the test helper", function(){
helper.selectLines($startLine, $endLine, startOffset, endOffset);
var selection = inner$.document.getSelection();
expect(cleanText(selection.toString())).to.be("ort lines ");
/*
* replace() is required here because Firefox keeps the line breaks.
*
* I'm not sure this is ideal behavior of getSelection() where the text
* is not consistent between browsers but that's the situation so that's
* how I'm covering it in this test.
*/
expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm,""))).to.be("ort lines ");
done();
});
@ -190,7 +303,15 @@ describe("the test helper", function(){
helper.selectLines($startLine, $endLine, startOffset, endOffset);
var selection = inner$.document.getSelection();
expect(cleanText(selection.toString())).to.be("ort lines to test");
/*
* replace() is required here because Firefox keeps the line breaks.
*
* I'm not sure this is ideal behavior of getSelection() where the text
* is not consistent between browsers but that's the situation so that's
* how I'm covering it in this test.
*/
expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm,""))).to.be("ort lines to test");
done();
});
@ -205,7 +326,15 @@ describe("the test helper", function(){
helper.selectLines($startLine, $endLine);
var selection = inner$.document.getSelection();
expect(cleanText(selection.toString())).to.be("short lines to test");
/*
* replace() is required here because Firefox keeps the line breaks.
*
* I'm not sure this is ideal behavior of getSelection() where the text
* is not consistent between browsers but that's the situation so that's
* how I'm covering it in this test.
*/
expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm,""))).to.be("short lines to test");
done();
});

View file

@ -159,7 +159,7 @@ describe("import functionality", function(){
//<ul class="list-bullet4"><li><span class="">bullet4 line 2</span></li></ul>\n\
//<br>\n')
})
var results = exportfunc(helper.padChrome$.window.location.href)
expect(results[0][1]).to.be('<ul class="bullet"><li>bullet line 1</li><li>bullet line 2</li><ul class="bullet"><li>bullet2 line 1</li><ul><ul class="bullet"><li>bullet4 line 2</li><li>bullet4 line 2</li><li>bullet4 line 2</li></ul><li>bullet3 line 1</li></ul></ul><li>bullet2 line 1</li></ul><br>')
expect(results[1][1]).to.be('\t* bullet line 1\n\t* bullet line 2\n\t\t* bullet2 line 1\n\t\t\t\t* bullet4 line 2\n\t\t\t\t* bullet4 line 2\n\t\t\t\t* bullet4 line 2\n\t\t\t* bullet3 line 1\n\t* bullet2 line 1\n\n')
@ -183,11 +183,11 @@ describe("import functionality", function(){
<br>\n')
})
var results = exportfunc(helper.padChrome$.window.location.href)
expect(results[0][1]).to.be('<ul class="bullet"><li>bullet line 1</li></ul><br><ul class="bullet"><li>bullet line 2</li><ul class="bullet"><li>bullet2 line 1</li></ul></ul><br><ul><ul><ul><ul class="bullet"><li><strong><em><s><u>bullet4 line 2 bisu</u></s></em></strong></li><li><strong><s>bullet4 line 2 bs</s></strong></li><li><u>bullet4 line 2 u<em><s>uis</s></em></u></li><ul><ul><ul><ul class="bullet"><li>foo</li><li><strong><s>foobar bs</s></strong></li></ul></ul></ul><li>foobar</li></ul></ul></ul></ul></ul><br>')
expect(results[0][1]).to.be('<ul class="bullet"><li>bullet line 1</li></ul><br><ul class="bullet"><li>bullet line 2</li><ul class="bullet"><li>bullet2 line 1</li></ul></ul><br><ul><ul><ul><ul class="bullet"><li><strong><em><s><u>bullet4 line 2 bisu</u></s></em></strong></li><li><strong><s>bullet4 line 2 bs</s></strong></li><li><u>bullet4 line 2 u<em><s>uis</s></em></u></li><ul><ul><ul><ul class="bullet"><li>foo</li><li><strong><s>foobar bs</s></strong></li></ul></ul></ul><li>foobar</li></ul></ul></ul></ul></ul><br>')
expect(results[1][1]).to.be('\t* bullet line 1\n\n\t* bullet line 2\n\t\t* bullet2 line 1\n\n\t\t\t\t* bullet4 line 2 bisu\n\t\t\t\t* bullet4 line 2 bs\n\t\t\t\t* bullet4 line 2 uuis\n\t\t\t\t\t\t\t\t* foo\n\t\t\t\t\t\t\t\t* foobar bs\n\t\t\t\t\t* foobar\n\n')
done()
})
xit("import a pad with ordered lists from html", function(done){
var importurl = helper.padChrome$.window.location.href+'/import'
var htmlWithBullets = '<html><body><ol class="list-number1" start="1"><li>number 1 line 1</li></ol><ol class="list-number1" start="2"><li>number 2 line 2</li></ol></body></html>'

View file

@ -66,7 +66,7 @@ describe("import indents functionality", function(){
expect(results[1][1]).to.be('\tindent line 1\n\tindent line 2\n\t\tindent2 line 1\n\t\tindent2 line 2\n\n')
done()
})
xit("import a pad with indented lists and newlines from html", function(done){
var importurl = helper.padChrome$.window.location.href+'/import'
var htmlWithIndents = '<html><body><ul class="list-indent1"><li>indent line 1</li></ul><br/><ul class="list-indent1"><li>indent 1 line 2</li><ul class="list-indent2"><li>indent 2 times line 1</li></ul></ul><br/><ul class="list-indent1"><ul class="list-indent2"><li>indent 2 times line 2</li></ul></ul></body></html>'
@ -104,7 +104,7 @@ describe("import indents functionality", function(){
<br>\n')
})
var results = exportfunc(helper.padChrome$.window.location.href)
expect(results[0][1]).to.be('<ul class="indent"><li>indent line 1</li></ul><br><ul class="indent"><li>indent line 2</li><ul class="indent"><li>indent2 line 1</li></ul></ul><br><ul><ul><ul><ul class="indent"><li><strong><em><s><u>indent4 line 2 bisu</u></s></em></strong></li><li><strong><s>indent4 line 2 bs</s></strong></li><li><u>indent4 line 2 u<em><s>uis</s></em></u></li><ul><ul><ul><ul class="indent"><li>foo</li><li><strong><s>foobar bs</s></strong></li></ul></ul></ul><li>foobar</li></ul></ul></ul></ul></ul><br>')
expect(results[0][1]).to.be('<ul class="indent"><li>indent line 1</li></ul><br><ul class="indent"><li>indent line 2</li><ul class="indent"><li>indent2 line 1</li></ul></ul><br><ul><ul><ul><ul class="indent"><li><strong><em><s><u>indent4 line 2 bisu</u></s></em></strong></li><li><strong><s>indent4 line 2 bs</s></strong></li><li><u>indent4 line 2 u<em><s>uis</s></em></u></li><ul><ul><ul><ul class="indent"><li>foo</li><li><strong><s>foobar bs</s></strong></li></ul></ul></ul><li>foobar</li></ul></ul></ul></ul></ul><br>')
expect(results[1][1]).to.be('\tindent line 1\n\n\tindent line 2\n\t\tindent2 line 1\n\n\t\t\t\tindent4 line 2 bisu\n\t\t\t\tindent4 line 2 bs\n\t\t\t\tindent4 line 2 uuis\n\t\t\t\t\t\t\t\tfoo\n\t\t\t\t\t\t\t\tfoobar bs\n\t\t\t\t\tfoobar\n\n')
done()
})

View file

@ -15,13 +15,7 @@ describe("indentation button", function(){
//select this text element
$firstTextElement.sendkeys('{selectall}');
if(inner$(window)[0].bowser.modernIE){ // if it's IE
var evtType = "keypress";
}else{
var evtType = "keydown";
}
var e = inner$.Event(evtType);
var e = inner$.Event(helper.evtType);
e.keyCode = 9; // tab :|
inner$("#innerdocbody").trigger(e);
@ -325,12 +319,7 @@ describe("indentation button", function(){
function pressEnter(){
var inner$ = helper.padInner$;
if(inner$(window)[0].bowser.modernIE){ // if it's IE
var evtType = "keypress";
}else{
var evtType = "keydown";
}
var e = inner$.Event(evtType);
var e = inner$.Event(helper.evtType);
e.keyCode = 13; // enter :|
inner$("#innerdocbody").trigger(e);
}

View file

@ -6,22 +6,22 @@ describe("italic some text", function(){
});
it("makes text italic using button", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first();
//select this text element
$firstTextElement.sendkeys('{selectall}');
//get the bold button and click it
var $boldButton = chrome$(".buttonicon-italic");
$boldButton.click();
//ace creates a new dom element when you press a button, so just get the first text element again
var $newFirstTextElement = inner$("div").first();
// is there a <i> element now?
var isItalic = $newFirstTextElement.find("i").length === 1;
@ -44,13 +44,7 @@ describe("italic some text", function(){
//select this text element
$firstTextElement.sendkeys('{selectall}');
if(inner$(window)[0].bowser.modernIE){ // if it's IE
var evtType = "keypress";
}else{
var evtType = "keydown";
}
var e = inner$.Event(evtType);
var e = inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key
e.which = 105; // i
inner$("#innerdocbody").trigger(e);

View file

@ -0,0 +1,51 @@
describe('author of pad edition', function() {
// author 1 creates a new pad with some content (regular lines and lists)
before(function(done) {
var padId = helper.newPad(function() {
// make sure pad has at least 3 lines
var $firstLine = helper.padInner$('div').first();
$firstLine.html("Hello World");
// wait for lines to be processed by Etherpad
helper.waitFor(function() {
return $firstLine.text() === 'Hello World';
}).done(function() {
// Reload pad, to make changes as a second user. Need a timeout here to make sure
// all changes were saved before reloading
setTimeout(function() {
// Expire cookie, so author is changed after reloading the pad.
// See https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie#Example_4_Reset_the_previous_cookie
helper.padChrome$.document.cookie = 'token=foo;expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
helper.newPad(done, padId);
}, 1000);
});
});
this.timeout(60000);
});
// author 2 makes some changes on the pad
it('Clears Authorship by second user', function(done) {
clearAuthorship(done);
});
var clearAuthorship = function(done){
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
// override the confirm dialogue functioon
helper.padChrome$.window.confirm = function(){
return true;
}
//get the clear authorship colors button and click it
var $clearauthorshipcolorsButton = chrome$(".buttonicon-clearauthorship");
$clearauthorshipcolorsButton.click();
// does the first divs span include an author class?
var hasAuthorClass = inner$("div span").first().attr("class").indexOf("author") !== -1;
expect(hasAuthorClass).to.be(false)
done();
}
});

View file

@ -100,7 +100,6 @@ describe("assign ordered list", function(){
}).done(function(){
var $newSecondLine = inner$("div").first().next();
var hasOLElement = $newSecondLine.find("ol li").length === 1;
console.log($newSecondLine.find("ol"));
expect(hasOLElement).to.be(true);
expect($newSecondLine.text()).to.be("line 2");
var hasLineNumber = $newSecondLine.find("ol").attr("start") === 2;
@ -111,12 +110,7 @@ describe("assign ordered list", function(){
var triggerCtrlShiftShortcut = function(shortcutChar) {
var inner$ = helper.padInner$;
if(inner$(window)[0].bowser.modernIE) { // if it's IE
var evtType = "keypress";
}else{
var evtType = "keydown";
}
var e = inner$.Event(evtType);
var e = inner$.Event(helper.evtType);
e.ctrlKey = true;
e.shiftKey = true;
e.which = shortcutChar.toString().charCodeAt(0);
@ -131,3 +125,80 @@ describe("assign ordered list", function(){
}
});
describe("Pressing Tab in an OL increases and decreases indentation", function(){
//create a new pad before each test run
beforeEach(function(cb){
helper.newPad(cb);
this.timeout(60000);
});
it("indent and de-indent list item with keypress", function(done){
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first();
//select this text element
$firstTextElement.sendkeys('{selectall}');
var $insertorderedlistButton = chrome$(".buttonicon-insertorderedlist");
$insertorderedlistButton.click();
var e = inner$.Event(helper.evtType);
e.keyCode = 9; // tab
inner$("#innerdocbody").trigger(e);
expect(inner$("div").first().find(".list-number2").length === 1).to.be(true);
e.shiftKey = true; // shift
e.keyCode = 9; // tab
inner$("#innerdocbody").trigger(e);
helper.waitFor(function(){
return inner$("div").first().find(".list-number1").length === 1;
}).done(done);
});
});
describe("Pressing indent/outdent button in an OL increases and decreases indentation and bullet / ol formatting", function(){
//create a new pad before each test run
beforeEach(function(cb){
helper.newPad(cb);
this.timeout(60000);
});
it("indent and de-indent list item with indent button", function(done){
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first();
//select this text element
$firstTextElement.sendkeys('{selectall}');
var $insertorderedlistButton = chrome$(".buttonicon-insertorderedlist");
$insertorderedlistButton.click();
var $indentButton = chrome$(".buttonicon-indent");
$indentButton.click(); // make it indented twice
expect(inner$("div").first().find(".list-number2").length === 1).to.be(true);
var $outdentButton = chrome$(".buttonicon-outdent");
$outdentButton.click(); // make it deindented to 1
helper.waitFor(function(){
return inner$("div").first().find(".list-number1").length === 1;
}).done(done);
});
});

View file

@ -1,6 +1,6 @@
describe('Pad modal', function() {
context('when modal is a "force reconnect" message', function() {
var MODAL_SELECTOR = '#connectivity .slowcommit';
var MODAL_SELECTOR = '#connectivity';
beforeEach(function(done) {
helper.newPad(function() {
@ -10,7 +10,7 @@ describe('Pad modal', function() {
// wait for modal to be displayed
var $modal = helper.padChrome$(MODAL_SELECTOR);
helper.waitFor(function() {
return $modal.is(':visible');
return $modal.hasClass('popup-show');
}, 50000).done(done);
});
@ -30,7 +30,7 @@ describe('Pad modal', function() {
it('does not close the modal', function(done) {
var $modal = helper.padChrome$(MODAL_SELECTOR);
var modalIsVisible = $modal.is(':visible');
var modalIsVisible = $modal.hasClass('popup-show');
expect(modalIsVisible).to.be(true);
@ -45,7 +45,7 @@ describe('Pad modal', function() {
it('does not close the modal', function(done) {
var $modal = helper.padChrome$(MODAL_SELECTOR);
var modalIsVisible = $modal.is(':visible');
var modalIsVisible = $modal.hasClass('popup-show');
expect(modalIsVisible).to.be(true);
@ -65,12 +65,13 @@ describe('Pad modal', function() {
this.timeout(60000);
});
// This test breaks safari testing
/*
it('does not disable editor', function(done) {
expect(isEditorDisabled()).to.be(false);
done();
});
*/
context('and user clicks on editor', function() {
beforeEach(function() {
clickOnPadInner();
@ -126,6 +127,7 @@ describe('Pad modal', function() {
var isModalOpened = function(modalSelector) {
var $modal = helper.padChrome$(modalSelector);
return $modal.is(':visible');
return $modal.hasClass('popup-show');
}
});

View file

@ -7,7 +7,7 @@ describe("undo button then redo button", function(){
it("redo some typing with button", function(done){
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
// get the first text element inside the editable space
var $firstTextElement = inner$("div span").first();
var originalValue = $firstTextElement.text(); // get the original value
@ -25,7 +25,6 @@ describe("undo button then redo button", function(){
$redoButton.click(); // resends foo
helper.waitFor(function(){
console.log(inner$("div span").first().text());
return inner$("div span").first().text() === newString;
}).done(function(){
var finalValue = inner$("div").first().text();
@ -47,24 +46,17 @@ describe("undo button then redo button", function(){
var modifiedValue = $firstTextElement.text(); // get the modified value
expect(modifiedValue).not.to.be(originalValue); // expect the value to change
if(inner$(window)[0].bowser.firefox || inner$(window)[0].bowser.modernIE){ // if it's a mozilla or IE
var evtType = "keypress";
}else{
var evtType = "keydown";
}
var e = inner$.Event(evtType);
var e = inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key
e.which = 90; // z
inner$("#innerdocbody").trigger(e);
var e = inner$.Event(evtType);
var e = inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key
e.which = 121; // y
inner$("#innerdocbody").trigger(e);
helper.waitFor(function(){
console.log(inner$("div span").first().text());
return inner$("div span").first().text() === newString;
}).done(function(){
var finalValue = inner$("div").first().text();

View file

@ -49,7 +49,7 @@ describe('Responsiveness of Editor', function() {
}).done(function(){
expect( inner$('div').text().length ).to.be.greaterThan( length ); // has the text changed?
var start = new Date().getTime(); // get the start time
var start = Date.now(); // get the start time
// send some new text to the screen (ensure all 3 key events are sent)
var el = inner$('div').first();
@ -65,11 +65,10 @@ describe('Responsiveness of Editor', function() {
helper.waitFor(function(){ // Wait for the ability to process
return true; // Ghetto but works for now
}).done(function(){
var end = new Date().getTime(); // get the current time
var end = Date.now(); // get the current time
var delay = end - start; // get the delay as the current time minus the start time
console.log('delay:', delay);
expect(delay).to.be.below(200);
expect(delay).to.be.below(300);
done();
}, 1000);

View file

@ -1,649 +0,0 @@
describe('scroll when focus line is out of viewport', function () {
before(function (done) {
helper.newPad(function(){
cleanPad(function(){
forceUseMonospacedFont();
scrollWhenPlaceCaretInTheLastLineOfViewport();
createPadWithSeveralLines(function(){
resizeEditorHeight();
done();
});
});
});
this.timeout(20000);
});
context('when user presses any arrow keys on a line above the viewport', function(){
context('and scroll percentage config is set to 0.2 on settings.json', function(){
var lineCloseOfTopOfPad = 10;
before(function (done) {
setScrollPercentageWhenFocusLineIsOutOfViewport(0.2, true);
scrollEditorToBottomOfPad();
placeCaretInTheBeginningOfLine(lineCloseOfTopOfPad, function(){ // place caret in the 10th line
// warning: even pressing right arrow, the caret does not change of position
// the column where the caret is, it has not importance, only the line
pressAndReleaseRightArrow();
done();
});
});
it('keeps the focus line scrolled 20% from the top of the viewport', function (done) {
// default behavior is to put the line in the top of viewport, but as
// scrollPercentageWhenFocusLineIsOutOfViewport is set to 0.2, we have an extra 20% of lines scrolled
// (2 lines, which are the 20% of the 10 that are visible on viewport)
var firstLineOfViewport = getFirstLineVisibileOfViewport();
expect(lineCloseOfTopOfPad).to.be(firstLineOfViewport + 2);
done();
});
});
});
context('when user presses any arrow keys on a line below the viewport', function(){
context('and scroll percentage config is set to 0.7 on settings.json', function(){
var lineCloseToBottomOfPad = 50;
before(function (done) {
setScrollPercentageWhenFocusLineIsOutOfViewport(0.7);
// firstly, scroll to make the lineCloseToBottomOfPad visible. After that, scroll to make it out of viewport
scrollEditorToTopOfPad();
placeCaretAtTheEndOfLine(lineCloseToBottomOfPad); // place caret in the 50th line
setTimeout(function() {
// warning: even pressing right arrow, the caret does not change of position
pressAndReleaseLeftArrow();
done();
}, 1000);
});
it('keeps the focus line scrolled 70% from the bottom of the viewport', function (done) {
// default behavior is to put the line in the top of viewport, but as
// scrollPercentageWhenFocusLineIsOutOfViewport is set to 0.7, we have an extra 70% of lines scrolled
// (7 lines, which are the 70% of the 10 that are visible on viewport)
var lastLineOfViewport = getLastLineVisibleOfViewport();
expect(lineCloseToBottomOfPad).to.be(lastLineOfViewport - 7);
done();
});
});
});
context('when user presses arrow up on the first line of the viewport', function(){
context('and percentageToScrollWhenUserPressesArrowUp is set to 0.3', function () {
var lineOnTopOfViewportWhenThePadIsScrolledDown;
before(function (done) {
setPercentageToScrollWhenUserPressesArrowUp(0.3);
// we need some room to make the scroll up
scrollEditorToBottomOfPad();
lineOnTopOfViewportWhenThePadIsScrolledDown = 91;
placeCaretAtTheEndOfLine(lineOnTopOfViewportWhenThePadIsScrolledDown);
setTimeout(function() {
// warning: even pressing up arrow, the caret does not change of position
pressAndReleaseUpArrow();
done();
}, 1000);
});
it('keeps the focus line scrolled 30% of the top of the viewport', function (done) {
// default behavior is to put the line in the top of viewport, but as
// PercentageToScrollWhenUserPressesArrowUp is set to 0.3, we have an extra 30% of lines scrolled
// (3 lines, which are the 30% of the 10 that are visible on viewport)
var firstLineOfViewport = getFirstLineVisibileOfViewport();
expect(firstLineOfViewport).to.be(lineOnTopOfViewportWhenThePadIsScrolledDown - 3);
done();
})
});
});
context('when user edits the last line of viewport', function(){
context('and scroll percentage config is set to 0 on settings.json', function(){
var lastLineOfViewportBeforeEnter = 10;
before(function () {
// the default value
resetScrollPercentageWhenFocusLineIsOutOfViewport();
// make sure the last line on viewport is the 10th one
scrollEditorToTopOfPad();
placeCaretAtTheEndOfLine(lastLineOfViewportBeforeEnter);
pressEnter();
});
it('keeps the focus line on the bottom of the viewport', function (done) {
var lastLineOfViewportAfterEnter = getLastLineVisibleOfViewport();
expect(lastLineOfViewportAfterEnter).to.be(lastLineOfViewportBeforeEnter + 1);
done();
});
});
context('and scrollPercentageWhenFocusLineIsOutOfViewport is set to 0.3', function(){ // this value is arbitrary
var lastLineOfViewportBeforeEnter = 9;
before(function () {
setScrollPercentageWhenFocusLineIsOutOfViewport(0.3);
// make sure the last line on viewport is the 10th one
scrollEditorToTopOfPad();
placeCaretAtTheEndOfLine(lastLineOfViewportBeforeEnter);
pressBackspace();
});
it('scrolls 30% of viewport up', function (done) {
var lastLineOfViewportAfterEnter = getLastLineVisibleOfViewport();
// default behavior is to scroll one line at the bottom of viewport, but as
// scrollPercentageWhenFocusLineIsOutOfViewport is set to 0.3, we have an extra 30% of lines scrolled
// (3 lines, which are the 30% of the 10 that are visible on viewport)
expect(lastLineOfViewportAfterEnter).to.be(lastLineOfViewportBeforeEnter + 3);
done();
});
});
context('and it is set to a value that overflow the interval [0, 1]', function(){
var lastLineOfViewportBeforeEnter = 10;
before(function(){
var scrollPercentageWhenFocusLineIsOutOfViewport = 1.5;
scrollEditorToTopOfPad();
placeCaretAtTheEndOfLine(lastLineOfViewportBeforeEnter);
setScrollPercentageWhenFocusLineIsOutOfViewport(scrollPercentageWhenFocusLineIsOutOfViewport);
pressEnter();
});
it('keeps the default behavior of moving the focus line on the bottom of the viewport', function (done) {
var lastLineOfViewportAfterEnter = getLastLineVisibleOfViewport();
expect(lastLineOfViewportAfterEnter).to.be(lastLineOfViewportBeforeEnter + 1);
done();
});
});
});
context('when user edits a line above the viewport', function(){
context('and scroll percentage config is set to 0 on settings.json', function(){
var lineCloseOfTopOfPad = 10;
before(function () {
// the default value
setScrollPercentageWhenFocusLineIsOutOfViewport(0);
// firstly, scroll to make the lineCloseOfTopOfPad visible. After that, scroll to make it out of viewport
scrollEditorToTopOfPad();
placeCaretAtTheEndOfLine(lineCloseOfTopOfPad); // place caret in the 10th line
scrollEditorToBottomOfPad();
pressBackspace(); // edit the line where the caret is, which is above the viewport
});
it('keeps the focus line on the top of the viewport', function (done) {
var firstLineOfViewportAfterEnter = getFirstLineVisibileOfViewport();
expect(firstLineOfViewportAfterEnter).to.be(lineCloseOfTopOfPad);
done();
});
});
context('and scrollPercentageWhenFocusLineIsOutOfViewport is set to 0.2', function(){ // this value is arbitrary
var lineCloseToBottomOfPad = 50;
before(function () {
// we force the line edited to be above the top of the viewport
setScrollPercentageWhenFocusLineIsOutOfViewport(0.2, true); // set scroll jump to 20%
scrollEditorToTopOfPad();
placeCaretAtTheEndOfLine(lineCloseToBottomOfPad);
scrollEditorToBottomOfPad();
pressBackspace(); // edit line
});
it('scrolls 20% of viewport down', function (done) {
// default behavior is to scroll one line at the top of viewport, but as
// scrollPercentageWhenFocusLineIsOutOfViewport is set to 0.2, we have an extra 20% of lines scrolled
// (2 lines, which are the 20% of the 10 that are visible on viewport)
var firstLineVisibileOfViewport = getFirstLineVisibileOfViewport();
expect(lineCloseToBottomOfPad).to.be(firstLineVisibileOfViewport + 2);
done();
});
});
});
context('when user places the caret at the last line visible of viewport', function(){
var lastLineVisible;
context('and scroll percentage config is set to 0 on settings.json', function(){
before(function (done) {
// reset to the default value
resetScrollPercentageWhenFocusLineIsOutOfViewport();
placeCaretInTheBeginningOfLine(0, function(){ // reset caret position
scrollEditorToTopOfPad();
lastLineVisible = getLastLineVisibleOfViewport();
placeCaretInTheBeginningOfLine(lastLineVisible, done); // place caret in the 9th line
});
});
it('does not scroll', function(done){
setTimeout(function() {
var lastLineOfViewport = getLastLineVisibleOfViewport();
var lineDoesNotScroll = lastLineOfViewport === lastLineVisible;
expect(lineDoesNotScroll).to.be(true);
done();
}, 1000);
});
});
context('and scroll percentage config is set to 0.5 on settings.json', function(){
before(function (done) {
setScrollPercentageWhenFocusLineIsOutOfViewport(0.5);
scrollEditorToTopOfPad();
placeCaretInTheBeginningOfLine(0, function(){ // reset caret position
// this timeout inside a callback is ugly but it necessary to give time to aceSelectionChange
// realizes that the selection has been changed
setTimeout(function() {
lastLineVisible = getLastLineVisibleOfViewport();
placeCaretInTheBeginningOfLine(lastLineVisible, done); // place caret in the 9th line
}, 1000);
});
});
it('scrolls line to 50% of the viewport', function(done){
helper.waitFor(function(){
var lastLineOfViewport = getLastLineVisibleOfViewport();
var lastLinesScrolledFiveLinesUp = lastLineOfViewport - 5 === lastLineVisible;
return lastLinesScrolledFiveLinesUp;
}).done(done);
});
});
});
// This is a special case. When user is selecting a text with arrow down or arrow left we have
// to keep the last line selected on focus
context('when the first line selected is out of the viewport and user presses shift arrow down', function(){
var lastLineOfPad = 99;
before(function (done) {
scrollEditorToTopOfPad();
// make a selection bigger than the viewport height
var $firstLineOfSelection = getLine(0);
var $lastLineOfSelection = getLine(lastLineOfPad);
var lengthOfLastLine = $lastLineOfSelection.text().length;
helper.selectLines($firstLineOfSelection, $lastLineOfSelection, 0, lengthOfLastLine);
// place the last line selected on the viewport
scrollEditorToBottomOfPad();
// press a key to make the selection goes down
// although we can't simulate the extending of selection. It's possible to send a key event
// which is captured on ace2_inner scroll function.
pressAndReleaseLeftArrow(true);
done();
});
it('keeps the last line selected on focus', function (done) {
var lastLineOfSelectionIsVisible = isLineOnViewport(lastLineOfPad);
expect(lastLineOfSelectionIsVisible).to.be(true);
done();
});
});
// In this scenario we avoid the bouncing scroll. E.g Let's suppose we have a big line that is
// the size of the viewport, and its top is above the viewport. When user presses '<-', this line
// will scroll down because the top is out of the viewport. When it scrolls down, the bottom of
// line gets below the viewport so when user presses '<-' again it scrolls up to make the bottom
// of line visible. If user presses arrow keys more than one time, the editor will keep scrolling up and down
context('when the line height is bigger than the scroll amount percentage * viewport height', function(){
var scrollOfEditorBeforePressKey;
var BIG_LINE_NUMBER = 0;
var MIDDLE_OF_BIG_LINE = 51;
before(function (done) {
createPadWithALineHigherThanViewportHeight(this, BIG_LINE_NUMBER, function(){
setScrollPercentageWhenFocusLineIsOutOfViewport(0.5); // set any value to force scroll to outside to viewport
var $bigLine = getLine(BIG_LINE_NUMBER);
// each line has about 5 chars, we place the caret in the middle of the line
helper.selectLines($bigLine, $bigLine, MIDDLE_OF_BIG_LINE, MIDDLE_OF_BIG_LINE);
scrollEditorToLeaveTopAndBottomOfBigLineOutOfViewport($bigLine);
scrollOfEditorBeforePressKey = getEditorScroll();
// press a key to force to scroll
pressAndReleaseRightArrow();
done();
});
});
// reset pad to the original text
after(function (done) {
this.timeout(5000);
cleanPad(function(){
createPadWithSeveralLines(function(){
resetEditorWidth();
done();
});
});
});
// as the editor.line is inside of the viewport, it should not scroll
it('should not scroll', function (done) {
var scrollOfEditorAfterPressKey = getEditorScroll();
expect(scrollOfEditorAfterPressKey).to.be(scrollOfEditorBeforePressKey);
done();
});
});
// Some plugins, for example the ep_page_view, change the editor dimensions. This plugin, for example,
// adds padding-top to the ace_outer, which changes the viewport height
describe('integration with plugins which changes the margin of editor', function(){
context('when editor dimensions changes', function(){
before(function () {
// reset the size of editor. Now we show more than 10 lines as in the other tests
resetResizeOfEditorHeight();
scrollEditorToTopOfPad();
// height of the editor viewport
var editorHeight = getEditorHeight();
// add a big padding-top, 50% of the viewport
var paddingTopOfAceOuter = editorHeight/2;
var chrome$ = helper.padChrome$;
var $outerIframe = chrome$('iframe');
$outerIframe.css('padding-top', paddingTopOfAceOuter);
// we set a big value to check if the scroll is made
setScrollPercentageWhenFocusLineIsOutOfViewport(1);
});
context('and user places the caret in the last line visible of the pad', function(){
var lastLineVisible;
beforeEach(function (done) {
lastLineVisible = getLastLineVisibleOfViewport();
placeCaretInTheBeginningOfLine(lastLineVisible, done);
});
it('scrolls the line where caret is', function(done){
helper.waitFor(function(){
var firstLineVisibileOfViewport = getFirstLineVisibileOfViewport();
var linesScrolled = firstLineVisibileOfViewport !== 0;
return linesScrolled;
}).done(done);
});
});
});
});
/* ********************* Helper functions/constants ********************* */
var TOP_OF_PAGE = 0;
var BOTTOM_OF_PAGE = 5000; // we use a big value to force the page to be scrolled all the way down
var LINES_OF_PAD = 100;
var ENTER = 13;
var BACKSPACE = 8;
var LEFT_ARROW = 37;
var UP_ARROW = 38;
var RIGHT_ARROW = 39;
var LINES_ON_VIEWPORT = 10;
var WIDTH_OF_EDITOR_RESIZED = 100;
var LONG_TEXT_CHARS = 100;
var cleanPad = function(callback) {
var inner$ = helper.padInner$;
var $padContent = inner$('#innerdocbody');
$padContent.html('');
// wait for Etherpad to re-create first line
helper.waitFor(function(){
var lineNumber = inner$('div').length;
return lineNumber === 1;
}, 2000).done(callback);
};
var createPadWithSeveralLines = function(done) {
var line = '<span>a</span><br>';
var $firstLine = helper.padInner$('div').first();
var lines = line.repeat(LINES_OF_PAD); //arbitrary number, we need to create lines that is over the viewport
$firstLine.html(lines);
helper.waitFor(function(){
var linesCreated = helper.padInner$('div').length;
return linesCreated === LINES_OF_PAD;
}, 4000).done(done);
};
var createPadWithALineHigherThanViewportHeight = function(test, line, done) {
var viewportHeight = 160; //10 lines * 16px (height of line)
test.timeout(5000);
cleanPad(function(){
// make the editor smaller to make test easier
// with that width the each line has about 5 chars
resizeEditorWidth();
// we create a line with 100 chars, which makes about 20 lines
setLongTextOnLine(line);
helper.waitFor(function () {
var $firstLine = getLine(line);
var heightOfLine = $firstLine.get(0).getBoundingClientRect().height;
return heightOfLine >= viewportHeight;
}, 4000).done(done);
});
};
var setLongTextOnLine = function(line) {
var $line = getLine(line);
var longText = 'a'.repeat(LONG_TEXT_CHARS);
$line.html(longText);
};
// resize the editor to make the tests easier
var resizeEditorHeight = function() {
var chrome$ = helper.padChrome$;
chrome$('#editorcontainer').css('height', getSizeOfViewport());
};
// this makes about 5 chars per line
var resizeEditorWidth = function() {
var chrome$ = helper.padChrome$;
chrome$('#editorcontainer').css('width', WIDTH_OF_EDITOR_RESIZED);
};
var resetResizeOfEditorHeight = function() {
var chrome$ = helper.padChrome$;
chrome$('#editorcontainer').css('height', '');
};
var resetEditorWidth = function () {
var chrome$ = helper.padChrome$;
chrome$('#editorcontainer').css('width', '');
};
var getEditorHeight = function() {
var chrome$ = helper.padChrome$;
var $editor = chrome$('#editorcontainer');
var editorHeight = $editor.get(0).clientHeight;
return editorHeight;
};
var getSizeOfViewport = function() {
return getLinePositionOnViewport(LINES_ON_VIEWPORT) - getLinePositionOnViewport(0);
};
var scrollPageTo = function(value) {
var outer$ = helper.padOuter$;
var $ace_outer = outer$('#outerdocbody').parent();
$ace_outer.parent().scrollTop(value);
};
var scrollEditorToTopOfPad = function() {
scrollPageTo(TOP_OF_PAGE);
};
var scrollEditorToBottomOfPad = function() {
scrollPageTo(BOTTOM_OF_PAGE);
};
var scrollEditorToLeaveTopAndBottomOfBigLineOutOfViewport = function ($bigLine) {
var lineHeight = $bigLine.get(0).getBoundingClientRect().height;
var middleOfLine = lineHeight/2;
scrollPageTo(middleOfLine);
};
var getLine = function(lineNum) {
var inner$ = helper.padInner$;
var $line = inner$('div').eq(lineNum);
return $line;
};
var placeCaretAtTheEndOfLine = function(lineNum) {
var $targetLine = getLine(lineNum);
var lineLength = $targetLine.text().length;
helper.selectLines($targetLine, $targetLine, lineLength, lineLength);
};
var placeCaretInTheBeginningOfLine = function(lineNum, cb) {
var $targetLine = getLine(lineNum);
helper.selectLines($targetLine, $targetLine, 0, 0);
helper.waitFor(function() {
var $lineWhereCaretIs = getLineWhereCaretIs();
return $targetLine.get(0) === $lineWhereCaretIs.get(0);
}).done(cb);
};
var getLineWhereCaretIs = function() {
var inner$ = helper.padInner$;
var nodeWhereCaretIs = inner$.document.getSelection().anchorNode;
var $lineWhereCaretIs = $(nodeWhereCaretIs).closest('div');
return $lineWhereCaretIs;
};
var getFirstLineVisibileOfViewport = function() {
return _.find(_.range(0, LINES_OF_PAD - 1), isLineOnViewport);
};
var getLastLineVisibleOfViewport = function() {
return _.find(_.range(LINES_OF_PAD - 1, 0, -1), isLineOnViewport);
};
var pressKey = function(keyCode, shiftIsPressed){
var inner$ = helper.padInner$;
var evtType;
if(inner$(window)[0].bowser.modernIE){ // if it's IE
evtType = 'keypress';
}else{
evtType = 'keydown';
}
var e = inner$.Event(evtType);
e.shiftKey = shiftIsPressed;
e.keyCode = keyCode;
e.which = keyCode; // etherpad listens to 'which'
inner$('#innerdocbody').trigger(e);
};
var releaseKey = function(keyCode){
var inner$ = helper.padInner$;
var evtType = 'keyup';
var e = inner$.Event(evtType);
e.keyCode = keyCode;
e.which = keyCode; // etherpad listens to 'which'
inner$('#innerdocbody').trigger(e);
};
var pressEnter = function() {
pressKey(ENTER);
};
var pressBackspace = function() {
pressKey(BACKSPACE);
};
var pressAndReleaseUpArrow = function() {
pressKey(UP_ARROW);
releaseKey(UP_ARROW);
};
var pressAndReleaseRightArrow = function() {
pressKey(RIGHT_ARROW);
releaseKey(RIGHT_ARROW);
};
var pressAndReleaseLeftArrow = function(shiftIsPressed) {
pressKey(LEFT_ARROW, shiftIsPressed);
releaseKey(LEFT_ARROW);
};
var isLineOnViewport = function(lineNumber) {
// in the function scrollNodeVerticallyIntoView from ace2_inner.js, iframePadTop is used to calculate
// how much scroll is needed. Although the name refers to padding-top, this value is not set on the
// padding-top.
var iframePadTop = 8;
var $line = getLine(lineNumber);
var linePosition = $line.get(0).getBoundingClientRect();
// position relative to the current viewport
var linePositionTopOnViewport = linePosition.top - getEditorScroll() + iframePadTop;
var linePositionBottomOnViewport = linePosition.bottom - getEditorScroll();
var lineBellowTop = linePositionBottomOnViewport > 0;
var lineAboveBottom = linePositionTopOnViewport < getClientHeightVisible();
var isVisible = lineBellowTop && lineAboveBottom;
return isVisible;
};
var getEditorScroll = function () {
var outer$ = helper.padOuter$;
var scrollTopFirefox = outer$('#outerdocbody').parent().scrollTop(); // works only on firefox
var scrollTop = outer$('#outerdocbody').scrollTop() || scrollTopFirefox;
return scrollTop;
};
// clientHeight includes padding, so we have to subtract it and consider only the visible viewport
var getClientHeightVisible = function () {
var outer$ = helper.padOuter$;
var $ace_outer = outer$('#outerdocbody').parent();
var ace_outerHeight = $ace_outer.get(0).clientHeight;
var ace_outerPaddingTop = getIntValueOfCSSProperty($ace_outer, 'padding-top');
var paddingAddedWhenPageViewIsEnable = getPaddingAddedWhenPageViewIsEnable();
var clientHeight = ace_outerHeight - ( ace_outerPaddingTop + paddingAddedWhenPageViewIsEnable);
return clientHeight;
};
// ep_page_view changes the dimensions of the editor. We have to guarantee
// the viewport height is calculated right
var getPaddingAddedWhenPageViewIsEnable = function () {
var chrome$ = helper.padChrome$;
var $outerIframe = chrome$('iframe');
var paddingAddedWhenPageViewIsEnable = parseInt($outerIframe.css('padding-top'));
return paddingAddedWhenPageViewIsEnable;
};
var getIntValueOfCSSProperty = function($element, property){
var valueString = $element.css(property);
return parseInt(valueString) || 0;
};
var forceUseMonospacedFont = function () {
helper.padChrome$.window.clientVars.padOptions.useMonospaceFont = true;
};
var setScrollPercentageWhenFocusLineIsOutOfViewport = function(value, editionAboveViewport) {
var scrollSettings = helper.padChrome$.window.clientVars.scrollWhenFocusLineIsOutOfViewport;
if (editionAboveViewport) {
scrollSettings.percentage.editionAboveViewport = value;
}else{
scrollSettings.percentage.editionBelowViewport = value;
}
};
var resetScrollPercentageWhenFocusLineIsOutOfViewport = function() {
var scrollSettings = helper.padChrome$.window.clientVars.scrollWhenFocusLineIsOutOfViewport;
scrollSettings.percentage.editionAboveViewport = 0;
scrollSettings.percentage.editionBelowViewport = 0;
};
var setPercentageToScrollWhenUserPressesArrowUp = function (value) {
var scrollSettings = helper.padChrome$.window.clientVars.scrollWhenFocusLineIsOutOfViewport;
scrollSettings.percentageToScrollWhenUserPressesArrowUp = value;
};
var scrollWhenPlaceCaretInTheLastLineOfViewport = function() {
var scrollSettings = helper.padChrome$.window.clientVars.scrollWhenFocusLineIsOutOfViewport;
scrollSettings.scrollWhenCaretIsInTheLastLineOfViewport = true;
};
var getLinePositionOnViewport = function(lineNumber) {
var $line = getLine(lineNumber);
var linePosition = $line.get(0).getBoundingClientRect();
// position relative to the current viewport
return linePosition.top - getEditorScroll();
};
});

View file

@ -63,19 +63,23 @@ describe("select formatting buttons when selection has style applied", function(
}
var applyStyleOnLineOnFullLineAndRemoveSelection = function(line, style, selectTarget, cb) {
// see if line html has changed
var inner$ = helper.padInner$;
var oldLineHTML = inner$.find("div")[line];
applyStyleOnLine(style, line);
// we have to give some time to Etherpad detects the selection changed
setTimeout(function() {
helper.waitFor(function(){
var lineHTML = inner$.find("div")[line];
return lineHTML !== oldLineHTML;
});
// remove selection from previous line
selectLine(line + 1);
setTimeout(function() {
// select the text or place the caret on a position that
// has the formatting text applied previously
selectTarget(line);
cb();
}, 1000);
}, 1000);
selectLine(line + 1);
//setTimeout(function() {
// select the text or place the caret on a position that
// has the formatting text applied previously
selectTarget(line);
cb();
//}, 1000);
}
var pressFormattingShortcutOnSelection = function(key) {
@ -88,13 +92,7 @@ describe("select formatting buttons when selection has style applied", function(
//select this text element
$firstTextElement.sendkeys('{selectall}');
if(inner$(window)[0].bowser.modernIE){ // if it's IE
var evtType = "keypress";
}else{
var evtType = "keydown";
}
var e = inner$.Event(evtType);
var e = inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key
e.which = key.charCodeAt(0); // I, U, B, 5
inner$("#innerdocbody").trigger(e);

View file

@ -6,22 +6,22 @@ describe("strikethrough button", function(){
});
it("makes text strikethrough", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first();
//select this text element
$firstTextElement.sendkeys('{selectall}');
//get the strikethrough button and click it
var $strikethroughButton = chrome$(".buttonicon-strikethrough");
$strikethroughButton.click();
//ace creates a new dom element when you press a button, so just get the first text element again
var $newFirstTextElement = inner$("div").first();
// is there a <i> element now?
var isstrikethrough = $newFirstTextElement.find("s").length === 1;

View file

@ -8,7 +8,7 @@ xdescribe("timeslider button takes you to the timeslider of a pad", function(){
it("timeslider contained in URL", function(done){
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
// get the first text element inside the editable space
var $firstTextElement = inner$("div span").first();
var originalValue = $firstTextElement.text(); // get the original value

View file

@ -0,0 +1,55 @@
describe("timeslider", function(){
//create a new pad before each test run
beforeEach(function(cb){
helper.newPad(cb);
this.timeout(6000);
});
it("follow content as it's added to timeslider", function(done) { // passes
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
// make some changes to produce 100 revisions
var timePerRev = 900
, revs = 10;
this.timeout(revs*timePerRev+10000);
for(var i=0; i < revs; i++) {
setTimeout(function() {
// enter 'a' in the first text element
inner$("div").last().sendkeys('a\n');
inner$("div").last().sendkeys('{enter}');
inner$("div").last().sendkeys('{enter}');
inner$("div").last().sendkeys('{enter}');
inner$("div").last().sendkeys('{enter}');
}, timePerRev*i);
}
setTimeout(function() {
// go to timeslider
$('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider#0');
setTimeout(function() {
var timeslider$ = $('#iframe-container iframe')[0].contentWindow.$;
var $sliderBar = timeslider$('#ui-slider-bar');
var latestContents = timeslider$('#innerdocbody').text();
// set to follow contents as it arrives
timeslider$('#options-followContents').prop("checked", true);
var originalTop = timeslider$('#innerdocbody').offset();
timeslider$('#playpause_button_icon').click();
setTimeout(function() {
//make sure the text has changed
var newTop = timeslider$('#innerdocbody').offset();
expect( originalTop ).not.to.eql( newTop );
done();
}, 1000);
}, 2000);
}, revs*timePerRev);
});
});

View file

@ -6,9 +6,9 @@ describe("timeslider", function(){
});
it("Shows a date and time in the timeslider and make sure it doesn't include NaN", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
// make some changes to produce 100 revisions
var revs = 10;
this.timeout(60000);
@ -18,15 +18,15 @@ describe("timeslider", function(){
inner$("div").first().sendkeys('a');
}, 200);
}
setTimeout(function() {
// go to timeslider
$('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider');
setTimeout(function() {
var timeslider$ = $('#iframe-container iframe')[0].contentWindow.$;
var $sliderBar = timeslider$('#ui-slider-bar');
var latestContents = timeslider$('#padcontent').text();
// Expect the date and time to be shown
@ -36,17 +36,17 @@ describe("timeslider", function(){
e.clientX = e.pageX = 150;
e.clientY = e.pageY = 45;
$sliderBar.trigger(e);
e = new jQuery.Event('mousedown');
e.clientX = e.pageX = 150;
e.clientY = e.pageY = 40;
$sliderBar.trigger(e);
e = new jQuery.Event('mousedown');
e.clientX = e.pageX = 150;
e.clientY = e.pageY = 50;
$sliderBar.trigger(e);
$sliderBar.trigger('mouseup')
setTimeout(function() {

View file

@ -0,0 +1,67 @@
describe("timeslider", function(){
var padId = 735773577357+(Math.round(Math.random()*1000));
//create a new pad before each test run
beforeEach(function(cb){
helper.newPad(cb, padId);
this.timeout(60000);
});
it("Makes sure the export URIs are as expected when the padID is numeric", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
// make some changes to produce 100 revisions
var revs = 10;
this.timeout(60000);
for(var i=0; i < revs; i++) {
setTimeout(function() {
// enter 'a' in the first text element
inner$("div").first().sendkeys('a');
}, 100);
}
setTimeout(function() {
// go to timeslider
$('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider');
setTimeout(function() {
var timeslider$ = $('#iframe-container iframe')[0].contentWindow.$;
var $sliderBar = timeslider$('#ui-slider-bar');
var latestContents = timeslider$('#padcontent').text();
// Expect the date and time to be shown
// Click somewhere on the timeslider
var e = new jQuery.Event('mousedown');
e.clientX = e.pageX = 150;
e.clientY = e.pageY = 45;
$sliderBar.trigger(e);
e = new jQuery.Event('mousedown');
e.clientX = e.pageX = 150;
e.clientY = e.pageY = 40;
$sliderBar.trigger(e);
e = new jQuery.Event('mousedown');
e.clientX = e.pageX = 150;
e.clientY = e.pageY = 50;
$sliderBar.trigger(e);
$sliderBar.trigger('mouseup')
setTimeout(function() {
// expect URI to be similar to
// http://192.168.1.48:9001/p/2/2/export/html
// http://192.168.1.48:9001/p/735773577399/0/export/html
var exportLink = timeslider$('#exporthtmla').attr('href');
var checkVal = padId + "/0/export/html";
var includesCorrectURI = exportLink.indexOf(checkVal);
expect(includesCorrectURI).to.not.be(-1);
done();
}, 400);
}, 2000);
}, 2000);
});
});

View file

@ -6,12 +6,12 @@ describe("timeslider", function(){
});
it("loads adds a hundred revisions", function(done) { // passes
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
// make some changes to produce 100 revisions
var timePerRev = 900
, revs = 100;
, revs = 99;
this.timeout(revs*timePerRev+10000);
for(var i=0; i < revs; i++) {
setTimeout(function() {
@ -20,43 +20,45 @@ describe("timeslider", function(){
}, timePerRev*i);
}
chrome$('.buttonicon-savedRevision').click();
setTimeout(function() {
// go to timeslider
$('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider');
setTimeout(function() {
var timeslider$ = $('#iframe-container iframe')[0].contentWindow.$;
var $sliderBar = timeslider$('#ui-slider-bar');
var latestContents = timeslider$('#padcontent').text();
var latestContents = timeslider$('#innerdocbody').text();
// Click somewhere on the timeslider
var e = new jQuery.Event('mousedown');
// sets y co-ordinate of the pad slider modal.
var base = (timeslider$('#ui-slider-bar').offset().top - 24)
e.clientX = e.pageX = 150;
e.clientY = e.pageY = 45;
e.clientY = e.pageY = base+5;
$sliderBar.trigger(e);
e = new jQuery.Event('mousedown');
e.clientX = e.pageX = 150;
e.clientY = e.pageY = 40;
e.clientY = e.pageY = base;
$sliderBar.trigger(e);
e = new jQuery.Event('mousedown');
e.clientX = e.pageX = 150;
e.clientY = e.pageY = 50;
e.clientY = e.pageY = base-5;
$sliderBar.trigger(e);
$sliderBar.trigger('mouseup')
setTimeout(function() {
//make sure the text has changed
expect( timeslider$('#padcontent').text() ).not.to.eql( latestContents );
expect( timeslider$('#innerdocbody').text() ).not.to.eql( latestContents );
var starIsVisible = timeslider$('.star').is(":visible");
expect( starIsVisible ).to.eql( true );
done();
}, 1000);
}, 6000);
}, revs*timePerRev);
});
@ -64,9 +66,9 @@ describe("timeslider", function(){
// Disabled as jquery trigger no longer works properly
xit("changes the url when clicking on the timeslider", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
// make some changes to produce 7 revisions
var timePerRev = 1000
, revs = 20;
@ -77,24 +79,24 @@ describe("timeslider", function(){
inner$("div").first().sendkeys('a');
}, timePerRev*i);
}
setTimeout(function() {
// go to timeslider
$('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider');
setTimeout(function() {
var timeslider$ = $('#iframe-container iframe')[0].contentWindow.$;
var $sliderBar = timeslider$('#ui-slider-bar');
var latestContents = timeslider$('#padcontent').text();
var latestContents = timeslider$('#innerdocbody').text();
var oldUrl = $('#iframe-container iframe')[0].contentWindow.location.hash;
// Click somewhere on the timeslider
var e = new jQuery.Event('mousedown');
e.clientX = e.pageX = 150;
e.clientY = e.pageY = 60;
$sliderBar.trigger(e);
helper.waitFor(function(){
return $('#iframe-container iframe')[0].contentWindow.location.hash != oldUrl;
}, 6000).always(function(){
@ -105,7 +107,7 @@ describe("timeslider", function(){
}, revs*timePerRev);
});
it("jumps to a revision given in the url", function(done) {
var inner$ = helper.padInner$;
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
this.timeout(20000);
@ -118,7 +120,7 @@ describe("timeslider", function(){
expect( oldLength ).to.not.eql( 0 );
inner$("div").first().sendkeys('a');
var timeslider$;
// wait for our additional revision to be added
helper.waitFor(function(){
// newLines takes the new lines into account which are strippen when using
@ -131,17 +133,17 @@ describe("timeslider", function(){
}, 6000).always(function() {
// go to timeslider with a specific revision set
$('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider#0');
// wait for the timeslider to be loaded
helper.waitFor(function(){
try {
timeslider$ = $('#iframe-container iframe')[0].contentWindow.$;
} catch(e){}
if(timeslider$){
return timeslider$('#padcontent').text().length == oldLength;
return timeslider$('#innerdocbody').text().length == oldLength;
}
}, 6000).always(function(){
expect( timeslider$('#padcontent').text().length ).to.eql( oldLength );
expect( timeslider$('#innerdocbody').text().length ).to.eql( oldLength );
done();
});
});
@ -149,17 +151,17 @@ describe("timeslider", function(){
});
it("checks the export url", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
this.timeout(11000);
inner$("div").first().sendkeys('a');
setTimeout(function() {
// go to timeslider
$('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider#0');
var timeslider$;
var exportLink;
helper.waitFor(function(){
try{
timeslider$ = $('#iframe-container iframe')[0].contentWindow.$;

View file

@ -4,11 +4,10 @@ describe("undo button", function(){
this.timeout(60000);
});
/*
it("undo some typing by clicking undo button", function(done){
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
// get the first text element inside the editable space
var $firstTextElement = inner$("div span").first();
var originalValue = $firstTextElement.text(); // get the original value
@ -30,7 +29,6 @@ describe("undo button", function(){
done();
});
});
*/
it("undo some typing using a keypress", function(done){
var inner$ = helper.padInner$;
@ -44,13 +42,7 @@ describe("undo button", function(){
var modifiedValue = $firstTextElement.text(); // get the modified value
expect(modifiedValue).not.to.be(originalValue); // expect the value to change
/*
* ACHTUNG: this is the only place in the test codebase in which a keydown
* is sent for IE. Everywhere else IE uses keypress.
*/
var evtType = "keydown";
var e = inner$.Event(evtType);
var e = inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key
e.which = 90; // z
inner$("#innerdocbody").trigger(e);

View file

@ -6,7 +6,7 @@ describe("assign unordered list", function(){
});
it("insert unordered list text then removes by outdent", function(done){
var inner$ = helper.padInner$;
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var originalText = inner$("div").first().text();
@ -33,3 +33,145 @@ describe("assign unordered list", function(){
});
});
describe("unassign unordered list", function(){
//create a new pad before each test run
beforeEach(function(cb){
helper.newPad(cb);
this.timeout(60000);
});
it("insert unordered list text then remove by clicking list again", function(done){
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var originalText = inner$("div").first().text();
var $insertunorderedlistButton = chrome$(".buttonicon-insertunorderedlist");
$insertunorderedlistButton.click();
helper.waitFor(function(){
var newText = inner$("div").first().text();
if(newText === originalText){
return inner$("div").first().find("ul li").length === 1;
}
}).done(function(){
// remove indentation by bullet and ensure text string remains the same
$insertunorderedlistButton.click();
helper.waitFor(function(){
var isList = inner$("div").find("ul").length === 1;
// sohuldn't be list
return (isList === false);
}).done(function(){
done();
});
});
});
});
describe("keep unordered list on enter key", function(){
//create a new pad before each test run
beforeEach(function(cb){
helper.newPad(cb);
this.timeout(60000);
});
it("Keeps the unordered list on enter for the new line", function(done){
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var $insertorderedlistButton = chrome$(".buttonicon-insertunorderedlist");
$insertorderedlistButton.click();
//type a bit, make a line break and type again
var $firstTextElement = inner$("div span").first();
$firstTextElement.sendkeys('line 1');
$firstTextElement.sendkeys('{enter}');
$firstTextElement.sendkeys('line 2');
$firstTextElement.sendkeys('{enter}');
helper.waitFor(function(){
return inner$("div span").first().text().indexOf("line 2") === -1;
}).done(function(){
var $newSecondLine = inner$("div").first().next();
var hasULElement = $newSecondLine.find("ul li").length === 1;
expect(hasULElement).to.be(true);
expect($newSecondLine.text()).to.be("line 2");
done();
});
});
});
describe("Pressing Tab in an UL increases and decreases indentation", function(){
//create a new pad before each test run
beforeEach(function(cb){
helper.newPad(cb);
this.timeout(60000);
});
it("indent and de-indent list item with keypress", function(done){
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first();
//select this text element
$firstTextElement.sendkeys('{selectall}');
var $insertorderedlistButton = chrome$(".buttonicon-insertunorderedlist");
$insertorderedlistButton.click();
var e = inner$.Event(helper.evtType);
e.keyCode = 9; // tab
inner$("#innerdocbody").trigger(e);
expect(inner$("div").first().find(".list-bullet2").length === 1).to.be(true);
e.shiftKey = true; // shift
e.keyCode = 9; // tab
inner$("#innerdocbody").trigger(e);
helper.waitFor(function(){
return inner$("div").first().find(".list-bullet1").length === 1;
}).done(done);
});
});
describe("Pressing indent/outdent button in an UL increases and decreases indentation and bullet / ol formatting", function(){
//create a new pad before each test run
beforeEach(function(cb){
helper.newPad(cb);
this.timeout(60000);
});
it("indent and de-indent list item with indent button", function(done){
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first();
//select this text element
$firstTextElement.sendkeys('{selectall}');
var $insertunorderedlistButton = chrome$(".buttonicon-insertunorderedlist");
$insertunorderedlistButton.click();
var $indentButton = chrome$(".buttonicon-indent");
$indentButton.click(); // make it indented twice
expect(inner$("div").first().find(".list-bullet2").length === 1).to.be(true);
var $outdentButton = chrome$(".buttonicon-outdent");
$outdentButton.click(); // make it deindented to 1
helper.waitFor(function(){
return inner$("div").first().find(".list-bullet1").length === 1;
}).done(done);
});
});

View file

@ -6,16 +6,16 @@ describe("urls", function(){
});
it("when you enter an url, it becomes clickable", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe
var firstTextElement = inner$("div").first();
// simulate key presses to delete content
firstTextElement.sendkeys('{selectall}'); // select all
firstTextElement.sendkeys('{del}'); // clear the first line
firstTextElement.sendkeys('http://etherpad.org'); // insert a URL
firstTextElement.sendkeys('https://etherpad.org'); // insert a URL
helper.waitFor(function(){
return inner$("div").first().find("a").length === 1;
@ -28,7 +28,7 @@ describe("urls", function(){
//get the first text element out of the inner iframe
var firstTextElement = inner$("div").first();
var url = "http://etherpad.org/!foo";
var url = "https://etherpad.org/!foo";
// simulate key presses to delete content
firstTextElement.sendkeys('{selectall}'); // select all
@ -50,7 +50,7 @@ describe("urls", function(){
//get the first text element out of the inner iframe
var firstTextElement = inner$("div").first();
var url = "http://etherpad.org/";
var url = "https://etherpad.org/";
// simulate key presses to delete content
firstTextElement.sendkeys('{selectall}'); // select all

View file

@ -17,95 +17,125 @@ var sauceTestWorker = async.queue(function (testSettings, callback) {
testSettings.name = name;
testSettings["public"] = true;
testSettings["build"] = process.env.GIT_HASH;
testSettings["extendedDebugging"] = true; // console.json can be downloaded via saucelabs, don't know how to print them into output of the tests
testSettings["tunnelIdentifier"] = process.env.TRAVIS_JOB_NUMBER;
browser.init(testSettings).get("http://localhost:9001/tests/frontend/", function(){
var url = "https://saucelabs.com/jobs/" + browser.sessionID;
console.log("Remote sauce test '" + name + "' started! " + url);
browser.init(testSettings).get("http://localhost:9001/tests/frontend/", function(){
var url = "https://saucelabs.com/jobs/" + browser.sessionID;
console.log("Remote sauce test '" + name + "' started! " + url);
//tear down the test excecution
var stopSauce = function(success){
getStatusInterval && clearInterval(getStatusInterval);
clearTimeout(timeout);
//tear down the test excecution
var stopSauce = function(success,timesup){
clearInterval(getStatusInterval);
clearTimeout(timeout);
browser.quit();
browser.quit(function(){
if(!success){
allTestsPassed = false;
}
if(!success){
allTestsPassed = false;
// if stopSauce is called via timeout (in contrast to via getStatusInterval) than the log of up to the last
// five seconds may not be available here. It's an error anyway, so don't care about it.
var testResult = knownConsoleText.replace(/\[red\]/g,'\x1B[31m').replace(/\[yellow\]/g,'\x1B[33m')
.replace(/\[green\]/g,'\x1B[32m').replace(/\[clear\]/g, '\x1B[39m');
testResult = testResult.split("\\n").map(function(line){
return "[" + testSettings.browserName + " " + testSettings.platform + (testSettings.version === "" ? '' : (" " + testSettings.version)) + "] " + line;
}).join("\n");
console.log(testResult);
if (timesup) {
console.log("[" + testSettings.browserName + " " + testSettings.platform + (testSettings.version === "" ? '' : (" " + testSettings.version)) + "] allowed test duration exceeded");
}
console.log("Remote sauce test '" + name + "' finished! " + url);
callback();
});
}
var testResult = knownConsoleText.replace(/\[red\]/g,'\x1B[31m').replace(/\[yellow\]/g,'\x1B[33m')
.replace(/\[green\]/g,'\x1B[32m').replace(/\[clear\]/g, '\x1B[39m');
testResult = testResult.split("\\n").map(function(line){
return "[" + testSettings.browserName + (testSettings.version === "" ? '' : (" " + testSettings.version)) + "] " + line;
}).join("\n");
/**
* timeout if a test hangs or the job exceeds 9.5 minutes
* It's necessary because if travis kills the saucelabs session due to inactivity, we don't get any output
* @todo this should be configured in testSettings, see https://wiki.saucelabs.com/display/DOCS/Test+Configuration+Options#TestConfigurationOptions-Timeouts
*/
var timeout = setTimeout(function(){
stopSauce(false,true);
}, 570000); // travis timeout is 10 minutes, set this to a slightly lower value
console.log(testResult);
console.log("Remote sauce test '" + name + "' finished! " + url);
var knownConsoleText = "";
var getStatusInterval = setInterval(function(){
browser.eval("$('#console').text()", function(err, consoleText){
if(!consoleText || err){
return;
}
knownConsoleText = consoleText;
callback();
}
if(knownConsoleText.indexOf("FINISHED") > 0){
let match = knownConsoleText.match(/FINISHED.*([0-9]+) tests passed, ([0-9]+) tests failed/);
// finished without failures
if (match[2] && match[2] == '0'){
stopSauce(true);
//timeout for the case the test hangs
var timeout = setTimeout(function(){
stopSauce(false);
}, 60000 * 10);
// finished but some tests did not return or some tests failed
} else {
stopSauce(false);
}
}
});
}, 5000);
});
var knownConsoleText = "";
var getStatusInterval = setInterval(function(){
browser.eval("$('#console').text()", function(err, consoleText){
if(!consoleText || err){
return;
}
knownConsoleText = consoleText;
}, 6); //run 6 tests in parrallel
if(knownConsoleText.indexOf("FINISHED") > 0){
var success = knownConsoleText.indexOf("FAILED") === -1;
stopSauce(success);
}
});
}, 5000);
});
}, 5); //run 5 tests in parrallel
// Firefox
// 1) Firefox on Linux
sauceTestWorker.push({
'platform' : 'Linux'
'platform' : 'Windows 7'
, 'browserName' : 'firefox'
, 'version' : ''
, 'version' : '52.0'
});
// Chrome
// 2) Chrome on Linux
sauceTestWorker.push({
'platform' : 'Linux'
, 'browserName' : 'googlechrome'
, 'version' : ''
'platform' : 'Windows 7'
, 'browserName' : 'chrome'
, 'version' : '55.0'
, 'args' : ['--use-fake-device-for-media-stream']
});
// 3) Safari on OSX 10.15
sauceTestWorker.push({
'platform' : 'OS X 10.15'
, 'browserName' : 'safari'
, 'version' : '13.1'
});
// 4) Safari on OSX 10.14
sauceTestWorker.push({
'platform' : 'OS X 10.14'
, 'browserName' : 'safari'
, 'version' : '12.0'
});
// IE 10 doesn't appear to be working anyway
/*
// IE 8
// 4) IE 10 on Win 8
sauceTestWorker.push({
'platform' : 'Windows 2003'
'platform' : 'Windows 8'
, 'browserName' : 'iexplore'
, 'version' : '8'
, 'version' : '10.0'
});
*/
// IE 9
// 5) Edge on Win 10
sauceTestWorker.push({
'platform' : 'Windows XP'
, 'browserName' : 'iexplore'
, 'version' : '9'
'platform' : 'Windows 10'
, 'browserName' : 'microsoftedge'
, 'version' : '83.0'
});
// 6) Firefox on Win 7
sauceTestWorker.push({
'platform' : 'Windows 7'
, 'browserName' : 'firefox'
, 'version' : '78.0'
});
// IE 10
sauceTestWorker.push({
'platform' : 'Windows 2012'
, 'browserName' : 'iexplore'
, 'version' : '10'
sauceTestWorker.drain(function() {
process.exit(allTestsPassed ? 0 : 1);
});
sauceTestWorker.drain = function() {
setTimeout(function(){
process.exit(allTestsPassed ? 0 : 1);
}, 3000);
}

View file

@ -1,18 +1,48 @@
#!/bin/sh
#!/bin/bash
if [ -z "${SAUCE_USERNAME}" ]; then echo "SAUCE_USERNAME is unset - exiting"; exit 1; fi
if [ -z "${SAUCE_ACCESS_KEY}" ]; then echo "SAUCE_ACCESS_KEY is unset - exiting"; exit 1; fi
#Move to the base folder
cd `dirname $0`
# do not continue if there is an error
set -eu
#start Etherpad
../../../bin/run.sh > /dev/null &
sleep 10
# source: https://stackoverflow.com/questions/59895/get-the-source-directory-of-a-bash-script-from-within-the-script-itself#246128
MY_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
#start remote runner
# reliably move to the etherpad base folder before running it
cd "${MY_DIR}/../../../"
# start Etherpad, assuming all dependencies are already installed.
#
# This is possible because the "install" section of .travis.yml already contains
# a call to bin/installDeps.sh
echo "Running Etherpad directly, assuming bin/installDeps.sh has already been run"
node node_modules/ep_etherpad-lite/node/server.js "${@}" &
echo "Now I will try for 15 seconds to connect to Etherpad on http://localhost:9001"
# wait for at most 15 seconds until Etherpad starts accepting connections
#
# modified from:
# https://unix.stackexchange.com/questions/5277/how-do-i-tell-a-script-to-wait-for-a-process-to-start-accepting-requests-on-a-po#349138
#
(timeout 15 bash -c 'until echo > /dev/tcp/localhost/9001; do sleep 0.5; done') || \
(echo "Could not connect to Etherpad on http://localhost:9001" ; exit 1)
echo "Successfully connected to Etherpad on http://localhost:9001"
# On the Travis VM, remote_runner.js is found at
# /home/travis/build/ether/[secure]/tests/frontend/travis/remote_runner.js
# which is the same directory that contains this script.
# Let's move back there.
#
# Probably remote_runner.js is injected by Saucelabs.
cd "${MY_DIR}"
# start the remote runner
echo "Now starting the remote runner"
node remote_runner.js
exit_code=$?
kill $!
kill $(cat /tmp/sauce.pid)
sleep 30
exit $exit_code
exit $exit_code

View file

@ -0,0 +1,51 @@
#!/bin/bash
# do not continue if there is an error
set -u
# source: https://stackoverflow.com/questions/59895/get-the-source-directory-of-a-bash-script-from-within-the-script-itself#246128
MY_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
# reliably move to the etherpad base folder before running it
cd "${MY_DIR}/../../../"
# Set soffice to /usr/bin/soffice
sed 's#\"soffice\": null,#\"soffice\":\"/usr/bin/soffice\",#g' settings.json.template > settings.json.soffice
# Set allowAnyoneToImport to true
sed 's/\"allowAnyoneToImport\": false,/\"allowAnyoneToImport\": true,/g' settings.json.soffice > settings.json.allowImport
# Set "max": 10 to 100 to not agressively rate limit
sed 's/\"max\": 10/\"max\": 100/g' settings.json.allowImport > settings.json.rateLimit
# Set "points": 10 to 1000 to not agressively rate limit commits
sed 's/\"points\": 10/\"points\": 1000/g' settings.json.rateLimit > settings.json
# start Etherpad, assuming all dependencies are already installed.
#
# This is possible because the "install" section of .travis.yml already contains
# a call to bin/installDeps.sh
echo "Running Etherpad directly, assuming bin/installDeps.sh has already been run"
node node_modules/ep_etherpad-lite/node/server.js "${@}" > /dev/null &
echo "Now I will try for 15 seconds to connect to Etherpad on http://localhost:9001"
# wait for at most 15 seconds until Etherpad starts accepting connections
#
# modified from:
# https://unix.stackexchange.com/questions/5277/how-do-i-tell-a-script-to-wait-for-a-process-to-start-accepting-requests-on-a-po#349138
#
(timeout 15 bash -c 'until echo > /dev/tcp/localhost/9001; do sleep 0.5; done') || \
(echo "Could not connect to Etherpad on http://localhost:9001" ; exit 1)
echo "Successfully connected to Etherpad on http://localhost:9001"
# run the backend tests
echo "Now run the backend tests"
cd src
failed=0
npm run test || failed=1
npm run test-contentcollector || failed=1
exit $failed

View file

@ -0,0 +1,51 @@
#!/bin/bash
# do not continue if there is an error
set -eu
# source: https://stackoverflow.com/questions/59895/get-the-source-directory-of-a-bash-script-from-within-the-script-itself#246128
MY_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
# reliably move to the etherpad base folder before running it
cd "${MY_DIR}/../../../"
# Set "points": 10 to 1000 to not agressively rate limit commits
sed 's/\"points\": 10/\"points\": 1000/g' settings.json.template > settings.json.points
# And enable loadTest
sed 's/\"loadTest\": false,/\"loadTest\": true,/g' settings.json.points > settings.json
# start Etherpad, assuming all dependencies are already installed.
#
# This is possible because the "install" section of .travis.yml already contains
# a call to bin/installDeps.sh
echo "Running Etherpad directly, assuming bin/installDeps.sh has already been run"
node node_modules/ep_etherpad-lite/node/server.js "${@}" > /dev/null &
echo "Now I will try for 15 seconds to connect to Etherpad on http://localhost:9001"
# wait for at most 15 seconds until Etherpad starts accepting connections
#
# modified from:
# https://unix.stackexchange.com/questions/5277/how-do-i-tell-a-script-to-wait-for-a-process-to-start-accepting-requests-on-a-po#349138
#
(timeout 15 bash -c 'until echo > /dev/tcp/localhost/9001; do sleep 0.5; done') || \
(echo "Could not connect to Etherpad on http://localhost:9001" ; exit 1)
echo "Successfully connected to Etherpad on http://localhost:9001"
# Build the minified files?
curl http://localhost:9001/p/minifyme -f -s > /dev/null
# just in case, let's wait for another 10 seconds before going on
sleep 10
# run the backend tests
echo "Now run the load tests for 30 seconds and if it stalls before 100 then error"
etherpad-loadtest -d 30
exit_code=$?
kill $!
sleep 5
exit $exit_code

View file

@ -1,11 +1,20 @@
#!/bin/bash
# download and unzip the sauce connector
curl https://saucelabs.com/downloads/sc-latest-linux.tar.gz > /tmp/sauce.tar.gz
#
# ACHTUNG: as of 2019-12-21, downloading sc-latest-linux.tar.gz does not work.
# It is necessary to explicitly download a specific version, for
# example https://saucelabs.com/downloads/sc-4.5.4-linux.tar.gz
# Supported versions are currently listed at:
# https://wiki.saucelabs.com/display/DOCS/Downloading+Sauce+Connect+Proxy
if [ -z "${SAUCE_USERNAME}" ]; then echo "SAUCE_USERNAME is unset - exiting"; exit 1; fi
if [ -z "${SAUCE_ACCESS_KEY}" ]; then echo "SAUCE_ACCESS_KEY is unset - exiting"; exit 1; fi
curl https://saucelabs.com/downloads/sc-4.6.2-linux.tar.gz > /tmp/sauce.tar.gz
tar zxf /tmp/sauce.tar.gz --directory /tmp
mv /tmp/sc-*-linux /tmp/sauce_connect
# start the sauce connector in background and make sure it doesn't output the secret key
(/tmp/sauce_connect/bin/sc --user $SAUCE_USERNAME --key $SAUCE_ACCESS_KEY --pidfile /tmp/sauce.pid --readyfile /tmp/tunnel > /dev/null )&
(/tmp/sauce_connect/bin/sc --user "${SAUCE_USERNAME}" --key "${SAUCE_ACCESS_KEY}" -i "${TRAVIS_JOB_NUMBER}" --pidfile /tmp/sauce.pid --readyfile /tmp/tunnel > /dev/null )&
# wait for the tunnel to build up
while [ ! -e "/tmp/tunnel" ]