lint: Run eslint --fix on bin/ and tests/

This commit is contained in:
Richard Hansen 2020-11-23 13:21:51 -05:00 committed by John McLear
parent 0625739cb8
commit b8d07a42eb
78 changed files with 4319 additions and 4599 deletions

View file

@ -3,88 +3,85 @@
*/ */
if (process.argv.length != 2) { if (process.argv.length != 2) {
console.error("Use: node bin/checkAllPads.js"); console.error('Use: node bin/checkAllPads.js');
process.exit(1); process.exit(1);
} }
// load and initialize NPM // load and initialize NPM
let npm = require('../src/node_modules/npm'); const npm = require('../src/node_modules/npm');
npm.load({}, async function() { npm.load({}, async () => {
try { try {
// initialize the database // initialize the database
let settings = require('../src/node/utils/Settings'); const settings = require('../src/node/utils/Settings');
let db = require('../src/node/db/DB'); const db = require('../src/node/db/DB');
await db.init(); await db.init();
// load modules // load modules
let Changeset = require('../src/static/js/Changeset'); const Changeset = require('../src/static/js/Changeset');
let padManager = require('../src/node/db/PadManager'); const padManager = require('../src/node/db/PadManager');
// get all pads // get all pads
let res = await padManager.listAllPads(); const res = await padManager.listAllPads();
for (let padId of res.padIDs) { for (const padId of res.padIDs) {
const pad = await padManager.getPad(padId);
let pad = await padManager.getPad(padId);
// check if the pad has a pool // check if the pad has a pool
if (pad.pool === undefined) { if (pad.pool === undefined) {
console.error("[" + pad.id + "] Missing attribute pool"); console.error(`[${pad.id}] Missing attribute pool`);
continue; continue;
} }
// create an array with key kevisions // create an array with key kevisions
// key revisions always save the full pad atext // key revisions always save the full pad atext
let head = pad.getHeadRevisionNumber(); const head = pad.getHeadRevisionNumber();
let keyRevisions = []; const keyRevisions = [];
for (let rev = 0; rev < head; rev += 100) { for (let rev = 0; rev < head; rev += 100) {
keyRevisions.push(rev); keyRevisions.push(rev);
} }
// run through all key revisions // run through all key revisions
for (let keyRev of keyRevisions) { for (const keyRev of keyRevisions) {
// create an array of revisions we need till the next keyRevision or the End // create an array of revisions we need till the next keyRevision or the End
var revisionsNeeded = []; const revisionsNeeded = [];
for (let rev = keyRev ; rev <= keyRev + 100 && rev <= head; rev++) { for (let rev = keyRev; rev <= keyRev + 100 && rev <= head; rev++) {
revisionsNeeded.push(rev); revisionsNeeded.push(rev);
} }
// this array will hold all revision changesets // this array will hold all revision changesets
var revisions = []; const revisions = [];
// run through all needed revisions and get them from the database // run through all needed revisions and get them from the database
for (let revNum of revisionsNeeded) { for (const revNum of revisionsNeeded) {
let revision = await db.get("pad:" + pad.id + ":revs:" + revNum); const revision = await db.get(`pad:${pad.id}:revs:${revNum}`);
revisions[revNum] = revision; revisions[revNum] = revision;
} }
// check if the revision exists // check if the revision exists
if (revisions[keyRev] == null) { if (revisions[keyRev] == null) {
console.error("[" + pad.id + "] Missing revision " + keyRev); console.error(`[${pad.id}] Missing revision ${keyRev}`);
continue; continue;
} }
// check if there is a atext in the keyRevisions // check if there is a atext in the keyRevisions
if (revisions[keyRev].meta === undefined || revisions[keyRev].meta.atext === undefined) { if (revisions[keyRev].meta === undefined || revisions[keyRev].meta.atext === undefined) {
console.error("[" + pad.id + "] Missing atext in revision " + keyRev); console.error(`[${pad.id}] Missing atext in revision ${keyRev}`);
continue; continue;
} }
let apool = pad.pool; const apool = pad.pool;
let atext = revisions[keyRev].meta.atext; let atext = revisions[keyRev].meta.atext;
for (let rev = keyRev + 1; rev <= keyRev + 100 && rev <= head; rev++) { for (let rev = keyRev + 1; rev <= keyRev + 100 && rev <= head; rev++) {
try { try {
let cs = revisions[rev].changeset; const cs = revisions[rev].changeset;
atext = Changeset.applyToAText(cs, atext, apool); atext = Changeset.applyToAText(cs, atext, apool);
} catch (e) { } catch (e) {
console.error("[" + pad.id + "] Bad changeset at revision " + i + " - " + e.message); console.error(`[${pad.id}] Bad changeset at revision ${i} - ${e.message}`);
} }
} }
} }
console.log("finished"); console.log('finished');
process.exit(0); process.exit(0);
} }
} catch (err) { } catch (err) {

View file

@ -3,7 +3,7 @@
*/ */
if (process.argv.length != 3) { if (process.argv.length != 3) {
console.error("Use: node bin/checkPad.js $PADID"); console.error('Use: node bin/checkPad.js $PADID');
process.exit(1); process.exit(1);
} }
@ -11,83 +11,80 @@ if (process.argv.length != 3) {
const padId = process.argv[2]; const padId = process.argv[2];
// load and initialize NPM; // load and initialize NPM;
let npm = require('../src/node_modules/npm'); const npm = require('../src/node_modules/npm');
npm.load({}, async function() { npm.load({}, async () => {
try { try {
// initialize database // initialize database
let settings = require('../src/node/utils/Settings'); const settings = require('../src/node/utils/Settings');
let db = require('../src/node/db/DB'); const db = require('../src/node/db/DB');
await db.init(); await db.init();
// load modules // load modules
let Changeset = require('ep_etherpad-lite/static/js/Changeset'); const Changeset = require('ep_etherpad-lite/static/js/Changeset');
let padManager = require('../src/node/db/PadManager'); const padManager = require('../src/node/db/PadManager');
let exists = await padManager.doesPadExists(padId); const exists = await padManager.doesPadExists(padId);
if (!exists) { if (!exists) {
console.error("Pad does not exist"); console.error('Pad does not exist');
process.exit(1); process.exit(1);
} }
// get the pad // get the pad
let pad = await padManager.getPad(padId); const pad = await padManager.getPad(padId);
// create an array with key revisions // create an array with key revisions
// key revisions always save the full pad atext // key revisions always save the full pad atext
let head = pad.getHeadRevisionNumber(); const head = pad.getHeadRevisionNumber();
let keyRevisions = []; const keyRevisions = [];
for (let rev = 0; rev < head; rev += 100) { for (let rev = 0; rev < head; rev += 100) {
keyRevisions.push(rev); keyRevisions.push(rev);
} }
// run through all key revisions // run through all key revisions
for (let keyRev of keyRevisions) { for (const keyRev of keyRevisions) {
// create an array of revisions we need till the next keyRevision or the End // create an array of revisions we need till the next keyRevision or the End
let revisionsNeeded = []; const revisionsNeeded = [];
for (let rev = keyRev; rev <= keyRev + 100 && rev <= head; rev++) { for (let rev = keyRev; rev <= keyRev + 100 && rev <= head; rev++) {
revisionsNeeded.push(rev); revisionsNeeded.push(rev);
} }
// this array will hold all revision changesets // this array will hold all revision changesets
var revisions = []; const revisions = [];
// run through all needed revisions and get them from the database // run through all needed revisions and get them from the database
for (let revNum of revisionsNeeded) { for (const revNum of revisionsNeeded) {
let revision = await db.get("pad:" + padId + ":revs:" + revNum); const revision = await db.get(`pad:${padId}:revs:${revNum}`);
revisions[revNum] = revision; revisions[revNum] = revision;
} }
// check if the pad has a pool // check if the pad has a pool
if (pad.pool === undefined ) { if (pad.pool === undefined) {
console.error("Attribute pool is missing"); console.error('Attribute pool is missing');
process.exit(1); process.exit(1);
} }
// check if there is an atext in the keyRevisions // check if there is an atext in the keyRevisions
if (revisions[keyRev] === undefined || revisions[keyRev].meta === undefined || revisions[keyRev].meta.atext === undefined) { if (revisions[keyRev] === undefined || revisions[keyRev].meta === undefined || revisions[keyRev].meta.atext === undefined) {
console.error("No atext in key revision " + keyRev); console.error(`No atext in key revision ${keyRev}`);
continue; continue;
} }
let apool = pad.pool; const apool = pad.pool;
let atext = revisions[keyRev].meta.atext; let atext = revisions[keyRev].meta.atext;
for (let rev = keyRev + 1; rev <= keyRev + 100 && rev <= head; rev++) { for (let rev = keyRev + 1; rev <= keyRev + 100 && rev <= head; rev++) {
try { try {
// console.log("check revision " + rev); // console.log("check revision " + rev);
let cs = revisions[rev].changeset; const cs = revisions[rev].changeset;
atext = Changeset.applyToAText(cs, atext, apool); atext = Changeset.applyToAText(cs, atext, apool);
} catch(e) { } catch (e) {
console.error("Bad changeset at revision " + rev + " - " + e.message); console.error(`Bad changeset at revision ${rev} - ${e.message}`);
continue; continue;
} }
} }
console.log("finished"); console.log('finished');
process.exit(0); process.exit(0);
} }
} catch (e) { } catch (e) {
console.trace(e); console.trace(e);
process.exit(1); process.exit(1);

View file

@ -3,7 +3,7 @@
*/ */
if (process.argv.length != 3) { if (process.argv.length != 3) {
console.error("Use: node bin/checkPadDeltas.js $PADID"); console.error('Use: node bin/checkPadDeltas.js $PADID');
process.exit(1); process.exit(1);
} }
@ -11,108 +11,101 @@ if (process.argv.length != 3) {
const padId = process.argv[2]; const padId = process.argv[2];
// load and initialize NPM; // load and initialize NPM;
var expect = require('expect.js') const expect = require('expect.js');
var diff = require('diff') const diff = require('diff');
var async = require('async') var async = require('async');
let npm = require('../src/node_modules/npm'); const npm = require('../src/node_modules/npm');
var async = require("ep_etherpad-lite/node_modules/async"); var async = require('ep_etherpad-lite/node_modules/async');
var Changeset = require("ep_etherpad-lite/static/js/Changeset"); const Changeset = require('ep_etherpad-lite/static/js/Changeset');
npm.load({}, async function() {
npm.load({}, async () => {
try { try {
// initialize database // initialize database
let settings = require('../src/node/utils/Settings'); const settings = require('../src/node/utils/Settings');
let db = require('../src/node/db/DB'); const db = require('../src/node/db/DB');
await db.init(); await db.init();
// load modules // load modules
let Changeset = require('ep_etherpad-lite/static/js/Changeset'); const Changeset = require('ep_etherpad-lite/static/js/Changeset');
let padManager = require('../src/node/db/PadManager'); const padManager = require('../src/node/db/PadManager');
let exists = await padManager.doesPadExists(padId); const exists = await padManager.doesPadExists(padId);
if (!exists) { if (!exists) {
console.error("Pad does not exist"); console.error('Pad does not exist');
process.exit(1); process.exit(1);
} }
// get the pad // get the pad
let pad = await padManager.getPad(padId); const pad = await padManager.getPad(padId);
//create an array with key revisions // create an array with key revisions
//key revisions always save the full pad atext // key revisions always save the full pad atext
var head = pad.getHeadRevisionNumber(); const head = pad.getHeadRevisionNumber();
var keyRevisions = []; const keyRevisions = [];
for(var i=0;i<head;i+=100) for (var i = 0; i < head; i += 100) {
{
keyRevisions.push(i); keyRevisions.push(i);
} }
//create an array with all revisions // create an array with all revisions
var revisions = []; const revisions = [];
for(var i=0;i<=head;i++) for (var i = 0; i <= head; i++) {
{
revisions.push(i); revisions.push(i);
} }
var atext = Changeset.makeAText("\n") let atext = Changeset.makeAText('\n');
//run trough all revisions // run trough all revisions
async.forEachSeries(revisions, function(revNum, callback) { async.forEachSeries(revisions, (revNum, callback) => {
//console.log('Fetching', revNum) // console.log('Fetching', revNum)
db.db.get("pad:"+padId+":revs:" + revNum, function(err, revision) { db.db.get(`pad:${padId}:revs:${revNum}`, (err, revision) => {
if(err) return callback(err); if (err) return callback(err);
//check if there is a atext in the keyRevisions // check if there is a atext in the keyRevisions
if(~keyRevisions.indexOf(revNum) && (revision === undefined || revision.meta === undefined || revision.meta.atext === undefined)) { if (~keyRevisions.indexOf(revNum) && (revision === undefined || revision.meta === undefined || revision.meta.atext === undefined)) {
console.error("No atext in key revision " + revNum); console.error(`No atext in key revision ${revNum}`);
callback(); callback();
return; return;
} }
try { try {
//console.log("check revision ", revNum); // console.log("check revision ", revNum);
var cs = revision.changeset; const cs = revision.changeset;
atext = Changeset.applyToAText(cs, atext, pad.pool); atext = Changeset.applyToAText(cs, atext, pad.pool);
} } catch (e) {
catch(e) { console.error(`Bad changeset at revision ${revNum} - ${e.message}`);
console.error("Bad changeset at revision " + revNum + " - " + e.message);
callback(); callback();
return; return;
} }
if(~keyRevisions.indexOf(revNum)) { if (~keyRevisions.indexOf(revNum)) {
try { try {
expect(revision.meta.atext.text).to.eql(atext.text) expect(revision.meta.atext.text).to.eql(atext.text);
expect(revision.meta.atext.attribs).to.eql(atext.attribs) expect(revision.meta.atext.attribs).to.eql(atext.attribs);
}catch(e) { } catch (e) {
console.error("Atext in key revision "+revNum+" doesn't match computed one.") console.error(`Atext in key revision ${revNum} doesn't match computed one.`);
console.log(diff.diffChars(atext.text, revision.meta.atext.text).map(function(op) {if(!op.added && !op.removed) op.value = op.value.length; return op})) console.log(diff.diffChars(atext.text, revision.meta.atext.text).map((op) => { if (!op.added && !op.removed) op.value = op.value.length; return op; }));
//console.error(e) // console.error(e)
//console.log('KeyRev. :', revision.meta.atext) // console.log('KeyRev. :', revision.meta.atext)
//console.log('Computed:', atext) // console.log('Computed:', atext)
callback() callback();
return return;
} }
} }
setImmediate(callback) setImmediate(callback);
}); });
}, function(er) { }, (er) => {
if(pad.atext.text == atext.text) console.log('ok') if (pad.atext.text == atext.text) { console.log('ok'); } else {
else { console.error('Pad AText doesn\'t match computed one! (Computed ', atext.text.length, ', db', pad.atext.text.length, ')');
console.error('Pad AText doesn\'t match computed one! (Computed ',atext.text.length, ', db', pad.atext.text.length,')') console.log(diff.diffChars(atext.text, pad.atext.text).map((op) => { if (!op.added && !op.removed) op.value = op.value.length; return op; }));
console.log(diff.diffChars(atext.text, pad.atext.text).map(function(op) {if(!op.added && !op.removed) op.value = op.value.length; return op}))
} }
callback(er) callback(er);
}); });
process.exit(0); process.exit(0);
} catch (e) { } catch (e) {
console.trace(e); console.trace(e);
process.exit(1); process.exit(1);
} }
}); });

View file

@ -1,120 +1,116 @@
var startTime = Date.now(); const startTime = Date.now();
var fs = require("fs"); const fs = require('fs');
var ueberDB = require("../src/node_modules/ueberdb2"); const ueberDB = require('../src/node_modules/ueberdb2');
var mysql = require("../src/node_modules/ueberdb2/node_modules/mysql"); const mysql = require('../src/node_modules/ueberdb2/node_modules/mysql');
var async = require("../src/node_modules/async"); const async = require('../src/node_modules/async');
var Changeset = require("ep_etherpad-lite/static/js/Changeset"); const Changeset = require('ep_etherpad-lite/static/js/Changeset');
var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString; const randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;
var AttributePool = require("ep_etherpad-lite/static/js/AttributePool"); const AttributePool = require('ep_etherpad-lite/static/js/AttributePool');
var settingsFile = process.argv[2]; const settingsFile = process.argv[2];
var sqlOutputFile = process.argv[3]; const sqlOutputFile = process.argv[3];
//stop if the settings file is not set // stop if the settings file is not set
if(!settingsFile || !sqlOutputFile) if (!settingsFile || !sqlOutputFile) {
{ console.error('Use: node convert.js $SETTINGSFILE $SQLOUTPUT');
console.error("Use: node convert.js $SETTINGSFILE $SQLOUTPUT");
process.exit(1); process.exit(1);
} }
log("read settings file..."); log('read settings file...');
//read the settings file and parse the json // read the settings file and parse the json
var settings = JSON.parse(fs.readFileSync(settingsFile, "utf8")); const settings = JSON.parse(fs.readFileSync(settingsFile, 'utf8'));
log("done"); log('done');
log("open output file..."); log('open output file...');
var sqlOutput = fs.openSync(sqlOutputFile, "w"); const sqlOutput = fs.openSync(sqlOutputFile, 'w');
var sql = "SET CHARACTER SET UTF8;\n" + const sql = 'SET CHARACTER SET UTF8;\n' +
"CREATE TABLE IF NOT EXISTS `store` ( \n" + 'CREATE TABLE IF NOT EXISTS `store` ( \n' +
"`key` VARCHAR( 100 ) NOT NULL , \n" + '`key` VARCHAR( 100 ) NOT NULL , \n' +
"`value` LONGTEXT NOT NULL , \n" + '`value` LONGTEXT NOT NULL , \n' +
"PRIMARY KEY ( `key` ) \n" + 'PRIMARY KEY ( `key` ) \n' +
") ENGINE = INNODB;\n" + ') ENGINE = INNODB;\n' +
"START TRANSACTION;\n\n"; 'START TRANSACTION;\n\n';
fs.writeSync(sqlOutput, sql); fs.writeSync(sqlOutput, sql);
log("done"); log('done');
var etherpadDB = mysql.createConnection({ const etherpadDB = mysql.createConnection({
host : settings.etherpadDB.host, host: settings.etherpadDB.host,
user : settings.etherpadDB.user, user: settings.etherpadDB.user,
password : settings.etherpadDB.password, password: settings.etherpadDB.password,
database : settings.etherpadDB.database, database: settings.etherpadDB.database,
port : settings.etherpadDB.port port: settings.etherpadDB.port,
}); });
//get the timestamp once // get the timestamp once
var timestamp = Date.now(); const timestamp = Date.now();
var padIDs; let padIDs;
async.series([ async.series([
//get all padids out of the database... // get all padids out of the database...
function(callback) { function (callback) {
log("get all padIds out of the database..."); log('get all padIds out of the database...');
etherpadDB.query("SELECT ID FROM PAD_META", [], function(err, _padIDs) { etherpadDB.query('SELECT ID FROM PAD_META', [], (err, _padIDs) => {
padIDs = _padIDs; padIDs = _padIDs;
callback(err); callback(err);
}); });
}, },
function(callback) { function (callback) {
log("done"); log('done');
//create a queue with a concurrency 100 // create a queue with a concurrency 100
var queue = async.queue(function (padId, callback) { const queue = async.queue((padId, callback) => {
convertPad(padId, function(err) { convertPad(padId, (err) => {
incrementPadStats(); incrementPadStats();
callback(err); callback(err);
}); });
}, 100); }, 100);
//set the step callback as the queue callback // set the step callback as the queue callback
queue.drain = callback; queue.drain = callback;
//add the padids to the worker queue // add the padids to the worker queue
for(var i=0,length=padIDs.length;i<length;i++) for (let i = 0, length = padIDs.length; i < length; i++) {
{
queue.push(padIDs[i].ID); queue.push(padIDs[i].ID);
} }
} },
], function(err) { ], (err) => {
if(err) throw err; if (err) throw err;
//write the groups // write the groups
var sql = ""; let sql = '';
for(var proID in proID2groupID) for (const proID in proID2groupID) {
{ const groupID = proID2groupID[proID];
var groupID = proID2groupID[proID]; const subdomain = proID2subdomain[proID];
var subdomain = proID2subdomain[proID];
sql+="REPLACE INTO store VALUES (" + etherpadDB.escape("group:" + groupID) + ", " + etherpadDB.escape(JSON.stringify(groups[groupID]))+ ");\n"; sql += `REPLACE INTO store VALUES (${etherpadDB.escape(`group:${groupID}`)}, ${etherpadDB.escape(JSON.stringify(groups[groupID]))});\n`;
sql+="REPLACE INTO store VALUES (" + etherpadDB.escape("mapper2group:subdomain:" + subdomain) + ", " + etherpadDB.escape(groupID)+ ");\n"; sql += `REPLACE INTO store VALUES (${etherpadDB.escape(`mapper2group:subdomain:${subdomain}`)}, ${etherpadDB.escape(groupID)});\n`;
} }
//close transaction // close transaction
sql+="COMMIT;"; sql += 'COMMIT;';
//end the sql file // end the sql file
fs.writeSync(sqlOutput, sql, undefined, "utf-8"); fs.writeSync(sqlOutput, sql, undefined, 'utf-8');
fs.closeSync(sqlOutput); fs.closeSync(sqlOutput);
log("finished."); log('finished.');
process.exit(0); process.exit(0);
}); });
function log(str) { function log(str) {
console.log((Date.now() - startTime)/1000 + "\t" + str); console.log(`${(Date.now() - startTime) / 1000}\t${str}`);
} }
var padsDone = 0; let padsDone = 0;
function incrementPadStats() { function incrementPadStats() {
padsDone++; padsDone++;
if(padsDone%100 == 0) if (padsDone % 100 == 0) {
{ const averageTime = Math.round(padsDone / ((Date.now() - startTime) / 1000));
var averageTime = Math.round(padsDone/((Date.now() - startTime)/1000)); log(`${padsDone}/${padIDs.length}\t${averageTime} pad/s`);
log(padsDone + "/" + padIDs.length + "\t" + averageTime + " pad/s")
} }
} }
@ -123,275 +119,245 @@ var proID2subdomain = {};
var groups = {}; var groups = {};
function convertPad(padId, callback) { function convertPad(padId, callback) {
var changesets = []; const changesets = [];
var changesetsMeta = []; const changesetsMeta = [];
var chatMessages = []; const chatMessages = [];
var authors = []; const authors = [];
var apool; let apool;
var subdomain; let subdomain;
var padmeta; let padmeta;
async.series([ async.series([
//get all needed db values // get all needed db values
function(callback) { function (callback) {
async.parallel([ async.parallel([
//get the pad revisions // get the pad revisions
function(callback) { function (callback) {
var sql = "SELECT * FROM `PAD_CHAT_TEXT` WHERE NUMID = (SELECT `NUMID` FROM `PAD_CHAT_META` WHERE ID=?)"; const sql = 'SELECT * FROM `PAD_CHAT_TEXT` WHERE NUMID = (SELECT `NUMID` FROM `PAD_CHAT_META` WHERE ID=?)';
etherpadDB.query(sql, [padId], function(err, results) { etherpadDB.query(sql, [padId], (err, results) => {
if(!err) if (!err) {
{ try {
try // parse the pages
{ for (let i = 0, length = results.length; i < length; i++) {
//parse the pages
for(var i=0,length=results.length;i<length;i++)
{
parsePage(chatMessages, results[i].PAGESTART, results[i].OFFSETS, results[i].DATA, true); parsePage(chatMessages, results[i].PAGESTART, results[i].OFFSETS, results[i].DATA, true);
} }
}catch(e) {err = e} } catch (e) { err = e; }
} }
callback(err); callback(err);
}); });
}, },
//get the chat entries // get the chat entries
function(callback) { function (callback) {
var sql = "SELECT * FROM `PAD_REVS_TEXT` WHERE NUMID = (SELECT `NUMID` FROM `PAD_REVS_META` WHERE ID=?)"; const sql = 'SELECT * FROM `PAD_REVS_TEXT` WHERE NUMID = (SELECT `NUMID` FROM `PAD_REVS_META` WHERE ID=?)';
etherpadDB.query(sql, [padId], function(err, results) { etherpadDB.query(sql, [padId], (err, results) => {
if(!err) if (!err) {
{ try {
try // parse the pages
{ for (let i = 0, length = results.length; i < length; i++) {
//parse the pages
for(var i=0,length=results.length;i<length;i++)
{
parsePage(changesets, results[i].PAGESTART, results[i].OFFSETS, results[i].DATA, false); parsePage(changesets, results[i].PAGESTART, results[i].OFFSETS, results[i].DATA, false);
} }
}catch(e) {err = e} } catch (e) { err = e; }
} }
callback(err); callback(err);
}); });
}, },
//get the pad revisions meta data // get the pad revisions meta data
function(callback) { function (callback) {
var sql = "SELECT * FROM `PAD_REVMETA_TEXT` WHERE NUMID = (SELECT `NUMID` FROM `PAD_REVMETA_META` WHERE ID=?)"; const sql = 'SELECT * FROM `PAD_REVMETA_TEXT` WHERE NUMID = (SELECT `NUMID` FROM `PAD_REVMETA_META` WHERE ID=?)';
etherpadDB.query(sql, [padId], function(err, results) { etherpadDB.query(sql, [padId], (err, results) => {
if(!err) if (!err) {
{ try {
try // parse the pages
{ for (let i = 0, length = results.length; i < length; i++) {
//parse the pages
for(var i=0,length=results.length;i<length;i++)
{
parsePage(changesetsMeta, results[i].PAGESTART, results[i].OFFSETS, results[i].DATA, true); parsePage(changesetsMeta, results[i].PAGESTART, results[i].OFFSETS, results[i].DATA, true);
} }
}catch(e) {err = e} } catch (e) { err = e; }
} }
callback(err); callback(err);
}); });
}, },
//get the attribute pool of this pad // get the attribute pool of this pad
function(callback) { function (callback) {
var sql = "SELECT `JSON` FROM `PAD_APOOL` WHERE `ID` = ?"; const sql = 'SELECT `JSON` FROM `PAD_APOOL` WHERE `ID` = ?';
etherpadDB.query(sql, [padId], function(err, results) { etherpadDB.query(sql, [padId], (err, results) => {
if(!err) if (!err) {
{ try {
try apool = JSON.parse(results[0].JSON).x;
{ } catch (e) { err = e; }
apool=JSON.parse(results[0].JSON).x;
}catch(e) {err = e}
} }
callback(err); callback(err);
}); });
}, },
//get the authors informations // get the authors informations
function(callback) { function (callback) {
var sql = "SELECT * FROM `PAD_AUTHORS_TEXT` WHERE NUMID = (SELECT `NUMID` FROM `PAD_AUTHORS_META` WHERE ID=?)"; const sql = 'SELECT * FROM `PAD_AUTHORS_TEXT` WHERE NUMID = (SELECT `NUMID` FROM `PAD_AUTHORS_META` WHERE ID=?)';
etherpadDB.query(sql, [padId], function(err, results) { etherpadDB.query(sql, [padId], (err, results) => {
if(!err) if (!err) {
{ try {
try // parse the pages
{ for (let i = 0, length = results.length; i < length; i++) {
//parse the pages
for(var i=0, length=results.length;i<length;i++)
{
parsePage(authors, results[i].PAGESTART, results[i].OFFSETS, results[i].DATA, true); parsePage(authors, results[i].PAGESTART, results[i].OFFSETS, results[i].DATA, true);
} }
}catch(e) {err = e} } catch (e) { err = e; }
} }
callback(err); callback(err);
}); });
}, },
//get the pad information // get the pad information
function(callback) { function (callback) {
var sql = "SELECT JSON FROM `PAD_META` WHERE ID=?"; const sql = 'SELECT JSON FROM `PAD_META` WHERE ID=?';
etherpadDB.query(sql, [padId], function(err, results) { etherpadDB.query(sql, [padId], (err, results) => {
if(!err) if (!err) {
{ try {
try
{
padmeta = JSON.parse(results[0].JSON).x; padmeta = JSON.parse(results[0].JSON).x;
}catch(e) {err = e} } catch (e) { err = e; }
} }
callback(err); callback(err);
}); });
}, },
//get the subdomain // get the subdomain
function(callback) { function (callback) {
//skip if this is no proPad // skip if this is no proPad
if(padId.indexOf("$") == -1) if (padId.indexOf('$') == -1) {
{
callback(); callback();
return; return;
} }
//get the proID out of this padID // get the proID out of this padID
var proID = padId.split("$")[0]; const proID = padId.split('$')[0];
var sql = "SELECT subDomain FROM pro_domains WHERE ID = ?"; const sql = 'SELECT subDomain FROM pro_domains WHERE ID = ?';
etherpadDB.query(sql, [proID], function(err, results) { etherpadDB.query(sql, [proID], (err, results) => {
if(!err) if (!err) {
{
subdomain = results[0].subDomain; subdomain = results[0].subDomain;
} }
callback(err); callback(err);
}); });
} },
], callback); ], callback);
}, },
function(callback) { function (callback) {
//saves all values that should be written to the database // saves all values that should be written to the database
var values = {}; const values = {};
//this is a pro pad, let's convert it to a group pad // this is a pro pad, let's convert it to a group pad
if(padId.indexOf("$") != -1) if (padId.indexOf('$') != -1) {
{ const padIdParts = padId.split('$');
var padIdParts = padId.split("$"); const proID = padIdParts[0];
var proID = padIdParts[0]; const padName = padIdParts[1];
var padName = padIdParts[1];
var groupID let groupID;
//this proID is not converted so far, do it // this proID is not converted so far, do it
if(proID2groupID[proID] == null) if (proID2groupID[proID] == null) {
{ groupID = `g.${randomString(16)}`;
groupID = "g." + randomString(16);
//create the mappers for this new group // create the mappers for this new group
proID2groupID[proID] = groupID; proID2groupID[proID] = groupID;
proID2subdomain[proID] = subdomain; proID2subdomain[proID] = subdomain;
groups[groupID] = {pads: {}}; groups[groupID] = {pads: {}};
} }
//use the generated groupID; // use the generated groupID;
groupID = proID2groupID[proID]; groupID = proID2groupID[proID];
//rename the pad // rename the pad
padId = groupID + "$" + padName; padId = `${groupID}$${padName}`;
//set the value for this pad in the group // set the value for this pad in the group
groups[groupID].pads[padId] = 1; groups[groupID].pads[padId] = 1;
} }
try try {
{ const newAuthorIDs = {};
var newAuthorIDs = {}; const oldName2newName = {};
var oldName2newName = {};
//replace the authors with generated authors // replace the authors with generated authors
// we need to do that cause where the original etherpad saves pad local authors, the new (lite) etherpad uses them global // we need to do that cause where the original etherpad saves pad local authors, the new (lite) etherpad uses them global
for(var i in apool.numToAttrib) for (var i in apool.numToAttrib) {
{
var key = apool.numToAttrib[i][0]; var key = apool.numToAttrib[i][0];
var value = apool.numToAttrib[i][1]; const value = apool.numToAttrib[i][1];
//skip non authors and anonymous authors // skip non authors and anonymous authors
if(key != "author" || value == "") if (key != 'author' || value == '') continue;
continue;
//generate new author values // generate new author values
var authorID = "a." + randomString(16); const authorID = `a.${randomString(16)}`;
var authorColorID = authors[i].colorId || Math.floor(Math.random()*(exports.getColorPalette().length)); const authorColorID = authors[i].colorId || Math.floor(Math.random() * (exports.getColorPalette().length));
var authorName = authors[i].name || null; const authorName = authors[i].name || null;
//overwrite the authorID of the attribute pool // overwrite the authorID of the attribute pool
apool.numToAttrib[i][1] = authorID; apool.numToAttrib[i][1] = authorID;
//write the author to the database // write the author to the database
values["globalAuthor:" + authorID] = {"colorId" : authorColorID, "name": authorName, "timestamp": timestamp}; values[`globalAuthor:${authorID}`] = {colorId: authorColorID, name: authorName, timestamp};
//save in mappers // save in mappers
newAuthorIDs[i] = authorID; newAuthorIDs[i] = authorID;
oldName2newName[value] = authorID; oldName2newName[value] = authorID;
} }
//save all revisions // save all revisions
for(var i=0;i<changesets.length;i++) for (var i = 0; i < changesets.length; i++) {
{ values[`pad:${padId}:revs:${i}`] = {changeset: changesets[i],
values["pad:" + padId + ":revs:" + i] = {changeset: changesets[i], meta: {
meta : {
author: newAuthorIDs[changesetsMeta[i].a], author: newAuthorIDs[changesetsMeta[i].a],
timestamp: changesetsMeta[i].t, timestamp: changesetsMeta[i].t,
atext: changesetsMeta[i].atext || undefined atext: changesetsMeta[i].atext || undefined,
}}; }};
} }
//save all chat messages // save all chat messages
for(var i=0;i<chatMessages.length;i++) for (var i = 0; i < chatMessages.length; i++) {
{ values[`pad:${padId}:chat:${i}`] = {text: chatMessages[i].lineText,
values["pad:" + padId + ":chat:" + i] = {"text": chatMessages[i].lineText, userId: oldName2newName[chatMessages[i].userId],
"userId": oldName2newName[chatMessages[i].userId], time: chatMessages[i].time};
"time": chatMessages[i].time}
} }
//generate the latest atext // generate the latest atext
var fullAPool = (new AttributePool()).fromJsonable(apool); const fullAPool = (new AttributePool()).fromJsonable(apool);
var keyRev = Math.floor(padmeta.head / padmeta.keyRevInterval) * padmeta.keyRevInterval; const keyRev = Math.floor(padmeta.head / padmeta.keyRevInterval) * padmeta.keyRevInterval;
var atext = changesetsMeta[keyRev].atext; let atext = changesetsMeta[keyRev].atext;
var curRev = keyRev; let curRev = keyRev;
while (curRev < padmeta.head) while (curRev < padmeta.head) {
{
curRev++; curRev++;
var changeset = changesets[curRev]; const changeset = changesets[curRev];
atext = Changeset.applyToAText(changeset, atext, fullAPool); atext = Changeset.applyToAText(changeset, atext, fullAPool);
} }
values["pad:" + padId] = {atext: atext, values[`pad:${padId}`] = {atext,
pool: apool, pool: apool,
head: padmeta.head, head: padmeta.head,
chatHead: padmeta.numChatMessages } chatHead: padmeta.numChatMessages};
} catch (e) {
} console.error(`Error while converting pad ${padId}, pad skipped`);
catch(e)
{
console.error("Error while converting pad " + padId + ", pad skipped");
console.error(e.stack ? e.stack : JSON.stringify(e)); console.error(e.stack ? e.stack : JSON.stringify(e));
callback(); callback();
return; return;
} }
var sql = ""; let sql = '';
for(var key in values) for (var key in values) {
{ sql += `REPLACE INTO store VALUES (${etherpadDB.escape(key)}, ${etherpadDB.escape(JSON.stringify(values[key]))});\n`;
sql+="REPLACE INTO store VALUES (" + etherpadDB.escape(key) + ", " + etherpadDB.escape(JSON.stringify(values[key]))+ ");\n";
} }
fs.writeSync(sqlOutput, sql, undefined, "utf-8"); fs.writeSync(sqlOutput, sql, undefined, 'utf-8');
callback(); callback();
} },
], callback); ], callback);
} }
@ -401,27 +367,25 @@ function convertPad(padId, callback) {
* all values behind each other * all values behind each other
*/ */
function parsePage(array, pageStart, offsets, data, json) { function parsePage(array, pageStart, offsets, data, json) {
var start = 0; let start = 0;
var lengths = offsets.split(","); const lengths = offsets.split(',');
for(var i=0;i<lengths.length;i++) for (let i = 0; i < lengths.length; i++) {
{ let unitLength = lengths[i];
var unitLength = lengths[i];
//skip empty units // skip empty units
if(unitLength == "") if (unitLength == '') continue;
continue;
//parse the number // parse the number
unitLength = Number(unitLength); unitLength = Number(unitLength);
//cut the unit out of data // cut the unit out of data
var unit = data.substr(start, unitLength); const unit = data.substr(start, unitLength);
//put it into the array // put it into the array
array[pageStart + i] = json ? JSON.parse(unit) : unit; array[pageStart + i] = json ? JSON.parse(unit) : unit;
//update start // update start
start+=unitLength; start += unitLength;
} }
} }

View file

@ -2,7 +2,7 @@
* A tool for generating a test user session which can be used for debugging configs * A tool for generating a test user session which can be used for debugging configs
* that require sessions. * that require sessions.
*/ */
const m = (f) => __dirname + '/../' + f; const m = (f) => `${__dirname}/../${f}`;
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
@ -12,7 +12,7 @@ const settings = require(m('src/node/utils/Settings'));
const supertest = require(m('src/node_modules/supertest')); const supertest = require(m('src/node_modules/supertest'));
(async () => { (async () => {
const api = supertest('http://'+settings.ip+':'+settings.port); const api = supertest(`http://${settings.ip}:${settings.port}`);
const filePath = path.join(__dirname, '../APIKEY.txt'); const filePath = path.join(__dirname, '../APIKEY.txt');
const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'}); const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'});

View file

@ -4,48 +4,48 @@
*/ */
const request = require('../src/node_modules/request'); const request = require('../src/node_modules/request');
const settings = require(__dirname+'/../tests/container/loadSettings').loadSettings(); const settings = require(`${__dirname}/../tests/container/loadSettings`).loadSettings();
const supertest = require(__dirname+'/../src/node_modules/supertest'); const supertest = require(`${__dirname}/../src/node_modules/supertest`);
const api = supertest('http://'+settings.ip+":"+settings.port); const api = supertest(`http://${settings.ip}:${settings.port}`);
const path = require('path'); const path = require('path');
const fs = require('fs'); const fs = require('fs');
// get the API Key // get the API Key
var filePath = path.join(__dirname, '../APIKEY.txt'); const filePath = path.join(__dirname, '../APIKEY.txt');
var apikey = fs.readFileSync(filePath, {encoding: 'utf-8'}); const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'});
// Set apiVersion to base value, we change this later. // Set apiVersion to base value, we change this later.
var apiVersion = 1; let apiVersion = 1;
var guids; let guids;
// Update the apiVersion // Update the apiVersion
api.get('/api/') api.get('/api/')
.expect(function(res){ .expect((res) => {
apiVersion = res.body.currentVersion; apiVersion = res.body.currentVersion;
if (!res.body.currentVersion) throw new Error("No version set in API"); if (!res.body.currentVersion) throw new Error('No version set in API');
return; return;
}) })
.then(function(){ .then(() => {
let guri = '/api/'+apiVersion+'/listAllGroups?apikey='+apikey; const guri = `/api/${apiVersion}/listAllGroups?apikey=${apikey}`;
api.get(guri) api.get(guri)
.then(function(res){ .then((res) => {
guids = res.body.data.groupIDs; guids = res.body.data.groupIDs;
guids.forEach(function(groupID){ guids.forEach((groupID) => {
let luri = '/api/'+apiVersion+'/listSessionsOfGroup?apikey='+apikey + "&groupID="+groupID; const luri = `/api/${apiVersion}/listSessionsOfGroup?apikey=${apikey}&groupID=${groupID}`;
api.get(luri) api.get(luri)
.then(function(res){ .then((res) => {
if(res.body.data){ if (res.body.data) {
Object.keys(res.body.data).forEach(function(sessionID){ Object.keys(res.body.data).forEach((sessionID) => {
if(sessionID){ if (sessionID) {
console.log("Deleting", sessionID); console.log('Deleting', sessionID);
let duri = '/api/'+apiVersion+'/deleteSession?apikey='+apikey + "&sessionID="+sessionID; const duri = `/api/${apiVersion}/deleteSession?apikey=${apikey}&sessionID=${sessionID}`;
api.post(duri); // deletes api.post(duri); // deletes
} }
}) });
}else{ } else {
// no session in this group. // no session in this group.
} }
}) });
}) });
}) });
}) });

View file

@ -4,47 +4,45 @@
*/ */
const request = require('../src/node_modules/request'); const request = require('../src/node_modules/request');
const settings = require(__dirname+'/../tests/container/loadSettings').loadSettings(); const settings = require(`${__dirname}/../tests/container/loadSettings`).loadSettings();
const supertest = require(__dirname+'/../src/node_modules/supertest'); const supertest = require(`${__dirname}/../src/node_modules/supertest`);
const api = supertest('http://'+settings.ip+":"+settings.port); const api = supertest(`http://${settings.ip}:${settings.port}`);
const path = require('path'); const path = require('path');
const fs = require('fs'); const fs = require('fs');
if (process.argv.length != 3) { if (process.argv.length != 3) {
console.error("Use: node deletePad.js $PADID"); console.error('Use: node deletePad.js $PADID');
process.exit(1); process.exit(1);
} }
// get the padID // get the padID
let padId = process.argv[2]; const padId = process.argv[2];
// get the API Key // get the API Key
var filePath = path.join(__dirname, '../APIKEY.txt'); const filePath = path.join(__dirname, '../APIKEY.txt');
var apikey = fs.readFileSync(filePath, {encoding: 'utf-8'}); const apikey = fs.readFileSync(filePath, {encoding: 'utf-8'});
// Set apiVersion to base value, we change this later. // Set apiVersion to base value, we change this later.
var apiVersion = 1; let apiVersion = 1;
// Update the apiVersion // Update the apiVersion
api.get('/api/') api.get('/api/')
.expect(function(res){ .expect((res) => {
apiVersion = res.body.currentVersion; apiVersion = res.body.currentVersion;
if (!res.body.currentVersion) throw new Error("No version set in API"); if (!res.body.currentVersion) throw new Error('No version set in API');
return; return;
}) })
.end(function(err, res){ .end((err, res) => {
// Now we know the latest API version, let's delete pad // Now we know the latest API version, let's delete pad
var uri = '/api/'+apiVersion+'/deletePad?apikey='+apikey+'&padID='+padId; const uri = `/api/${apiVersion}/deletePad?apikey=${apikey}&padID=${padId}`;
api.post(uri) api.post(uri)
.expect(function(res){ .expect((res) => {
if (res.body.code === 1){ if (res.body.code === 1) {
console.error("Error deleting pad", res.body); console.error('Error deleting pad', res.body);
}else{ } else {
console.log("Deleted pad", res.body); console.log('Deleted pad', res.body);
} }
return; return;
}) })
.end(function(){}) .end(() => {});
}); });
// end // end

View file

@ -20,19 +20,19 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE. // USE OR OTHER DEALINGS IN THE SOFTWARE.
var marked = require('marked'); const marked = require('marked');
var fs = require('fs'); const fs = require('fs');
var path = require('path'); const path = require('path');
// parse the args. // parse the args.
// Don't use nopt or whatever for this. It's simple enough. // Don't use nopt or whatever for this. It's simple enough.
var args = process.argv.slice(2); const args = process.argv.slice(2);
var format = 'json'; let format = 'json';
var template = null; let template = null;
var inputFile = null; let inputFile = null;
args.forEach(function (arg) { args.forEach((arg) => {
if (!arg.match(/^\-\-/)) { if (!arg.match(/^\-\-/)) {
inputFile = arg; inputFile = arg;
} else if (arg.match(/^\-\-format=/)) { } else if (arg.match(/^\-\-format=/)) {
@ -40,7 +40,7 @@ args.forEach(function (arg) {
} else if (arg.match(/^\-\-template=/)) { } else if (arg.match(/^\-\-template=/)) {
template = arg.replace(/^\-\-template=/, ''); template = arg.replace(/^\-\-template=/, '');
} }
}) });
if (!inputFile) { if (!inputFile) {
@ -49,25 +49,25 @@ if (!inputFile) {
console.error('Input file = %s', inputFile); console.error('Input file = %s', inputFile);
fs.readFile(inputFile, 'utf8', function(er, input) { fs.readFile(inputFile, 'utf8', (er, input) => {
if (er) throw er; if (er) throw er;
// process the input for @include lines // process the input for @include lines
processIncludes(inputFile, input, next); processIncludes(inputFile, input, next);
}); });
var includeExpr = /^@include\s+([A-Za-z0-9-_\/]+)(?:\.)?([a-zA-Z]*)$/gmi; const includeExpr = /^@include\s+([A-Za-z0-9-_\/]+)(?:\.)?([a-zA-Z]*)$/gmi;
var includeData = {}; const includeData = {};
function processIncludes(inputFile, input, cb) { function processIncludes(inputFile, input, cb) {
var includes = input.match(includeExpr); const includes = input.match(includeExpr);
if (includes === null) return cb(null, input); if (includes === null) return cb(null, input);
var errState = null; let errState = null;
console.error(includes); console.error(includes);
var incCount = includes.length; let incCount = includes.length;
if (incCount === 0) cb(null, input); if (incCount === 0) cb(null, input);
includes.forEach(function(include) { includes.forEach((include) => {
var fname = include.replace(/^@include\s+/, ''); let fname = include.replace(/^@include\s+/, '');
if (!fname.match(/\.md$/)) fname += '.md'; if (!fname.match(/\.md$/)) fname += '.md';
if (includeData.hasOwnProperty(fname)) { if (includeData.hasOwnProperty(fname)) {
@ -78,11 +78,11 @@ function processIncludes(inputFile, input, cb) {
} }
} }
var fullFname = path.resolve(path.dirname(inputFile), fname); const fullFname = path.resolve(path.dirname(inputFile), fname);
fs.readFile(fullFname, 'utf8', function(er, inc) { fs.readFile(fullFname, 'utf8', (er, inc) => {
if (errState) return; if (errState) return;
if (er) return cb(errState = er); if (er) return cb(errState = er);
processIncludes(fullFname, inc, function(er, inc) { processIncludes(fullFname, inc, (er, inc) => {
if (errState) return; if (errState) return;
if (er) return cb(errState = er); if (er) return cb(errState = er);
incCount--; incCount--;
@ -101,20 +101,20 @@ function next(er, input) {
if (er) throw er; if (er) throw er;
switch (format) { switch (format) {
case 'json': case 'json':
require('./json.js')(input, inputFile, function(er, obj) { require('./json.js')(input, inputFile, (er, obj) => {
console.log(JSON.stringify(obj, null, 2)); console.log(JSON.stringify(obj, null, 2));
if (er) throw er; if (er) throw er;
}); });
break; break;
case 'html': case 'html':
require('./html.js')(input, inputFile, template, function(er, html) { require('./html.js')(input, inputFile, template, (er, html) => {
if (er) throw er; if (er) throw er;
console.log(html); console.log(html);
}); });
break; break;
default: default:
throw new Error('Invalid format: ' + format); throw new Error(`Invalid format: ${format}`);
} }
} }

View file

@ -19,15 +19,15 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE. // USE OR OTHER DEALINGS IN THE SOFTWARE.
var fs = require('fs'); const fs = require('fs');
var marked = require('marked'); const marked = require('marked');
var path = require('path'); const path = require('path');
module.exports = toHTML; module.exports = toHTML;
function toHTML(input, filename, template, cb) { function toHTML(input, filename, template, cb) {
var lexed = marked.lexer(input); const lexed = marked.lexer(input);
fs.readFile(template, 'utf8', function(er, template) { fs.readFile(template, 'utf8', (er, template) => {
if (er) return cb(er); if (er) return cb(er);
render(lexed, filename, template, cb); render(lexed, filename, template, cb);
}); });
@ -35,7 +35,7 @@ function toHTML(input, filename, template, cb) {
function render(lexed, filename, template, cb) { function render(lexed, filename, template, cb) {
// get the section // get the section
var section = getSection(lexed); const section = getSection(lexed);
filename = path.basename(filename, '.md'); filename = path.basename(filename, '.md');
@ -43,7 +43,7 @@ function render(lexed, filename, template, cb) {
// generate the table of contents. // generate the table of contents.
// this mutates the lexed contents in-place. // this mutates the lexed contents in-place.
buildToc(lexed, filename, function(er, toc) { buildToc(lexed, filename, (er, toc) => {
if (er) return cb(er); if (er) return cb(er);
template = template.replace(/__FILENAME__/g, filename); template = template.replace(/__FILENAME__/g, filename);
@ -63,11 +63,11 @@ function render(lexed, filename, template, cb) {
// just update the list item text in-place. // just update the list item text in-place.
// lists that come right after a heading are what we're after. // lists that come right after a heading are what we're after.
function parseLists(input) { function parseLists(input) {
var state = null; let state = null;
var depth = 0; let depth = 0;
var output = []; const output = [];
output.links = input.links; output.links = input.links;
input.forEach(function(tok) { input.forEach((tok) => {
if (state === null) { if (state === null) {
if (tok.type === 'heading') { if (tok.type === 'heading') {
state = 'AFTERHEADING'; state = 'AFTERHEADING';
@ -79,7 +79,7 @@ function parseLists(input) {
if (tok.type === 'list_start') { if (tok.type === 'list_start') {
state = 'LIST'; state = 'LIST';
if (depth === 0) { if (depth === 0) {
output.push({ type:'html', text: '<div class="signature">' }); output.push({type: 'html', text: '<div class="signature">'});
} }
depth++; depth++;
output.push(tok); output.push(tok);
@ -99,7 +99,7 @@ function parseLists(input) {
depth--; depth--;
if (depth === 0) { if (depth === 0) {
state = null; state = null;
output.push({ type:'html', text: '</div>' }); output.push({type: 'html', text: '</div>'});
} }
output.push(tok); output.push(tok);
return; return;
@ -117,16 +117,16 @@ function parseLists(input) {
function parseListItem(text) { function parseListItem(text) {
text = text.replace(/\{([^\}]+)\}/, '<span class="type">$1</span>'); text = text.replace(/\{([^\}]+)\}/, '<span class="type">$1</span>');
//XXX maybe put more stuff here? // XXX maybe put more stuff here?
return text; return text;
} }
// section is just the first heading // section is just the first heading
function getSection(lexed) { function getSection(lexed) {
var section = ''; const section = '';
for (var i = 0, l = lexed.length; i < l; i++) { for (let i = 0, l = lexed.length; i < l; i++) {
var tok = lexed[i]; const tok = lexed[i];
if (tok.type === 'heading') return tok.text; if (tok.type === 'heading') return tok.text;
} }
return ''; return '';
@ -134,40 +134,39 @@ function getSection(lexed) {
function buildToc(lexed, filename, cb) { function buildToc(lexed, filename, cb) {
var indent = 0; const indent = 0;
var toc = []; let toc = [];
var depth = 0; let depth = 0;
lexed.forEach(function(tok) { lexed.forEach((tok) => {
if (tok.type !== 'heading') return; if (tok.type !== 'heading') return;
if (tok.depth - depth > 1) { if (tok.depth - depth > 1) {
return cb(new Error('Inappropriate heading level\n' + return cb(new Error(`Inappropriate heading level\n${
JSON.stringify(tok))); JSON.stringify(tok)}`));
} }
depth = tok.depth; depth = tok.depth;
var id = getId(filename + '_' + tok.text.trim()); const id = getId(`${filename}_${tok.text.trim()}`);
toc.push(new Array((depth - 1) * 2 + 1).join(' ') + toc.push(`${new Array((depth - 1) * 2 + 1).join(' ')
'* <a href="#' + id + '">' + }* <a href="#${id}">${
tok.text + '</a>'); tok.text}</a>`);
tok.text += '<span><a class="mark" href="#' + id + '" ' + tok.text += `<span><a class="mark" href="#${id}" ` +
'id="' + id + '">#</a></span>'; `id="${id}">#</a></span>`;
}); });
toc = marked.parse(toc.join('\n')); toc = marked.parse(toc.join('\n'));
cb(null, toc); cb(null, toc);
} }
var idCounters = {}; const idCounters = {};
function getId(text) { function getId(text) {
text = text.toLowerCase(); text = text.toLowerCase();
text = text.replace(/[^a-z0-9]+/g, '_'); text = text.replace(/[^a-z0-9]+/g, '_');
text = text.replace(/^_+|_+$/, ''); text = text.replace(/^_+|_+$/, '');
text = text.replace(/^([^a-z])/, '_$1'); text = text.replace(/^([^a-z])/, '_$1');
if (idCounters.hasOwnProperty(text)) { if (idCounters.hasOwnProperty(text)) {
text += '_' + (++idCounters[text]); text += `_${++idCounters[text]}`;
} else { } else {
idCounters[text] = 0; idCounters[text] = 0;
} }
return text; return text;
} }

View file

@ -24,24 +24,24 @@ module.exports = doJSON;
// Take the lexed input, and return a JSON-encoded object // Take the lexed input, and return a JSON-encoded object
// A module looks like this: https://gist.github.com/1777387 // A module looks like this: https://gist.github.com/1777387
var marked = require('marked'); const marked = require('marked');
function doJSON(input, filename, cb) { function doJSON(input, filename, cb) {
var root = {source: filename}; const root = {source: filename};
var stack = [root]; const stack = [root];
var depth = 0; let depth = 0;
var current = root; let current = root;
var state = null; let state = null;
var lexed = marked.lexer(input); const lexed = marked.lexer(input);
lexed.forEach(function (tok) { lexed.forEach((tok) => {
var type = tok.type; const type = tok.type;
var text = tok.text; let text = tok.text;
// <!-- type = module --> // <!-- type = module -->
// This is for cases where the markdown semantic structure is lacking. // This is for cases where the markdown semantic structure is lacking.
if (type === 'paragraph' || type === 'html') { if (type === 'paragraph' || type === 'html') {
var metaExpr = /<!--([^=]+)=([^\-]+)-->\n*/g; const metaExpr = /<!--([^=]+)=([^\-]+)-->\n*/g;
text = text.replace(metaExpr, function(_0, k, v) { text = text.replace(metaExpr, (_0, k, v) => {
current[k.trim()] = v.trim(); current[k.trim()] = v.trim();
return ''; return '';
}); });
@ -52,8 +52,8 @@ function doJSON(input, filename, cb) {
if (type === 'heading' && if (type === 'heading' &&
!text.trim().match(/^example/i)) { !text.trim().match(/^example/i)) {
if (tok.depth - depth > 1) { if (tok.depth - depth > 1) {
return cb(new Error('Inappropriate heading level\n'+ return cb(new Error(`Inappropriate heading level\n${
JSON.stringify(tok))); JSON.stringify(tok)}`));
} }
// Sometimes we have two headings with a single // Sometimes we have two headings with a single
@ -61,7 +61,7 @@ function doJSON(input, filename, cb) {
if (current && if (current &&
state === 'AFTERHEADING' && state === 'AFTERHEADING' &&
depth === tok.depth) { depth === tok.depth) {
var clone = current; const clone = current;
current = newSection(tok); current = newSection(tok);
current.clone = clone; current.clone = clone;
// don't keep it around on the stack. // don't keep it around on the stack.
@ -75,7 +75,7 @@ function doJSON(input, filename, cb) {
// root is always considered the level=0 section, // root is always considered the level=0 section,
// and the lowest heading is 1, so this should always // and the lowest heading is 1, so this should always
// result in having a valid parent node. // result in having a valid parent node.
var d = tok.depth; let d = tok.depth;
while (d <= depth) { while (d <= depth) {
finishSection(stack.pop(), stack[stack.length - 1]); finishSection(stack.pop(), stack[stack.length - 1]);
d++; d++;
@ -98,7 +98,7 @@ function doJSON(input, filename, cb) {
// //
// If one of these isn't found, then anything that comes between // If one of these isn't found, then anything that comes between
// here and the next heading should be parsed as the desc. // here and the next heading should be parsed as the desc.
var stability let stability;
if (state === 'AFTERHEADING') { if (state === 'AFTERHEADING') {
if (type === 'code' && if (type === 'code' &&
(stability = text.match(/^Stability: ([0-5])(?:\s*-\s*)?(.*)$/))) { (stability = text.match(/^Stability: ([0-5])(?:\s*-\s*)?(.*)$/))) {
@ -138,7 +138,6 @@ function doJSON(input, filename, cb) {
current.desc = current.desc || []; current.desc = current.desc || [];
current.desc.push(tok); current.desc.push(tok);
}); });
// finish any sections left open // finish any sections left open
@ -146,7 +145,7 @@ function doJSON(input, filename, cb) {
finishSection(current, stack[stack.length - 1]); finishSection(current, stack[stack.length - 1]);
} }
return cb(null, root) return cb(null, root);
} }
@ -193,14 +192,14 @@ function doJSON(input, filename, cb) {
// default: 'false' } ] } ] // default: 'false' } ] } ]
function processList(section) { function processList(section) {
var list = section.list; const list = section.list;
var values = []; const values = [];
var current; let current;
var stack = []; const stack = [];
// for now, *just* build the hierarchical list // for now, *just* build the hierarchical list
list.forEach(function(tok) { list.forEach((tok) => {
var type = tok.type; const type = tok.type;
if (type === 'space') return; if (type === 'space') return;
if (type === 'list_item_start') { if (type === 'list_item_start') {
if (!current) { if (!current) {
@ -217,26 +216,26 @@ function processList(section) {
return; return;
} else if (type === 'list_item_end') { } else if (type === 'list_item_end') {
if (!current) { if (!current) {
throw new Error('invalid list - end without current item\n' + throw new Error(`invalid list - end without current item\n${
JSON.stringify(tok) + '\n' + JSON.stringify(tok)}\n${
JSON.stringify(list)); JSON.stringify(list)}`);
} }
current = stack.pop(); current = stack.pop();
} else if (type === 'text') { } else if (type === 'text') {
if (!current) { if (!current) {
throw new Error('invalid list - text without current item\n' + throw new Error(`invalid list - text without current item\n${
JSON.stringify(tok) + '\n' + JSON.stringify(tok)}\n${
JSON.stringify(list)); JSON.stringify(list)}`);
} }
current.textRaw = current.textRaw || ''; current.textRaw = current.textRaw || '';
current.textRaw += tok.text + ' '; current.textRaw += `${tok.text} `;
} }
}); });
// shove the name in there for properties, since they are always // shove the name in there for properties, since they are always
// just going to be the value etc. // just going to be the value etc.
if (section.type === 'property' && values[0]) { if (section.type === 'property' && values[0]) {
values[0].textRaw = '`' + section.name + '` ' + values[0].textRaw; values[0].textRaw = `\`${section.name}\` ${values[0].textRaw}`;
} }
// now pull the actual values out of the text bits. // now pull the actual values out of the text bits.
@ -252,9 +251,9 @@ function processList(section) {
// each item is an argument, unless the name is 'return', // each item is an argument, unless the name is 'return',
// in which case it's the return value. // in which case it's the return value.
section.signatures = section.signatures || []; section.signatures = section.signatures || [];
var sig = {} var sig = {};
section.signatures.push(sig); section.signatures.push(sig);
sig.params = values.filter(function(v) { sig.params = values.filter((v) => {
if (v.name === 'return') { if (v.name === 'return') {
sig.return = v; sig.return = v;
return false; return false;
@ -271,7 +270,7 @@ function processList(section) {
delete value.name; delete value.name;
section.typeof = value.type; section.typeof = value.type;
delete value.type; delete value.type;
Object.keys(value).forEach(function(k) { Object.keys(value).forEach((k) => {
section[k] = value[k]; section[k] = value[k];
}); });
break; break;
@ -289,36 +288,36 @@ function processList(section) {
// textRaw = "someobject.someMethod(a, [b=100], [c])" // textRaw = "someobject.someMethod(a, [b=100], [c])"
function parseSignature(text, sig) { function parseSignature(text, sig) {
var params = text.match(paramExpr); let params = text.match(paramExpr);
if (!params) return; if (!params) return;
params = params[1]; params = params[1];
// the ] is irrelevant. [ indicates optionalness. // the ] is irrelevant. [ indicates optionalness.
params = params.replace(/\]/g, ''); params = params.replace(/\]/g, '');
params = params.split(/,/) params = params.split(/,/);
params.forEach(function(p, i, _) { params.forEach((p, i, _) => {
p = p.trim(); p = p.trim();
if (!p) return; if (!p) return;
var param = sig.params[i]; let param = sig.params[i];
var optional = false; let optional = false;
var def; let def;
// [foo] -> optional // [foo] -> optional
if (p.charAt(0) === '[') { if (p.charAt(0) === '[') {
optional = true; optional = true;
p = p.substr(1); p = p.substr(1);
} }
var eq = p.indexOf('='); const eq = p.indexOf('=');
if (eq !== -1) { if (eq !== -1) {
def = p.substr(eq + 1); def = p.substr(eq + 1);
p = p.substr(0, eq); p = p.substr(0, eq);
} }
if (!param) { if (!param) {
param = sig.params[i] = { name: p }; param = sig.params[i] = {name: p};
} }
// at this point, the name should match. // at this point, the name should match.
if (p !== param.name) { if (p !== param.name) {
console.error('Warning: invalid param "%s"', p); console.error('Warning: invalid param "%s"', p);
console.error(' > ' + JSON.stringify(param)); console.error(` > ${JSON.stringify(param)}`);
console.error(' > ' + text); console.error(` > ${text}`);
} }
if (optional) param.optional = true; if (optional) param.optional = true;
if (def !== undefined) param.default = def; if (def !== undefined) param.default = def;
@ -332,18 +331,18 @@ function parseListItem(item) {
// the goal here is to find the name, type, default, and optional. // the goal here is to find the name, type, default, and optional.
// anything left over is 'desc' // anything left over is 'desc'
var text = item.textRaw.trim(); let text = item.textRaw.trim();
// text = text.replace(/^(Argument|Param)s?\s*:?\s*/i, ''); // text = text.replace(/^(Argument|Param)s?\s*:?\s*/i, '');
text = text.replace(/^, /, '').trim(); text = text.replace(/^, /, '').trim();
var retExpr = /^returns?\s*:?\s*/i; const retExpr = /^returns?\s*:?\s*/i;
var ret = text.match(retExpr); const ret = text.match(retExpr);
if (ret) { if (ret) {
item.name = 'return'; item.name = 'return';
text = text.replace(retExpr, ''); text = text.replace(retExpr, '');
} else { } else {
var nameExpr = /^['`"]?([^'`": \{]+)['`"]?\s*:?\s*/; const nameExpr = /^['`"]?([^'`": \{]+)['`"]?\s*:?\s*/;
var name = text.match(nameExpr); const name = text.match(nameExpr);
if (name) { if (name) {
item.name = name[1]; item.name = name[1];
text = text.replace(nameExpr, ''); text = text.replace(nameExpr, '');
@ -351,24 +350,24 @@ function parseListItem(item) {
} }
text = text.trim(); text = text.trim();
var defaultExpr = /\(default\s*[:=]?\s*['"`]?([^, '"`]*)['"`]?\)/i; const defaultExpr = /\(default\s*[:=]?\s*['"`]?([^, '"`]*)['"`]?\)/i;
var def = text.match(defaultExpr); const def = text.match(defaultExpr);
if (def) { if (def) {
item.default = def[1]; item.default = def[1];
text = text.replace(defaultExpr, ''); text = text.replace(defaultExpr, '');
} }
text = text.trim(); text = text.trim();
var typeExpr = /^\{([^\}]+)\}/; const typeExpr = /^\{([^\}]+)\}/;
var type = text.match(typeExpr); const type = text.match(typeExpr);
if (type) { if (type) {
item.type = type[1]; item.type = type[1];
text = text.replace(typeExpr, ''); text = text.replace(typeExpr, '');
} }
text = text.trim(); text = text.trim();
var optExpr = /^Optional\.|(?:, )?Optional$/; const optExpr = /^Optional\.|(?:, )?Optional$/;
var optional = text.match(optExpr); const optional = text.match(optExpr);
if (optional) { if (optional) {
item.optional = true; item.optional = true;
text = text.replace(optExpr, ''); text = text.replace(optExpr, '');
@ -382,9 +381,9 @@ function parseListItem(item) {
function finishSection(section, parent) { function finishSection(section, parent) {
if (!section || !parent) { if (!section || !parent) {
throw new Error('Invalid finishSection call\n'+ throw new Error(`Invalid finishSection call\n${
JSON.stringify(section) + '\n' + JSON.stringify(section)}\n${
JSON.stringify(parent)); JSON.stringify(parent)}`);
} }
if (!section.type) { if (!section.type) {
@ -411,10 +410,10 @@ function finishSection(section, parent) {
// Merge them into the parent. // Merge them into the parent.
if (section.type === 'class' && section.ctors) { if (section.type === 'class' && section.ctors) {
section.signatures = section.signatures || []; section.signatures = section.signatures || [];
var sigs = section.signatures; const sigs = section.signatures;
section.ctors.forEach(function(ctor) { section.ctors.forEach((ctor) => {
ctor.signatures = ctor.signatures || [{}]; ctor.signatures = ctor.signatures || [{}];
ctor.signatures.forEach(function(sig) { ctor.signatures.forEach((sig) => {
sig.desc = ctor.desc; sig.desc = ctor.desc;
}); });
sigs.push.apply(sigs, ctor.signatures); sigs.push.apply(sigs, ctor.signatures);
@ -425,7 +424,7 @@ function finishSection(section, parent) {
// properties are a bit special. // properties are a bit special.
// their "type" is the type of object, not "property" // their "type" is the type of object, not "property"
if (section.properties) { if (section.properties) {
section.properties.forEach(function (p) { section.properties.forEach((p) => {
if (p.typeof) p.type = p.typeof; if (p.typeof) p.type = p.typeof;
else delete p.type; else delete p.type;
delete p.typeof; delete p.typeof;
@ -434,27 +433,27 @@ function finishSection(section, parent) {
// handle clones // handle clones
if (section.clone) { if (section.clone) {
var clone = section.clone; const clone = section.clone;
delete section.clone; delete section.clone;
delete clone.clone; delete clone.clone;
deepCopy(section, clone); deepCopy(section, clone);
finishSection(clone, parent); finishSection(clone, parent);
} }
var plur; let plur;
if (section.type.slice(-1) === 's') { if (section.type.slice(-1) === 's') {
plur = section.type + 'es'; plur = `${section.type}es`;
} else if (section.type.slice(-1) === 'y') { } else if (section.type.slice(-1) === 'y') {
plur = section.type.replace(/y$/, 'ies'); plur = section.type.replace(/y$/, 'ies');
} else { } else {
plur = section.type + 's'; plur = `${section.type}s`;
} }
// if the parent's type is 'misc', then it's just a random // if the parent's type is 'misc', then it's just a random
// collection of stuff, like the "globals" section. // collection of stuff, like the "globals" section.
// Make the children top-level items. // Make the children top-level items.
if (section.type === 'misc') { if (section.type === 'misc') {
Object.keys(section).forEach(function(k) { Object.keys(section).forEach((k) => {
switch (k) { switch (k) {
case 'textRaw': case 'textRaw':
case 'name': case 'name':
@ -486,9 +485,7 @@ function finishSection(section, parent) {
// Not a general purpose deep copy. // Not a general purpose deep copy.
// But sufficient for these basic things. // But sufficient for these basic things.
function deepCopy(src, dest) { function deepCopy(src, dest) {
Object.keys(src).filter(function(k) { Object.keys(src).filter((k) => !dest.hasOwnProperty(k)).forEach((k) => {
return !dest.hasOwnProperty(k);
}).forEach(function(k) {
dest[k] = deepCopy_(src[k]); dest[k] = deepCopy_(src[k]);
}); });
} }
@ -497,14 +494,14 @@ function deepCopy_(src) {
if (!src) return src; if (!src) return src;
if (Array.isArray(src)) { if (Array.isArray(src)) {
var c = new Array(src.length); var c = new Array(src.length);
src.forEach(function(v, i) { src.forEach((v, i) => {
c[i] = deepCopy_(v); c[i] = deepCopy_(v);
}); });
return c; return c;
} }
if (typeof src === 'object') { if (typeof src === 'object') {
var c = {}; var c = {};
Object.keys(src).forEach(function(k) { Object.keys(src).forEach((k) => {
c[k] = deepCopy_(src[k]); c[k] = deepCopy_(src[k]);
}); });
return c; return c;
@ -514,21 +511,21 @@ function deepCopy_(src) {
// these parse out the contents of an H# tag // these parse out the contents of an H# tag
var eventExpr = /^Event(?::|\s)+['"]?([^"']+).*$/i; const eventExpr = /^Event(?::|\s)+['"]?([^"']+).*$/i;
var classExpr = /^Class:\s*([^ ]+).*?$/i; const classExpr = /^Class:\s*([^ ]+).*?$/i;
var propExpr = /^(?:property:?\s*)?[^\.]+\.([^ \.\(\)]+)\s*?$/i; const propExpr = /^(?:property:?\s*)?[^\.]+\.([^ \.\(\)]+)\s*?$/i;
var braceExpr = /^(?:property:?\s*)?[^\.\[]+(\[[^\]]+\])\s*?$/i; const braceExpr = /^(?:property:?\s*)?[^\.\[]+(\[[^\]]+\])\s*?$/i;
var classMethExpr = const classMethExpr =
/^class\s*method\s*:?[^\.]+\.([^ \.\(\)]+)\([^\)]*\)\s*?$/i; /^class\s*method\s*:?[^\.]+\.([^ \.\(\)]+)\([^\)]*\)\s*?$/i;
var methExpr = const methExpr =
/^(?:method:?\s*)?(?:[^\.]+\.)?([^ \.\(\)]+)\([^\)]*\)\s*?$/i; /^(?:method:?\s*)?(?:[^\.]+\.)?([^ \.\(\)]+)\([^\)]*\)\s*?$/i;
var newExpr = /^new ([A-Z][a-z]+)\([^\)]*\)\s*?$/; const newExpr = /^new ([A-Z][a-z]+)\([^\)]*\)\s*?$/;
var paramExpr = /\((.*)\);?$/; var paramExpr = /\((.*)\);?$/;
function newSection(tok) { function newSection(tok) {
var section = {}; const section = {};
// infer the type from the text. // infer the type from the text.
var text = section.textRaw = tok.text; const text = section.textRaw = tok.text;
if (text.match(eventExpr)) { if (text.match(eventExpr)) {
section.type = 'event'; section.type = 'event';
section.name = text.replace(eventExpr, '$1'); section.name = text.replace(eventExpr, '$1');

View file

@ -5,60 +5,60 @@
*/ */
if (process.argv.length != 3) { if (process.argv.length != 3) {
console.error("Use: node extractPadData.js $PADID"); console.error('Use: node extractPadData.js $PADID');
process.exit(1); process.exit(1);
} }
// get the padID // get the padID
let padId = process.argv[2]; const padId = process.argv[2];
let npm = require('../src/node_modules/npm'); const npm = require('../src/node_modules/npm');
npm.load({}, async function(er) { npm.load({}, async (er) => {
if (er) { if (er) {
console.error("Could not load NPM: " + er) console.error(`Could not load NPM: ${er}`);
process.exit(1); process.exit(1);
} }
try { try {
// initialize database // initialize database
let settings = require('../src/node/utils/Settings'); const settings = require('../src/node/utils/Settings');
let db = require('../src/node/db/DB'); const db = require('../src/node/db/DB');
await db.init(); await db.init();
// load extra modules // load extra modules
let dirtyDB = require('../src/node_modules/dirty'); const dirtyDB = require('../src/node_modules/dirty');
let padManager = require('../src/node/db/PadManager'); const padManager = require('../src/node/db/PadManager');
let util = require('util'); const util = require('util');
// initialize output database // initialize output database
let dirty = dirtyDB(padId + '.db'); const dirty = dirtyDB(`${padId}.db`);
// Promise wrapped get and set function // Promise wrapped get and set function
let wrapped = db.db.db.wrappedDB; const wrapped = db.db.db.wrappedDB;
let get = util.promisify(wrapped.get.bind(wrapped)); const get = util.promisify(wrapped.get.bind(wrapped));
let set = util.promisify(dirty.set.bind(dirty)); const set = util.promisify(dirty.set.bind(dirty));
// array in which required key values will be accumulated // array in which required key values will be accumulated
let neededDBValues = ['pad:' + padId]; const neededDBValues = [`pad:${padId}`];
// get the actual pad object // get the actual pad object
let pad = await padManager.getPad(padId); const pad = await padManager.getPad(padId);
// add all authors // add all authors
neededDBValues.push(...pad.getAllAuthors().map(author => 'globalAuthor:' + author)); neededDBValues.push(...pad.getAllAuthors().map((author) => `globalAuthor:${author}`));
// add all revisions // add all revisions
for (let rev = 0; rev <= pad.head; ++rev) { for (let rev = 0; rev <= pad.head; ++rev) {
neededDBValues.push('pad:' + padId + ':revs:' + rev); neededDBValues.push(`pad:${padId}:revs:${rev}`);
} }
// add all chat values // add all chat values
for (let chat = 0; chat <= pad.chatHead; ++chat) { for (let chat = 0; chat <= pad.chatHead; ++chat) {
neededDBValues.push('pad:' + padId + ':chat:' + chat); neededDBValues.push(`pad:${padId}:chat:${chat}`);
} }
for (let dbkey of neededDBValues) { for (const dbkey of neededDBValues) {
let dbvalue = await get(dbkey); let dbvalue = await get(dbkey);
if (dbvalue && typeof dbvalue !== 'object') { if (dbvalue && typeof dbvalue !== 'object') {
dbvalue = JSON.parse(dbvalue); dbvalue = JSON.parse(dbvalue);

View file

@ -1,69 +1,64 @@
var startTime = Date.now(); const startTime = Date.now();
require("ep_etherpad-lite/node_modules/npm").load({}, function(er,npm) { require('ep_etherpad-lite/node_modules/npm').load({}, (er, npm) => {
const fs = require('fs');
var fs = require("fs"); const ueberDB = require('ep_etherpad-lite/node_modules/ueberdb2');
const settings = require('ep_etherpad-lite/node/utils/Settings');
const log4js = require('ep_etherpad-lite/node_modules/log4js');
var ueberDB = require("ep_etherpad-lite/node_modules/ueberdb2"); const dbWrapperSettings = {
var settings = require("ep_etherpad-lite/node/utils/Settings");
var log4js = require('ep_etherpad-lite/node_modules/log4js');
var dbWrapperSettings = {
cache: 0, cache: 0,
writeInterval: 100, writeInterval: 100,
json: false // data is already json encoded json: false, // data is already json encoded
}; };
var db = new ueberDB.database(settings.dbType, settings.dbSettings, dbWrapperSettings, log4js.getLogger("ueberDB")); const db = new ueberDB.database(settings.dbType, settings.dbSettings, dbWrapperSettings, log4js.getLogger('ueberDB'));
var sqlFile = process.argv[2]; const sqlFile = process.argv[2];
//stop if the settings file is not set // stop if the settings file is not set
if(!sqlFile) if (!sqlFile) {
{ console.error('Use: node importSqlFile.js $SQLFILE');
console.error("Use: node importSqlFile.js $SQLFILE");
process.exit(1); process.exit(1);
} }
log("initializing db"); log('initializing db');
db.init(function(err) { db.init((err) => {
//there was an error while initializing the database, output it and stop // there was an error while initializing the database, output it and stop
if(err) if (err) {
{ console.error('ERROR: Problem while initializing the database');
console.error("ERROR: Problem while initializing the database");
console.error(err.stack ? err.stack : err); console.error(err.stack ? err.stack : err);
process.exit(1); process.exit(1);
} } else {
else log('done');
{
log("done");
log("open output file..."); log('open output file...');
var lines = fs.readFileSync(sqlFile, 'utf8').split("\n"); const lines = fs.readFileSync(sqlFile, 'utf8').split('\n');
var count = lines.length; const count = lines.length;
var keyNo = 0; let keyNo = 0;
process.stdout.write("Start importing " + count + " keys...\n"); process.stdout.write(`Start importing ${count} keys...\n`);
lines.forEach(function(l) { lines.forEach((l) => {
if (l.substr(0, 27) == "REPLACE INTO store VALUES (") { if (l.substr(0, 27) == 'REPLACE INTO store VALUES (') {
var pos = l.indexOf("', '"); const pos = l.indexOf("', '");
var key = l.substr(28, pos - 28); const key = l.substr(28, pos - 28);
var value = l.substr(pos + 3); let value = l.substr(pos + 3);
value = value.substr(0, value.length - 2); value = value.substr(0, value.length - 2);
console.log("key: " + key + " val: " + value); console.log(`key: ${key} val: ${value}`);
console.log("unval: " + unescape(value)); console.log(`unval: ${unescape(value)}`);
db.set(key, unescape(value), null); db.set(key, unescape(value), null);
keyNo++; keyNo++;
if (keyNo % 1000 == 0) { if (keyNo % 1000 == 0) {
process.stdout.write(" " + keyNo + "/" + count + "\n"); process.stdout.write(` ${keyNo}/${count}\n`);
} }
} }
}); });
process.stdout.write("\n"); process.stdout.write('\n');
process.stdout.write("done. waiting for db to finish transaction. depended on dbms this may take some time...\n"); process.stdout.write('done. waiting for db to finish transaction. depended on dbms this may take some time...\n');
db.doShutdown(function() { db.doShutdown(() => {
log("finished, imported " + keyNo + " keys."); log(`finished, imported ${keyNo} keys.`);
process.exit(0); process.exit(0);
}); });
} }
@ -71,22 +66,22 @@ require("ep_etherpad-lite/node_modules/npm").load({}, function(er,npm) {
}); });
function log(str) { function log(str) {
console.log((Date.now() - startTime)/1000 + "\t" + str); console.log(`${(Date.now() - startTime) / 1000}\t${str}`);
} }
unescape = function(val) { unescape = function (val) {
// value is a string // value is a string
if (val.substr(0, 1) == "'") { if (val.substr(0, 1) == "'") {
val = val.substr(0, val.length - 1).substr(1); val = val.substr(0, val.length - 1).substr(1);
return val.replace(/\\[0nrbtZ\\'"]/g, function(s) { return val.replace(/\\[0nrbtZ\\'"]/g, (s) => {
switch(s) { switch (s) {
case "\\0": return "\0"; case '\\0': return '\0';
case "\\n": return "\n"; case '\\n': return '\n';
case "\\r": return "\r"; case '\\r': return '\r';
case "\\b": return "\b"; case '\\b': return '\b';
case "\\t": return "\t"; case '\\t': return '\t';
case "\\Z": return "\x1a"; case '\\Z': return '\x1a';
default: return s.substr(1); default: return s.substr(1);
} }
}); });

View file

@ -1,6 +1,5 @@
require("ep_etherpad-lite/node_modules/npm").load({}, function(er,npm) { require('ep_etherpad-lite/node_modules/npm').load({}, (er, npm) => {
process.chdir(`${npm.root}/..`);
process.chdir(npm.root+'/..')
// This script requires that you have modified your settings.json file // This script requires that you have modified your settings.json file
// to work with a real database. Please make a backup of your dirty.db // to work with a real database. Please make a backup of your dirty.db
@ -10,40 +9,40 @@ require("ep_etherpad-lite/node_modules/npm").load({}, function(er,npm) {
// `node --max-old-space-size=4096 bin/migrateDirtyDBtoRealDB.js` // `node --max-old-space-size=4096 bin/migrateDirtyDBtoRealDB.js`
var settings = require("ep_etherpad-lite/node/utils/Settings"); const settings = require('ep_etherpad-lite/node/utils/Settings');
var dirty = require("../src/node_modules/dirty"); let dirty = require('../src/node_modules/dirty');
var ueberDB = require("../src/node_modules/ueberdb2"); const ueberDB = require('../src/node_modules/ueberdb2');
var log4js = require("../src/node_modules/log4js"); const log4js = require('../src/node_modules/log4js');
var dbWrapperSettings = { const dbWrapperSettings = {
"cache": "0", // The cache slows things down when you're mostly writing. cache: '0', // The cache slows things down when you're mostly writing.
"writeInterval": 0 // Write directly to the database, don't buffer writeInterval: 0, // Write directly to the database, don't buffer
}; };
var db = new ueberDB.database(settings.dbType, settings.dbSettings, dbWrapperSettings, log4js.getLogger("ueberDB")); const db = new ueberDB.database(settings.dbType, settings.dbSettings, dbWrapperSettings, log4js.getLogger('ueberDB'));
var i = 0; let i = 0;
var length = 0; let length = 0;
db.init(function() { db.init(() => {
console.log("Waiting for dirtyDB to parse its file."); console.log('Waiting for dirtyDB to parse its file.');
dirty = dirty('var/dirty.db').on("load", function() { dirty = dirty('var/dirty.db').on('load', () => {
dirty.forEach(function(){ dirty.forEach(() => {
length++; length++;
}); });
console.log(`Found ${length} records, processing now.`); console.log(`Found ${length} records, processing now.`);
dirty.forEach(async function(key, value) { dirty.forEach(async (key, value) => {
let error = await db.set(key, value); const error = await db.set(key, value);
console.log(`Wrote record ${i}`); console.log(`Wrote record ${i}`);
i++; i++;
if (i === length) { if (i === length) {
console.log("finished, just clearing up for a bit..."); console.log('finished, just clearing up for a bit...');
setTimeout(function() { setTimeout(() => {
process.exit(0); process.exit(0);
}, 5000); }, 5000);
} }
}); });
console.log("Please wait for all records to flush to database, then kill this process."); console.log('Please wait for all records to flush to database, then kill this process.');
}); });
console.log("done?") console.log('done?');
}); });
}); });

View file

@ -9,96 +9,96 @@ node bin/plugins/checkPlugins.js ep_whatever autofix autocommit
*/ */
const fs = require("fs"); const fs = require('fs');
const { exec } = require("child_process"); const {exec} = require('child_process');
// get plugin name & path from user input // get plugin name & path from user input
const pluginName = process.argv[2]; const pluginName = process.argv[2];
if(!pluginName){ if (!pluginName) {
console.error("no plugin name specified"); console.error('no plugin name specified');
process.exit(1); process.exit(1);
} }
const pluginPath = "node_modules/"+pluginName; const pluginPath = `node_modules/${pluginName}`;
console.log("Checking the plugin: "+ pluginName) console.log(`Checking the plugin: ${pluginName}`);
// Should we autofix? // Should we autofix?
if (process.argv[3] && process.argv[3] === "autofix") var autoFix = true; if (process.argv[3] && process.argv[3] === 'autofix') var autoFix = true;
// Should we update files where possible? // Should we update files where possible?
if (process.argv[5] && process.argv[5] === "autoupdate") var autoUpdate = true; if (process.argv[5] && process.argv[5] === 'autoupdate') var autoUpdate = true;
// Should we automcommit and npm publish?! // Should we automcommit and npm publish?!
if (process.argv[4] && process.argv[4] === "autocommit") var autoCommit = true; if (process.argv[4] && process.argv[4] === 'autocommit') var autoCommit = true;
if(autoCommit){ if (autoCommit) {
console.warn("Auto commit is enabled, I hope you know what you are doing...") console.warn('Auto commit is enabled, I hope you know what you are doing...');
} }
fs.readdir(pluginPath, function (err, rootFiles) { fs.readdir(pluginPath, (err, rootFiles) => {
//handling error // handling error
if (err) { if (err) {
return console.log('Unable to scan directory: ' + err); return console.log(`Unable to scan directory: ${err}`);
} }
// rewriting files to lower case // rewriting files to lower case
var files = []; const files = [];
// some files we need to know the actual file name. Not compulsory but might help in the future. // some files we need to know the actual file name. Not compulsory but might help in the future.
var readMeFileName; let readMeFileName;
var repository; let repository;
var hasAutoFixed = false; let hasAutoFixed = false;
for (var i = 0; i < rootFiles.length; i++) { for (let i = 0; i < rootFiles.length; i++) {
if(rootFiles[i].toLowerCase().indexOf("readme") !== -1) readMeFileName = rootFiles[i]; if (rootFiles[i].toLowerCase().indexOf('readme') !== -1) readMeFileName = rootFiles[i];
files.push(rootFiles[i].toLowerCase()); files.push(rootFiles[i].toLowerCase());
} }
if(files.indexOf(".git") === -1){ if (files.indexOf('.git') === -1) {
console.error("No .git folder, aborting"); console.error('No .git folder, aborting');
process.exit(1); process.exit(1);
} }
// do a git pull... // do a git pull...
var child_process = require('child_process'); var child_process = require('child_process');
try{ try {
child_process.execSync('git pull ',{"cwd":pluginPath+"/"}); child_process.execSync('git pull ', {cwd: `${pluginPath}/`});
}catch(e){ } catch (e) {
console.error("Error git pull", e); console.error('Error git pull', e);
}; }
try { try {
const path = pluginPath + '/.github/workflows/npmpublish.yml'; const path = `${pluginPath}/.github/workflows/npmpublish.yml`;
if (!fs.existsSync(path)) { if (!fs.existsSync(path)) {
console.log('no .github/workflows/npmpublish.yml, create one and set npm secret to auto publish to npm on commit'); console.log('no .github/workflows/npmpublish.yml, create one and set npm secret to auto publish to npm on commit');
if (autoFix) { if (autoFix) {
const npmpublish = const npmpublish =
fs.readFileSync('bin/plugins/lib/npmpublish.yml', {encoding: 'utf8', flag: 'r'}); fs.readFileSync('bin/plugins/lib/npmpublish.yml', {encoding: 'utf8', flag: 'r'});
fs.mkdirSync(pluginPath + '/.github/workflows', {recursive: true}); fs.mkdirSync(`${pluginPath}/.github/workflows`, {recursive: true});
fs.writeFileSync(path, npmpublish); fs.writeFileSync(path, npmpublish);
hasAutoFixed = true; hasAutoFixed = true;
console.log("If you haven't already, setup autopublish for this plugin https://github.com/ether/etherpad-lite/wiki/Plugins:-Automatically-publishing-to-npm-on-commit-to-Github-Repo"); console.log("If you haven't already, setup autopublish for this plugin https://github.com/ether/etherpad-lite/wiki/Plugins:-Automatically-publishing-to-npm-on-commit-to-Github-Repo");
} else { } else {
console.log('Setup autopublish for this plugin https://github.com/ether/etherpad-lite/wiki/Plugins:-Automatically-publishing-to-npm-on-commit-to-Github-Repo'); console.log('Setup autopublish for this plugin https://github.com/ether/etherpad-lite/wiki/Plugins:-Automatically-publishing-to-npm-on-commit-to-Github-Repo');
} }
}else{ } else {
// autopublish exists, we should check the version.. // autopublish exists, we should check the version..
// checkVersion takes two file paths and checks for a version string in them. // checkVersion takes two file paths and checks for a version string in them.
const currVersionFile = fs.readFileSync(path, {encoding: 'utf8', flag: 'r'}); const currVersionFile = fs.readFileSync(path, {encoding: 'utf8', flag: 'r'});
const existingConfigLocation = currVersionFile.indexOf("##ETHERPAD_NPM_V="); const existingConfigLocation = currVersionFile.indexOf('##ETHERPAD_NPM_V=');
const existingValue = parseInt(currVersionFile.substr(existingConfigLocation+17, existingConfigLocation.length)); const existingValue = parseInt(currVersionFile.substr(existingConfigLocation + 17, existingConfigLocation.length));
const reqVersionFile = fs.readFileSync('bin/plugins/lib/npmpublish.yml', {encoding: 'utf8', flag: 'r'}); const reqVersionFile = fs.readFileSync('bin/plugins/lib/npmpublish.yml', {encoding: 'utf8', flag: 'r'});
const reqConfigLocation = reqVersionFile.indexOf("##ETHERPAD_NPM_V="); const reqConfigLocation = reqVersionFile.indexOf('##ETHERPAD_NPM_V=');
const reqValue = parseInt(reqVersionFile.substr(reqConfigLocation+17, reqConfigLocation.length)); const reqValue = parseInt(reqVersionFile.substr(reqConfigLocation + 17, reqConfigLocation.length));
if(!existingValue || (reqValue > existingValue)){ if (!existingValue || (reqValue > existingValue)) {
const npmpublish = const npmpublish =
fs.readFileSync('bin/plugins/lib/npmpublish.yml', {encoding: 'utf8', flag: 'r'}); fs.readFileSync('bin/plugins/lib/npmpublish.yml', {encoding: 'utf8', flag: 'r'});
fs.mkdirSync(pluginPath + '/.github/workflows', {recursive: true}); fs.mkdirSync(`${pluginPath}/.github/workflows`, {recursive: true});
fs.writeFileSync(path, npmpublish); fs.writeFileSync(path, npmpublish);
hasAutoFixed = true; hasAutoFixed = true;
} }
@ -107,152 +107,151 @@ fs.readdir(pluginPath, function (err, rootFiles) {
console.error(err); console.error(err);
} }
if(files.indexOf("package.json") === -1){ if (files.indexOf('package.json') === -1) {
console.warn("no package.json, please create"); console.warn('no package.json, please create');
} }
if(files.indexOf("package.json") !== -1){ if (files.indexOf('package.json') !== -1) {
let packageJSON = fs.readFileSync(pluginPath+"/package.json", {encoding:'utf8', flag:'r'}); const packageJSON = fs.readFileSync(`${pluginPath}/package.json`, {encoding: 'utf8', flag: 'r'});
let parsedPackageJSON = JSON.parse(packageJSON); const parsedPackageJSON = JSON.parse(packageJSON);
if(autoFix){ if (autoFix) {
var updatedPackageJSON = false; let updatedPackageJSON = false;
if(!parsedPackageJSON.funding){ if (!parsedPackageJSON.funding) {
updatedPackageJSON = true; updatedPackageJSON = true;
parsedPackageJSON.funding = { parsedPackageJSON.funding = {
"type": "individual", type: 'individual',
"url": "http://etherpad.org/" url: 'http://etherpad.org/',
};
} }
} if (updatedPackageJSON) {
if(updatedPackageJSON){
hasAutoFixed = true; hasAutoFixed = true;
fs.writeFileSync(pluginPath+"/package.json", JSON.stringify(parsedPackageJSON, null, 2)); fs.writeFileSync(`${pluginPath}/package.json`, JSON.stringify(parsedPackageJSON, null, 2));
} }
} }
if(packageJSON.toLowerCase().indexOf("repository") === -1){ if (packageJSON.toLowerCase().indexOf('repository') === -1) {
console.warn("No repository in package.json"); console.warn('No repository in package.json');
if(autoFix){ if (autoFix) {
console.warn("Repository not detected in package.json. Please add repository section manually.") console.warn('Repository not detected in package.json. Please add repository section manually.');
} }
}else{ } else {
// useful for creating README later. // useful for creating README later.
repository = parsedPackageJSON.repository.url; repository = parsedPackageJSON.repository.url;
} }
// include lint config // include lint config
if(packageJSON.toLowerCase().indexOf("devdependencies") === -1 || !parsedPackageJSON.devDependencies.eslint){ if (packageJSON.toLowerCase().indexOf('devdependencies') === -1 || !parsedPackageJSON.devDependencies.eslint) {
console.warn("Missing eslint reference in devDependencies"); console.warn('Missing eslint reference in devDependencies');
if(autoFix){ if (autoFix) {
let devDependencies = { const devDependencies = {
"eslint": "^7.14.0", 'eslint': '^7.14.0',
"eslint-config-etherpad": "^1.0.10", 'eslint-config-etherpad': '^1.0.10',
"eslint-plugin-mocha": "^8.0.0", 'eslint-plugin-mocha': '^8.0.0',
"eslint-plugin-node": "^11.1.0", 'eslint-plugin-node': '^11.1.0',
"eslint-plugin-prefer-arrow": "^1.2.2", 'eslint-plugin-prefer-arrow': '^1.2.2',
"eslint-plugin-promise": "^4.2.1" 'eslint-plugin-promise': '^4.2.1',
} };
hasAutoFixed = true; hasAutoFixed = true;
parsedPackageJSON.devDependencies = devDependencies; parsedPackageJSON.devDependencies = devDependencies;
fs.writeFileSync(pluginPath+"/package.json", JSON.stringify(parsedPackageJSON, null, 2)); fs.writeFileSync(`${pluginPath}/package.json`, JSON.stringify(parsedPackageJSON, null, 2));
let child_process = require('child_process'); const child_process = require('child_process');
try{ try {
child_process.execSync('npm install',{"cwd":pluginPath+"/"}); child_process.execSync('npm install', {cwd: `${pluginPath}/`});
hasAutoFixed = true; hasAutoFixed = true;
}catch(e){ } catch (e) {
console.error("Failed to create package-lock.json"); console.error('Failed to create package-lock.json');
} }
} }
} }
if(packageJSON.toLowerCase().indexOf("eslintconfig") === -1){ if (packageJSON.toLowerCase().indexOf('eslintconfig') === -1) {
console.warn("No esLintConfig in package.json"); console.warn('No esLintConfig in package.json');
if(autoFix){ if (autoFix) {
let eslintConfig = { const eslintConfig = {
"root": true, root: true,
"extends": "etherpad/plugin" extends: 'etherpad/plugin',
} };
hasAutoFixed = true; hasAutoFixed = true;
parsedPackageJSON.eslintConfig = eslintConfig; parsedPackageJSON.eslintConfig = eslintConfig;
fs.writeFileSync(pluginPath+"/package.json", JSON.stringify(parsedPackageJSON, null, 2)); fs.writeFileSync(`${pluginPath}/package.json`, JSON.stringify(parsedPackageJSON, null, 2));
} }
} }
if(packageJSON.toLowerCase().indexOf("scripts") === -1){ if (packageJSON.toLowerCase().indexOf('scripts') === -1) {
console.warn("No scripts in package.json"); console.warn('No scripts in package.json');
if(autoFix){ if (autoFix) {
let scripts = { const scripts = {
"lint": "eslint .", 'lint': 'eslint .',
"lint:fix": "eslint --fix ." 'lint:fix': 'eslint --fix .',
} };
hasAutoFixed = true; hasAutoFixed = true;
parsedPackageJSON.scripts = scripts; parsedPackageJSON.scripts = scripts;
fs.writeFileSync(pluginPath+"/package.json", JSON.stringify(parsedPackageJSON, null, 2)); fs.writeFileSync(`${pluginPath}/package.json`, JSON.stringify(parsedPackageJSON, null, 2));
} }
} }
if(packageJSON.toLowerCase().indexOf("engines") === -1){ if (packageJSON.toLowerCase().indexOf('engines') === -1) {
console.warn("No engines in package.json"); console.warn('No engines in package.json');
if(autoFix){ if (autoFix) {
let engines = { const engines = {
"lint": "eslint ." lint: 'eslint .',
} };
hasAutoFixed = true; hasAutoFixed = true;
parsedPackageJSON.engines = engines; parsedPackageJSON.engines = engines;
fs.writeFileSync(pluginPath+"/package.json", JSON.stringify(parsedPackageJSON, null, 2)); fs.writeFileSync(`${pluginPath}/package.json`, JSON.stringify(parsedPackageJSON, null, 2));
}
} }
} }
} if (files.indexOf('package-lock.json') === -1) {
console.warn('package-lock.json file not found. Please run npm install in the plugin folder and commit the package-lock.json file.');
if(files.indexOf("package-lock.json") === -1){ if (autoFix) {
console.warn("package-lock.json file not found. Please run npm install in the plugin folder and commit the package-lock.json file.")
if(autoFix){
var child_process = require('child_process'); var child_process = require('child_process');
try{ try {
child_process.execSync('npm install',{"cwd":pluginPath+"/"}); child_process.execSync('npm install', {cwd: `${pluginPath}/`});
console.log("Making package-lock.json"); console.log('Making package-lock.json');
hasAutoFixed = true; hasAutoFixed = true;
}catch(e){ } catch (e) {
console.error("Failed to create package-lock.json"); console.error('Failed to create package-lock.json');
} }
} }
} }
if(files.indexOf("readme") === -1 && files.indexOf("readme.md") === -1){ if (files.indexOf('readme') === -1 && files.indexOf('readme.md') === -1) {
console.warn("README.md file not found, please create"); console.warn('README.md file not found, please create');
if(autoFix){ if (autoFix) {
console.log("Autofixing missing README.md file, please edit the README.md file further to include plugin specific details."); console.log('Autofixing missing README.md file, please edit the README.md file further to include plugin specific details.');
let readme = fs.readFileSync("bin/plugins/lib/README.md", {encoding:'utf8', flag:'r'}) let readme = fs.readFileSync('bin/plugins/lib/README.md', {encoding: 'utf8', flag: 'r'});
readme = readme.replace(/\[plugin_name\]/g, pluginName); readme = readme.replace(/\[plugin_name\]/g, pluginName);
if(repository){ if (repository) {
let org = repository.split("/")[3]; const org = repository.split('/')[3];
let name = repository.split("/")[4]; const name = repository.split('/')[4];
readme = readme.replace(/\[org_name\]/g, org); readme = readme.replace(/\[org_name\]/g, org);
readme = readme.replace(/\[repo_url\]/g, name); readme = readme.replace(/\[repo_url\]/g, name);
fs.writeFileSync(pluginPath+"/README.md", readme); fs.writeFileSync(`${pluginPath}/README.md`, readme);
}else{ } else {
console.warn("Unable to find repository in package.json, aborting.") console.warn('Unable to find repository in package.json, aborting.');
} }
} }
} }
if(files.indexOf("readme") !== -1 && files.indexOf("readme.md") !== -1){ if (files.indexOf('readme') !== -1 && files.indexOf('readme.md') !== -1) {
let readme = fs.readFileSync(pluginPath+"/"+readMeFileName, {encoding:'utf8', flag:'r'}); const readme = fs.readFileSync(`${pluginPath}/${readMeFileName}`, {encoding: 'utf8', flag: 'r'});
if(readme.toLowerCase().indexOf("license") === -1){ if (readme.toLowerCase().indexOf('license') === -1) {
console.warn("No license section in README"); console.warn('No license section in README');
if(autoFix){ if (autoFix) {
console.warn("Please add License section to README manually.") console.warn('Please add License section to README manually.');
} }
} }
} }
if(files.indexOf("license") === -1 && files.indexOf("license.md") === -1){ if (files.indexOf('license') === -1 && files.indexOf('license.md') === -1) {
console.warn("LICENSE.md file not found, please create"); console.warn('LICENSE.md file not found, please create');
if(autoFix){ if (autoFix) {
hasAutoFixed = true; hasAutoFixed = true;
console.log("Autofixing missing LICENSE.md file, including Apache 2 license."); console.log('Autofixing missing LICENSE.md file, including Apache 2 license.');
exec("git config user.name", (error, name, stderr) => { exec('git config user.name', (error, name, stderr) => {
if (error) { if (error) {
console.log(`error: ${error.message}`); console.log(`error: ${error.message}`);
return; return;
@ -261,120 +260,118 @@ fs.readdir(pluginPath, function (err, rootFiles) {
console.log(`stderr: ${stderr}`); console.log(`stderr: ${stderr}`);
return; return;
} }
let license = fs.readFileSync("bin/plugins/lib/LICENSE.md", {encoding:'utf8', flag:'r'}); let license = fs.readFileSync('bin/plugins/lib/LICENSE.md', {encoding: 'utf8', flag: 'r'});
license = license.replace("[yyyy]", new Date().getFullYear()); license = license.replace('[yyyy]', new Date().getFullYear());
license = license.replace("[name of copyright owner]", name) license = license.replace('[name of copyright owner]', name);
fs.writeFileSync(pluginPath+"/LICENSE.md", license); fs.writeFileSync(`${pluginPath}/LICENSE.md`, license);
}); });
} }
} }
var travisConfig = fs.readFileSync("bin/plugins/lib/travis.yml", {encoding:'utf8', flag:'r'}); let travisConfig = fs.readFileSync('bin/plugins/lib/travis.yml', {encoding: 'utf8', flag: 'r'});
travisConfig = travisConfig.replace(/\[plugin_name\]/g, pluginName); travisConfig = travisConfig.replace(/\[plugin_name\]/g, pluginName);
if(files.indexOf(".travis.yml") === -1){ if (files.indexOf('.travis.yml') === -1) {
console.warn(".travis.yml file not found, please create. .travis.yml is used for automatically CI testing Etherpad. It is useful to know if your plugin breaks another feature for example.") console.warn('.travis.yml file not found, please create. .travis.yml is used for automatically CI testing Etherpad. It is useful to know if your plugin breaks another feature for example.');
// TODO: Make it check version of the .travis file to see if it needs an update. // TODO: Make it check version of the .travis file to see if it needs an update.
if(autoFix){ if (autoFix) {
hasAutoFixed = true; hasAutoFixed = true;
console.log("Autofixing missing .travis.yml file"); console.log('Autofixing missing .travis.yml file');
fs.writeFileSync(pluginPath+"/.travis.yml", travisConfig); fs.writeFileSync(`${pluginPath}/.travis.yml`, travisConfig);
console.log("Travis file created, please sign into travis and enable this repository") console.log('Travis file created, please sign into travis and enable this repository');
} }
} }
if(autoFix && autoUpdate){ if (autoFix && autoUpdate) {
// checks the file versioning of .travis and updates it to the latest. // checks the file versioning of .travis and updates it to the latest.
let existingConfig = fs.readFileSync(pluginPath + "/.travis.yml", {encoding:'utf8', flag:'r'}); const existingConfig = fs.readFileSync(`${pluginPath}/.travis.yml`, {encoding: 'utf8', flag: 'r'});
let existingConfigLocation = existingConfig.indexOf("##ETHERPAD_TRAVIS_V="); const existingConfigLocation = existingConfig.indexOf('##ETHERPAD_TRAVIS_V=');
let existingValue = parseInt(existingConfig.substr(existingConfigLocation+20, existingConfig.length)); const existingValue = parseInt(existingConfig.substr(existingConfigLocation + 20, existingConfig.length));
let newConfigLocation = travisConfig.indexOf("##ETHERPAD_TRAVIS_V="); const newConfigLocation = travisConfig.indexOf('##ETHERPAD_TRAVIS_V=');
let newValue = parseInt(travisConfig.substr(newConfigLocation+20, travisConfig.length)); const newValue = parseInt(travisConfig.substr(newConfigLocation + 20, travisConfig.length));
if(existingConfigLocation === -1){ if (existingConfigLocation === -1) {
console.warn("no previous .travis.yml version found so writing new.") console.warn('no previous .travis.yml version found so writing new.');
// we will write the newTravisConfig to the location. // we will write the newTravisConfig to the location.
fs.writeFileSync(pluginPath + "/.travis.yml", travisConfig); fs.writeFileSync(`${pluginPath}/.travis.yml`, travisConfig);
}else{ } else if (newValue > existingValue) {
if(newValue > existingValue){ console.log('updating .travis.yml');
console.log("updating .travis.yml"); fs.writeFileSync(`${pluginPath}/.travis.yml`, travisConfig);
fs.writeFileSync(pluginPath + "/.travis.yml", travisConfig);
hasAutoFixed = true; hasAutoFixed = true;
} }
} }
}
if(files.indexOf(".gitignore") === -1){ if (files.indexOf('.gitignore') === -1) {
console.warn(".gitignore file not found, please create. .gitignore files are useful to ensure files aren't incorrectly commited to a repository.") console.warn(".gitignore file not found, please create. .gitignore files are useful to ensure files aren't incorrectly commited to a repository.");
if(autoFix){ if (autoFix) {
hasAutoFixed = true; hasAutoFixed = true;
console.log("Autofixing missing .gitignore file"); console.log('Autofixing missing .gitignore file');
let gitignore = fs.readFileSync("bin/plugins/lib/gitignore", {encoding:'utf8', flag:'r'}); const gitignore = fs.readFileSync('bin/plugins/lib/gitignore', {encoding: 'utf8', flag: 'r'});
fs.writeFileSync(pluginPath+"/.gitignore", gitignore); fs.writeFileSync(`${pluginPath}/.gitignore`, gitignore);
} }
} }
// if we include templates but don't have translations... // if we include templates but don't have translations...
if(files.indexOf("templates") !== -1 && files.indexOf("locales") === -1){ if (files.indexOf('templates') !== -1 && files.indexOf('locales') === -1) {
console.warn("Translations not found, please create. Translation files help with Etherpad accessibility."); console.warn('Translations not found, please create. Translation files help with Etherpad accessibility.');
} }
if(files.indexOf(".ep_initialized") !== -1){ if (files.indexOf('.ep_initialized') !== -1) {
console.warn(".ep_initialized found, please remove. .ep_initialized should never be commited to git and should only exist once the plugin has been executed one time.") console.warn('.ep_initialized found, please remove. .ep_initialized should never be commited to git and should only exist once the plugin has been executed one time.');
if(autoFix){ if (autoFix) {
hasAutoFixed = true; hasAutoFixed = true;
console.log("Autofixing incorrectly existing .ep_initialized file"); console.log('Autofixing incorrectly existing .ep_initialized file');
fs.unlinkSync(pluginPath+"/.ep_initialized"); fs.unlinkSync(`${pluginPath}/.ep_initialized`);
} }
} }
if(files.indexOf("npm-debug.log") !== -1){ if (files.indexOf('npm-debug.log') !== -1) {
console.warn("npm-debug.log found, please remove. npm-debug.log should never be commited to your repository.") console.warn('npm-debug.log found, please remove. npm-debug.log should never be commited to your repository.');
if(autoFix){ if (autoFix) {
hasAutoFixed = true; hasAutoFixed = true;
console.log("Autofixing incorrectly existing npm-debug.log file"); console.log('Autofixing incorrectly existing npm-debug.log file');
fs.unlinkSync(pluginPath+"/npm-debug.log"); fs.unlinkSync(`${pluginPath}/npm-debug.log`);
} }
} }
if(files.indexOf("static") !== -1){ if (files.indexOf('static') !== -1) {
fs.readdir(pluginPath+"/static", function (errRead, staticFiles) { fs.readdir(`${pluginPath}/static`, (errRead, staticFiles) => {
if(staticFiles.indexOf("tests") === -1){ if (staticFiles.indexOf('tests') === -1) {
console.warn("Test files not found, please create tests. https://github.com/ether/etherpad-lite/wiki/Creating-a-plugin#writing-and-running-front-end-tests-for-your-plugin") console.warn('Test files not found, please create tests. https://github.com/ether/etherpad-lite/wiki/Creating-a-plugin#writing-and-running-front-end-tests-for-your-plugin');
} }
}) });
}else{ } else {
console.warn("Test files not found, please create tests. https://github.com/ether/etherpad-lite/wiki/Creating-a-plugin#writing-and-running-front-end-tests-for-your-plugin") console.warn('Test files not found, please create tests. https://github.com/ether/etherpad-lite/wiki/Creating-a-plugin#writing-and-running-front-end-tests-for-your-plugin');
} }
// linting begins // linting begins
if(autoFix){ if (autoFix) {
var lintCmd = 'npm run lint:fix'; var lintCmd = 'npm run lint:fix';
}else{ } else {
var lintCmd = 'npm run lint'; var lintCmd = 'npm run lint';
} }
try{ try {
child_process.execSync(lintCmd,{"cwd":pluginPath+"/"}); child_process.execSync(lintCmd, {cwd: `${pluginPath}/`});
console.log("Linting..."); console.log('Linting...');
if(autoFix){ if (autoFix) {
// todo: if npm run lint doesn't do anything no need for... // todo: if npm run lint doesn't do anything no need for...
hasAutoFixed = true; hasAutoFixed = true;
} }
}catch(e){ } catch (e) {
// it is gonna throw an error anyway // it is gonna throw an error anyway
console.log("Manual linting probably required, check with: npm run lint"); console.log('Manual linting probably required, check with: npm run lint');
} }
// linting ends. // linting ends.
if(hasAutoFixed){ if (hasAutoFixed) {
console.log("Fixes applied, please check git diff then run the following command:\n\n") console.log('Fixes applied, please check git diff then run the following command:\n\n');
// bump npm Version // bump npm Version
if(autoCommit){ if (autoCommit) {
// holy shit you brave. // holy shit you brave.
console.log("Attempting autocommit and auto publish to npm") console.log('Attempting autocommit and auto publish to npm');
// github should push to npm for us :) // github should push to npm for us :)
exec("cd node_modules/"+ pluginName + " && git add -A && git commit --allow-empty -m 'autofixes from Etherpad checkPlugins.js' && git push && cd ../..", (error, name, stderr) => { exec(`cd node_modules/${pluginName} && git add -A && git commit --allow-empty -m 'autofixes from Etherpad checkPlugins.js' && git push && cd ../..`, (error, name, stderr) => {
if (error) { if (error) {
console.log(`error: ${error.message}`); console.log(`error: ${error.message}`);
return; return;
@ -383,14 +380,13 @@ fs.readdir(pluginPath, function (err, rootFiles) {
console.log(`stderr: ${stderr}`); console.log(`stderr: ${stderr}`);
return; return;
} }
console.log("I think she's got it! By George she's got it!") console.log("I think she's got it! By George she's got it!");
process.exit(0) process.exit(0);
}); });
}else{ } else {
console.log("cd node_modules/"+ pluginName + " && git add -A && git commit --allow-empty -m 'autofixes from Etherpad checkPlugins.js' && npm version patch && git add package.json && git commit --allow-empty -m 'bump version' && git push && npm publish && cd ../..") console.log(`cd node_modules/${pluginName} && git add -A && git commit --allow-empty -m 'autofixes from Etherpad checkPlugins.js' && npm version patch && git add package.json && git commit --allow-empty -m 'bump version' && git push && npm publish && cd ../..`);
} }
} }
console.log("Finished"); console.log('Finished');
}); });

View file

@ -3,38 +3,39 @@
known "good" revision. known "good" revision.
*/ */
if(process.argv.length != 4 && process.argv.length != 5) { if (process.argv.length != 4 && process.argv.length != 5) {
console.error("Use: node bin/repairPad.js $PADID $REV [$NEWPADID]"); console.error('Use: node bin/repairPad.js $PADID $REV [$NEWPADID]');
process.exit(1); process.exit(1);
} }
var npm = require("../src/node_modules/npm"); const npm = require('../src/node_modules/npm');
var async = require("../src/node_modules/async"); const async = require('../src/node_modules/async');
var ueberDB = require("../src/node_modules/ueberdb2"); const ueberDB = require('../src/node_modules/ueberdb2');
var padId = process.argv[2]; const padId = process.argv[2];
var newRevHead = process.argv[3]; const newRevHead = process.argv[3];
var newPadId = process.argv[4] || padId + "-rebuilt"; const newPadId = process.argv[4] || `${padId}-rebuilt`;
var db, oldPad, newPad, settings; let db, oldPad, newPad, settings;
var AuthorManager, ChangeSet, Pad, PadManager; let AuthorManager, ChangeSet, Pad, PadManager;
async.series([ async.series([
function(callback) { function (callback) {
npm.load({}, function(err) { npm.load({}, (err) => {
if(err) { if (err) {
console.error("Could not load NPM: " + err) console.error(`Could not load NPM: ${err}`);
process.exit(1); process.exit(1);
} else { } else {
callback(); callback();
} }
}) });
}, },
function(callback) { function (callback) {
// Get a handle into the database // Get a handle into the database
db = require('../src/node/db/DB'); db = require('../src/node/db/DB');
db.init(callback); db.init(callback);
}, function(callback) { },
function (callback) {
PadManager = require('../src/node/db/PadManager'); PadManager = require('../src/node/db/PadManager');
Pad = require('../src/node/db/Pad').Pad; Pad = require('../src/node/db/Pad').Pad;
// Get references to the original pad and to a newly created pad // Get references to the original pad and to a newly created pad
@ -45,78 +46,81 @@ async.series([
// Validate the newPadId if specified and that a pad with that ID does // Validate the newPadId if specified and that a pad with that ID does
// not already exist to avoid overwriting it. // not already exist to avoid overwriting it.
if (!PadManager.isValidPadId(newPadId)) { if (!PadManager.isValidPadId(newPadId)) {
console.error("Cannot create a pad with that id as it is invalid"); console.error('Cannot create a pad with that id as it is invalid');
process.exit(1); process.exit(1);
} }
PadManager.doesPadExists(newPadId, function(err, exists) { PadManager.doesPadExists(newPadId, (err, exists) => {
if (exists) { if (exists) {
console.error("Cannot create a pad with that id as it already exists"); console.error('Cannot create a pad with that id as it already exists');
process.exit(1); process.exit(1);
} }
}); });
PadManager.getPad(padId, function(err, pad) { PadManager.getPad(padId, (err, pad) => {
oldPad = pad; oldPad = pad;
newPad = new Pad(newPadId); newPad = new Pad(newPadId);
callback(); callback();
}); });
}, function(callback) { },
function (callback) {
// Clone all Chat revisions // Clone all Chat revisions
var chatHead = oldPad.chatHead; const chatHead = oldPad.chatHead;
for(var i = 0, curHeadNum = 0; i <= chatHead; i++) { for (var i = 0, curHeadNum = 0; i <= chatHead; i++) {
db.db.get("pad:" + padId + ":chat:" + i, function (err, chat) { db.db.get(`pad:${padId}:chat:${i}`, (err, chat) => {
db.db.set("pad:" + newPadId + ":chat:" + curHeadNum++, chat); db.db.set(`pad:${newPadId}:chat:${curHeadNum++}`, chat);
console.log("Created: Chat Revision: pad:" + newPadId + ":chat:" + curHeadNum); console.log(`Created: Chat Revision: pad:${newPadId}:chat:${curHeadNum}`);
}); });
} }
callback(); callback();
}, function(callback) { },
function (callback) {
// Rebuild Pad from revisions up to and including the new revision head // Rebuild Pad from revisions up to and including the new revision head
AuthorManager = require("../src/node/db/AuthorManager"); AuthorManager = require('../src/node/db/AuthorManager');
Changeset = require("ep_etherpad-lite/static/js/Changeset"); Changeset = require('ep_etherpad-lite/static/js/Changeset');
// Author attributes are derived from changesets, but there can also be // Author attributes are derived from changesets, but there can also be
// non-author attributes with specific mappings that changesets depend on // non-author attributes with specific mappings that changesets depend on
// and, AFAICT, cannot be recreated any other way // and, AFAICT, cannot be recreated any other way
newPad.pool.numToAttrib = oldPad.pool.numToAttrib; newPad.pool.numToAttrib = oldPad.pool.numToAttrib;
for(var curRevNum = 0; curRevNum <= newRevHead; curRevNum++) { for (let curRevNum = 0; curRevNum <= newRevHead; curRevNum++) {
db.db.get("pad:" + padId + ":revs:" + curRevNum, function(err, rev) { db.db.get(`pad:${padId}:revs:${curRevNum}`, (err, rev) => {
if (rev.meta) { if (rev.meta) {
throw "The specified revision number could not be found."; throw 'The specified revision number could not be found.';
} }
var newRevNum = ++newPad.head; const newRevNum = ++newPad.head;
var newRevId = "pad:" + newPad.id + ":revs:" + newRevNum; const newRevId = `pad:${newPad.id}:revs:${newRevNum}`;
db.db.set(newRevId, rev); db.db.set(newRevId, rev);
AuthorManager.addPad(rev.meta.author, newPad.id); AuthorManager.addPad(rev.meta.author, newPad.id);
newPad.atext = Changeset.applyToAText(rev.changeset, newPad.atext, newPad.pool); newPad.atext = Changeset.applyToAText(rev.changeset, newPad.atext, newPad.pool);
console.log("Created: Revision: pad:" + newPad.id + ":revs:" + newRevNum); console.log(`Created: Revision: pad:${newPad.id}:revs:${newRevNum}`);
if (newRevNum == newRevHead) { if (newRevNum == newRevHead) {
callback(); callback();
} }
}); });
} }
}, function(callback) { },
function (callback) {
// Add saved revisions up to the new revision head // Add saved revisions up to the new revision head
console.log(newPad.head); console.log(newPad.head);
var newSavedRevisions = []; const newSavedRevisions = [];
for(var i in oldPad.savedRevisions) { for (const i in oldPad.savedRevisions) {
savedRev = oldPad.savedRevisions[i] savedRev = oldPad.savedRevisions[i];
if (savedRev.revNum <= newRevHead) { if (savedRev.revNum <= newRevHead) {
newSavedRevisions.push(savedRev); newSavedRevisions.push(savedRev);
console.log("Added: Saved Revision: " + savedRev.revNum); console.log(`Added: Saved Revision: ${savedRev.revNum}`);
} }
} }
newPad.savedRevisions = newSavedRevisions; newPad.savedRevisions = newSavedRevisions;
callback(); callback();
}, function(callback) { },
function (callback) {
// Save the source pad // Save the source pad
db.db.set("pad:"+newPadId, newPad, function(err) { db.db.set(`pad:${newPadId}`, newPad, (err) => {
console.log("Created: Source Pad: pad:" + newPadId); console.log(`Created: Source Pad: pad:${newPadId}`);
newPad.saveToDatabase().then(() => callback(), callback); newPad.saveToDatabase().then(() => callback(), callback);
}); });
} },
], function (err) { ], (err) => {
if(err) throw err; if (err) { throw err; } else {
else { console.info('finished');
console.info("finished");
process.exit(0); process.exit(0);
} }
}); });

View file

@ -2,47 +2,47 @@
* This is a repair tool. It extracts all datas of a pad, removes and inserts them again. * This is a repair tool. It extracts all datas of a pad, removes and inserts them again.
*/ */
console.warn("WARNING: This script must not be used while etherpad is running!"); console.warn('WARNING: This script must not be used while etherpad is running!');
if (process.argv.length != 3) { if (process.argv.length != 3) {
console.error("Use: node bin/repairPad.js $PADID"); console.error('Use: node bin/repairPad.js $PADID');
process.exit(1); process.exit(1);
} }
// get the padID // get the padID
var padId = process.argv[2]; const padId = process.argv[2];
let npm = require("../src/node_modules/npm"); const npm = require('../src/node_modules/npm');
npm.load({}, async function(er) { npm.load({}, async (er) => {
if (er) { if (er) {
console.error("Could not load NPM: " + er) console.error(`Could not load NPM: ${er}`);
process.exit(1); process.exit(1);
} }
try { try {
// intialize database // intialize database
let settings = require('../src/node/utils/Settings'); const settings = require('../src/node/utils/Settings');
let db = require('../src/node/db/DB'); const db = require('../src/node/db/DB');
await db.init(); await db.init();
// get the pad // get the pad
let padManager = require('../src/node/db/PadManager'); const padManager = require('../src/node/db/PadManager');
let pad = await padManager.getPad(padId); const pad = await padManager.getPad(padId);
// accumulate the required keys // accumulate the required keys
let neededDBValues = ["pad:" + padId]; const neededDBValues = [`pad:${padId}`];
// add all authors // add all authors
neededDBValues.push(...pad.getAllAuthors().map(author => "globalAuthor:")); neededDBValues.push(...pad.getAllAuthors().map((author) => 'globalAuthor:'));
// add all revisions // add all revisions
for (let rev = 0; rev <= pad.head; ++rev) { for (let rev = 0; rev <= pad.head; ++rev) {
neededDBValues.push("pad:" + padId + ":revs:" + rev); neededDBValues.push(`pad:${padId}:revs:${rev}`);
} }
// add all chat values // add all chat values
for (let chat = 0; chat <= pad.chatHead; ++chat) { for (let chat = 0; chat <= pad.chatHead; ++chat) {
neededDBValues.push("pad:" + padId + ":chat:" + chat); neededDBValues.push(`pad:${padId}:chat:${chat}`);
} }
// //
@ -55,21 +55,20 @@ npm.load({}, async function(er) {
// //
// See gitlab issue #3545 // See gitlab issue #3545
// //
console.info("aborting [gitlab #3545]"); console.info('aborting [gitlab #3545]');
process.exit(1); process.exit(1);
// now fetch and reinsert every key // now fetch and reinsert every key
neededDBValues.forEach(function(key, value) { neededDBValues.forEach((key, value) => {
console.log("Key: " + key+ ", value: " + value); console.log(`Key: ${key}, value: ${value}`);
db.remove(key); db.remove(key);
db.set(key, value); db.set(key, value);
}); });
console.info("finished"); console.info('finished');
process.exit(0); process.exit(0);
} catch (er) { } catch (er) {
if (er.name === "apierror") { if (er.name === 'apierror') {
console.error(er); console.error(er);
} else { } else {
console.trace(er); console.trace(er);

View file

@ -1,6 +1,6 @@
/* global __dirname, exports, require */ /* global __dirname, exports, require */
function m(mod) { return __dirname + '/../../src/' + mod; } function m(mod) { return `${__dirname}/../../src/${mod}`; }
const apiHandler = require(m('node/handler/APIHandler')); const apiHandler = require(m('node/handler/APIHandler'));
const log4js = require(m('node_modules/log4js')); const log4js = require(m('node_modules/log4js'));
@ -25,7 +25,7 @@ const logLevel = exports.logger.level;
// https://github.com/mochajs/mocha/issues/2640 // https://github.com/mochajs/mocha/issues/2640
process.on('unhandledRejection', (reason, promise) => { throw reason; }); process.on('unhandledRejection', (reason, promise) => { throw reason; });
exports.init = async function() { exports.init = async function () {
if (inited) return exports.agent; if (inited) return exports.agent;
inited = true; inited = true;
@ -50,7 +50,7 @@ exports.init = async function() {
backups.authnFailureDelayMs = webaccess.authnFailureDelayMs; backups.authnFailureDelayMs = webaccess.authnFailureDelayMs;
webaccess.authnFailureDelayMs = 0; webaccess.authnFailureDelayMs = 0;
after(async function() { after(async function () {
webaccess.authnFailureDelayMs = backups.authnFailureDelayMs; webaccess.authnFailureDelayMs = backups.authnFailureDelayMs;
await server.stop(); await server.stop();
// Note: This does not unset settings that were added. // Note: This does not unset settings that were added.

View file

@ -3,44 +3,44 @@
* Usage: node fuzzImportTest.js * Usage: node fuzzImportTest.js
*/ */
const common = require('./common'); const common = require('./common');
const settings = require(__dirname+'/loadSettings').loadSettings(); const settings = require(`${__dirname}/loadSettings`).loadSettings();
const host = "http://" + settings.ip + ":" + settings.port; const host = `http://${settings.ip}:${settings.port}`;
const request = require('request'); const request = require('request');
const froth = require('mocha-froth'); const froth = require('mocha-froth');
const apiKey = common.apiKey; const apiKey = common.apiKey;
var apiVersion = 1; const apiVersion = 1;
var testPadId = "TEST_fuzz" + makeid(); const testPadId = `TEST_fuzz${makeid()}`;
var endPoint = function(point, version){ const endPoint = function (point, version) {
version = version || apiVersion; version = version || apiVersion;
return '/api/'+version+'/'+point+'?apikey='+apiKey; return `/api/${version}/${point}?apikey=${apiKey}`;
} };
console.log("Testing against padID", testPadId); console.log('Testing against padID', testPadId);
console.log("To watch the test live visit " + host + "/p/" + testPadId); console.log(`To watch the test live visit ${host}/p/${testPadId}`);
console.log("Tests will start in 5 seconds, click the URL now!"); console.log('Tests will start in 5 seconds, click the URL now!');
setTimeout(function(){ setTimeout(() => {
for (let i=1; i<1000000; i++) { // 1M runs for (let i = 1; i < 1000000; i++) { // 1M runs
setTimeout( function timer(){ setTimeout(() => {
runTest(i); runTest(i);
}, i*100 ); // 100 ms }, i * 100); // 100 ms
} }
},5000); // wait 5 seconds }, 5000); // wait 5 seconds
function runTest(number){ function runTest(number) {
request(host + endPoint('createPad') + '&padID=' + testPadId, function(err, res, body){ request(`${host + endPoint('createPad')}&padID=${testPadId}`, (err, res, body) => {
var req = request.post(host + '/p/'+testPadId+'/import', function (err, res, body) { const req = request.post(`${host}/p/${testPadId}/import`, (err, res, body) => {
if (err) { if (err) {
throw new Error("FAILURE", err); throw new Error('FAILURE', err);
}else{ } else {
console.log("Success"); console.log('Success');
} }
}); });
var fN = '/test.txt'; let fN = '/test.txt';
var cT = 'text/plain'; let cT = 'text/plain';
// To be more agressive every other test we mess with Etherpad // To be more agressive every other test we mess with Etherpad
// We provide a weird file name and also set a weird contentType // We provide a weird file name and also set a weird contentType
@ -49,23 +49,20 @@ function runTest(number){
cT = froth().toString(); cT = froth().toString();
} }
let form = req.form(); const form = req.form();
form.append('file', froth().toString(), { form.append('file', froth().toString(), {
filename: fN, filename: fN,
contentType: cT contentType: cT,
}); });
}); });
} }
function makeid() { function makeid() {
var text = ""; let text = '';
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for( var i=0; i < 5; i++ ){ for (let i = 0; i < 5; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length)); text += possible.charAt(Math.floor(Math.random() * possible.length));
} }
return text; return text;
} }

View file

@ -7,23 +7,23 @@
*/ */
const common = require('../../common'); const common = require('../../common');
const supertest = require(__dirname + '/../../../../src/node_modules/supertest'); const supertest = require(`${__dirname}/../../../../src/node_modules/supertest`);
const settings = require(__dirname + '/../../../../src/node/utils/Settings'); const settings = require(`${__dirname}/../../../../src/node/utils/Settings`);
const api = supertest('http://' + settings.ip + ':' + settings.port); const api = supertest(`http://${settings.ip}:${settings.port}`);
var validateOpenAPI = require(__dirname + '/../../../../src/node_modules/openapi-schema-validation').validate; const validateOpenAPI = require(`${__dirname}/../../../../src/node_modules/openapi-schema-validation`).validate;
const apiKey = common.apiKey; const apiKey = common.apiKey;
var apiVersion = 1; let apiVersion = 1;
var testPadId = makeid(); const testPadId = makeid();
describe(__filename, function() { describe(__filename, function () {
describe('API Versioning', function() { describe('API Versioning', function () {
it('errors if can not connect', function(done) { it('errors if can not connect', function (done) {
api api
.get('/api/') .get('/api/')
.expect(function(res) { .expect((res) => {
apiVersion = res.body.currentVersion; apiVersion = res.body.currentVersion;
if (!res.body.currentVersion) throw new Error('No version set in API'); if (!res.body.currentVersion) throw new Error('No version set in API');
return; return;
@ -32,12 +32,12 @@ describe(__filename, function() {
}); });
}); });
describe('OpenAPI definition', function() { describe('OpenAPI definition', function () {
it('generates valid openapi definition document', function(done) { it('generates valid openapi definition document', function (done) {
api api
.get('/api/openapi.json') .get('/api/openapi.json')
.expect(function(res) { .expect((res) => {
const { valid, errors } = validateOpenAPI(res.body, 3); const {valid, errors} = validateOpenAPI(res.body, 3);
if (!valid) { if (!valid) {
const prettyErrors = JSON.stringify(errors, null, 2); const prettyErrors = JSON.stringify(errors, null, 2);
throw new Error(`Document is not valid OpenAPI. ${errors.length} validation errors:\n${prettyErrors}`); throw new Error(`Document is not valid OpenAPI. ${errors.length} validation errors:\n${prettyErrors}`);
@ -48,11 +48,11 @@ describe(__filename, function() {
}); });
}); });
describe('jsonp support', function() { describe('jsonp support', function () {
it('supports jsonp calls', function(done) { it('supports jsonp calls', function (done) {
api api
.get(endPoint('createPad') + '&jsonp=jsonp_1&padID=' + testPadId) .get(`${endPoint('createPad')}&jsonp=jsonp_1&padID=${testPadId}`)
.expect(function(res) { .expect((res) => {
if (!res.text.match('jsonp_1')) throw new Error('no jsonp call seen'); if (!res.text.match('jsonp_1')) throw new Error('no jsonp call seen');
}) })
.expect('Content-Type', /javascript/) .expect('Content-Type', /javascript/)
@ -61,15 +61,15 @@ describe(__filename, function() {
}); });
}); });
var endPoint = function(point) { var endPoint = function (point) {
return '/api/' + apiVersion + '/' + point + '?apikey=' + apiKey; return `/api/${apiVersion}/${point}?apikey=${apiKey}`;
}; };
function makeid() { function makeid() {
var text = ''; let text = '';
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (var i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length)); text += possible.charAt(Math.floor(Math.random() * possible.length));
} }
return text; return text;

View file

@ -5,86 +5,86 @@
*/ */
const common = require('../../common'); const common = require('../../common');
const supertest = require(__dirname+'/../../../../src/node_modules/supertest'); const supertest = require(`${__dirname}/../../../../src/node_modules/supertest`);
const fs = require('fs'); const fs = require('fs');
const settings = require(__dirname + '/../../../../src/node/utils/Settings'); const settings = require(`${__dirname}/../../../../src/node/utils/Settings`);
const api = supertest('http://'+settings.ip+":"+settings.port); const api = supertest(`http://${settings.ip}:${settings.port}`);
const apiKey = common.apiKey; const apiKey = common.apiKey;
var apiVersion = 1; let apiVersion = 1;
var testPadId = makeid(); const testPadId = makeid();
describe(__filename, function() { describe(__filename, function () {
describe('Connectivity For Character Encoding', function() { describe('Connectivity For Character Encoding', function () {
it('can connect', function(done) { it('can connect', function (done) {
api.get('/api/') api.get('/api/')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200, done) .expect(200, done);
});
}); });
})
describe('API Versioning', function() { describe('API Versioning', function () {
it('finds the version tag', function(done) { it('finds the version tag', function (done) {
api.get('/api/') api.get('/api/')
.expect(function(res){ .expect((res) => {
apiVersion = res.body.currentVersion; apiVersion = res.body.currentVersion;
if (!res.body.currentVersion) throw new Error("No version set in API"); if (!res.body.currentVersion) throw new Error('No version set in API');
return; return;
}) })
.expect(200, done) .expect(200, done);
});
}); });
})
describe('Permission', function() { describe('Permission', function () {
it('errors with invalid APIKey', function(done) { it('errors with invalid APIKey', function (done) {
// This is broken because Etherpad doesn't handle HTTP codes properly see #2343 // This is broken because Etherpad doesn't handle HTTP codes properly see #2343
// If your APIKey is password you deserve to fail all tests anyway // If your APIKey is password you deserve to fail all tests anyway
var permErrorURL = '/api/'+apiVersion+'/createPad?apikey=password&padID=test'; const permErrorURL = `/api/${apiVersion}/createPad?apikey=password&padID=test`;
api.get(permErrorURL) api.get(permErrorURL)
.expect(401, done) .expect(401, done);
}); });
})
describe('createPad', function() {
it('creates a new Pad', function(done) {
api.get(endPoint('createPad')+"&padID="+testPadId)
.expect(function(res){
if(res.body.code !== 0) throw new Error("Unable to create new Pad");
})
.expect('Content-Type', /json/)
.expect(200, done)
}); });
})
describe('setHTML', function() { describe('createPad', function () {
it('Sets the HTML of a Pad attempting to weird utf8 encoded content', function(done) { it('creates a new Pad', function (done) {
fs.readFile('../tests/backend/specs/api/emojis.html', 'utf8', function(err, html) { api.get(`${endPoint('createPad')}&padID=${testPadId}`)
api.post(endPoint('setHTML')) .expect((res) => {
.send({ if (res.body.code !== 0) throw new Error('Unable to create new Pad');
"padID": testPadId,
"html": html,
})
.expect(function(res){
if(res.body.code !== 0) throw new Error("Can't set HTML properly");
}) })
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200, done); .expect(200, done);
}); });
}); });
})
describe('getHTML', function() { describe('setHTML', function () {
it('get the HTML of Pad with emojis', function(done) { it('Sets the HTML of a Pad attempting to weird utf8 encoded content', function (done) {
api.get(endPoint('getHTML')+"&padID="+testPadId) fs.readFile('../tests/backend/specs/api/emojis.html', 'utf8', (err, html) => {
.expect(function(res){ api.post(endPoint('setHTML'))
if (res.body.data.html.indexOf("&#127484") === -1) { .send({
throw new Error("Unable to get the HTML"); padID: testPadId,
html,
})
.expect((res) => {
if (res.body.code !== 0) throw new Error("Can't set HTML properly");
})
.expect('Content-Type', /json/)
.expect(200, done);
});
});
});
describe('getHTML', function () {
it('get the HTML of Pad with emojis', function (done) {
api.get(`${endPoint('getHTML')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.data.html.indexOf('&#127484') === -1) {
throw new Error('Unable to get the HTML');
} }
}) })
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200, done) .expect(200, done);
});
}); });
})
}); });
/* /*
@ -93,16 +93,16 @@ describe(__filename, function() {
*/ */
var endPoint = function(point, version){ var endPoint = function (point, version) {
version = version || apiVersion; version = version || apiVersion;
return '/api/'+version+'/'+point+'?apikey='+apiKey; return `/api/${version}/${point}?apikey=${apiKey}`;
} };
function makeid() { function makeid() {
var text = ""; let text = '';
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for( var i=0; i < 10; i++ ){ for (let i = 0; i < 10; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length)); text += possible.charAt(Math.floor(Math.random() * possible.length));
} }
return text; return text;

View file

@ -1,4 +1,4 @@
function m(mod) { return __dirname + '/../../../../src/' + mod; } function m(mod) { return `${__dirname}/../../../../src/${mod}`; }
const common = require('../../common'); const common = require('../../common');
const settings = require(m('node/utils/Settings')); const settings = require(m('node/utils/Settings'));
@ -6,27 +6,27 @@ const supertest = require(m('node_modules/supertest'));
const api = supertest(`http://${settings.ip}:${settings.port}`); const api = supertest(`http://${settings.ip}:${settings.port}`);
const apiKey = common.apiKey; const apiKey = common.apiKey;
var apiVersion = 1; let apiVersion = 1;
var authorID = ""; let authorID = '';
var padID = makeid(); const padID = makeid();
var timestamp = Date.now(); const timestamp = Date.now();
describe(__filename, function() { describe(__filename, function () {
describe('API Versioning', function(){ describe('API Versioning', function () {
it('errors if can not connect', function(done) { it('errors if can not connect', function (done) {
api.get('/api/') api.get('/api/')
.expect(function(res){ .expect((res) => {
apiVersion = res.body.currentVersion; apiVersion = res.body.currentVersion;
if (!res.body.currentVersion) throw new Error("No version set in API"); if (!res.body.currentVersion) throw new Error('No version set in API');
return; return;
}) })
.expect(200, done) .expect(200, done);
});
}); });
})
// BEGIN GROUP AND AUTHOR TESTS // BEGIN GROUP AND AUTHOR TESTS
///////////////////////////////////// // ///////////////////////////////////
///////////////////////////////////// // ///////////////////////////////////
/* Tests performed /* Tests performed
-> createPad(padID) -> createPad(padID)
@ -36,76 +36,76 @@ describe(__filename, function() {
-> getChatHistory(padID) -> getChatHistory(padID)
*/ */
describe('createPad', function(){ describe('createPad', function () {
it('creates a new Pad', function(done) { it('creates a new Pad', function (done) {
api.get(endPoint('createPad')+"&padID="+padID) api.get(`${endPoint('createPad')}&padID=${padID}`)
.expect(function(res){ .expect((res) => {
if(res.body.code !== 0) throw new Error("Unable to create new Pad"); if (res.body.code !== 0) throw new Error('Unable to create new Pad');
}) })
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200, done) .expect(200, done);
});
}); });
})
describe('createAuthor', function(){ describe('createAuthor', function () {
it('Creates an author with a name set', function(done) { it('Creates an author with a name set', function (done) {
api.get(endPoint('createAuthor')) api.get(endPoint('createAuthor'))
.expect(function(res){ .expect((res) => {
if(res.body.code !== 0 || !res.body.data.authorID) throw new Error("Unable to create author"); if (res.body.code !== 0 || !res.body.data.authorID) throw new Error('Unable to create author');
authorID = res.body.data.authorID; // we will be this author for the rest of the tests authorID = res.body.data.authorID; // we will be this author for the rest of the tests
}) })
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200, done) .expect(200, done);
});
}); });
})
describe('appendChatMessage', function(){ describe('appendChatMessage', function () {
it('Adds a chat message to the pad', function(done) { it('Adds a chat message to the pad', function (done) {
api.get(endPoint('appendChatMessage')+"&padID="+padID+"&text=blalblalbha&authorID="+authorID+"&time="+timestamp) api.get(`${endPoint('appendChatMessage')}&padID=${padID}&text=blalblalbha&authorID=${authorID}&time=${timestamp}`)
.expect(function(res){ .expect((res) => {
if(res.body.code !== 0) throw new Error("Unable to create chat message"); if (res.body.code !== 0) throw new Error('Unable to create chat message');
}) })
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200, done) .expect(200, done);
});
}); });
})
describe('getChatHead', function(){ describe('getChatHead', function () {
it('Gets the head of chat', function(done) { it('Gets the head of chat', function (done) {
api.get(endPoint('getChatHead')+"&padID="+padID) api.get(`${endPoint('getChatHead')}&padID=${padID}`)
.expect(function(res){ .expect((res) => {
if(res.body.data.chatHead !== 0) throw new Error("Chat Head Length is wrong"); if (res.body.data.chatHead !== 0) throw new Error('Chat Head Length is wrong');
if(res.body.code !== 0) throw new Error("Unable to get chat head"); if (res.body.code !== 0) throw new Error('Unable to get chat head');
}) })
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200, done) .expect(200, done);
});
}); });
})
describe('getChatHistory', function(){ describe('getChatHistory', function () {
it('Gets Chat History of a Pad', function(done) { it('Gets Chat History of a Pad', function (done) {
api.get(endPoint('getChatHistory')+"&padID="+padID) api.get(`${endPoint('getChatHistory')}&padID=${padID}`)
.expect(function(res){ .expect((res) => {
if(res.body.data.messages.length !== 1) throw new Error("Chat History Length is wrong"); if (res.body.data.messages.length !== 1) throw new Error('Chat History Length is wrong');
if(res.body.code !== 0) throw new Error("Unable to get chat history"); if (res.body.code !== 0) throw new Error('Unable to get chat history');
}) })
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200, done) .expect(200, done);
});
}); });
})
}); });
var endPoint = function(point){ var endPoint = function (point) {
return '/api/'+apiVersion+'/'+point+'?apikey='+apiKey; return `/api/${apiVersion}/${point}?apikey=${apiKey}`;
} };
function makeid() { function makeid() {
var text = ""; let text = '';
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for( var i=0; i < 5; i++ ){ for (let i = 0; i < 5; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length)); text += possible.charAt(Math.floor(Math.random() * possible.length));
} }
return text; return text;

View file

@ -6,39 +6,39 @@
*/ */
const common = require('../../common'); const common = require('../../common');
const supertest = require(__dirname+'/../../../../src/node_modules/supertest'); const supertest = require(`${__dirname}/../../../../src/node_modules/supertest`);
const settings = require(__dirname+'/../../../../tests/container/loadSettings.js').loadSettings(); const settings = require(`${__dirname}/../../../../tests/container/loadSettings.js`).loadSettings();
const api = supertest('http://'+settings.ip+":"+settings.port); const api = supertest(`http://${settings.ip}:${settings.port}`);
const apiKey = common.apiKey; const apiKey = common.apiKey;
var apiVersion = 1; const apiVersion = 1;
var lastEdited = ""; const lastEdited = '';
var testImports = { const testImports = {
"malformed": { 'malformed': {
input: '<html><body><li>wtf</ul></body></html>', input: '<html><body><li>wtf</ul></body></html>',
expectedHTML: '<!DOCTYPE HTML><html><body>wtf<br><br></body></html>', expectedHTML: '<!DOCTYPE HTML><html><body>wtf<br><br></body></html>',
expectedText: 'wtf\n\n' expectedText: 'wtf\n\n',
}, },
"nonelistiteminlist #3620":{ 'nonelistiteminlist #3620': {
input: '<html><body><ul>test<li>FOO</li></ul></body></html>', input: '<html><body><ul>test<li>FOO</li></ul></body></html>',
expectedHTML: '<!DOCTYPE HTML><html><body><ul class="bullet">test<li>FOO</ul><br></body></html>', expectedHTML: '<!DOCTYPE HTML><html><body><ul class="bullet">test<li>FOO</ul><br></body></html>',
expectedText: '\ttest\n\t* FOO\n\n' expectedText: '\ttest\n\t* FOO\n\n',
}, },
"whitespaceinlist #3620":{ 'whitespaceinlist #3620': {
input: '<html><body><ul> <li>FOO</li></ul></body></html>', input: '<html><body><ul> <li>FOO</li></ul></body></html>',
expectedHTML: '<!DOCTYPE HTML><html><body><ul class="bullet"><li>FOO</ul><br></body></html>', expectedHTML: '<!DOCTYPE HTML><html><body><ul class="bullet"><li>FOO</ul><br></body></html>',
expectedText: '\t* FOO\n\n' expectedText: '\t* FOO\n\n',
}, },
"prefixcorrectlinenumber":{ 'prefixcorrectlinenumber': {
input: '<html><body><ol><li>should be 1</li><li>should be 2</li></ol></body></html>', input: '<html><body><ol><li>should be 1</li><li>should be 2</li></ol></body></html>',
expectedHTML: '<!DOCTYPE HTML><html><body><ol start="1" class="number"><li>should be 1</li><li>should be 2</ol><br></body></html>', expectedHTML: '<!DOCTYPE HTML><html><body><ol start="1" class="number"><li>should be 1</li><li>should be 2</ol><br></body></html>',
expectedText: '\t1. should be 1\n\t2. should be 2\n\n' expectedText: '\t1. should be 1\n\t2. should be 2\n\n',
}, },
"prefixcorrectlinenumbernested":{ 'prefixcorrectlinenumbernested': {
input: '<html><body><ol><li>should be 1</li><ol><li>foo</li></ol><li>should be 2</li></ol></body></html>', input: '<html><body><ol><li>should be 1</li><ol><li>foo</li></ol><li>should be 2</li></ol></body></html>',
expectedHTML: '<!DOCTYPE HTML><html><body><ol start="1" class="number"><li>should be 1<ol start="2" class="number"><li>foo</ol><li>should be 2</ol><br></body></html>', expectedHTML: '<!DOCTYPE HTML><html><body><ol start="1" class="number"><li>should be 1<ol start="2" class="number"><li>foo</ol><li>should be 2</ol><br></body></html>',
expectedText: '\t1. should be 1\n\t\t1.1. foo\n\t2. should be 2\n\n' expectedText: '\t1. should be 1\n\t\t1.1. foo\n\t2. should be 2\n\n',
}, },
/* /*
@ -54,39 +54,39 @@ var testImports = {
expectedText: '\t1. should be 1\n\ttest\n\t2. should be 2\n\n' expectedText: '\t1. should be 1\n\ttest\n\t2. should be 2\n\n'
} }
*/ */
} };
describe(__filename, function() { describe(__filename, function () {
Object.keys(testImports).forEach(function(testName) { Object.keys(testImports).forEach((testName) => {
var testPadId = makeid(); const testPadId = makeid();
test = testImports[testName]; test = testImports[testName];
describe('createPad', function() { describe('createPad', function () {
it('creates a new Pad', function(done) { it('creates a new Pad', function (done) {
api.get(endPoint('createPad') + "&padID=" + testPadId) api.get(`${endPoint('createPad')}&padID=${testPadId}`)
.expect(function(res) { .expect((res) => {
if(res.body.code !== 0) throw new Error("Unable to create new Pad"); if (res.body.code !== 0) throw new Error('Unable to create new Pad');
}) })
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200, done) .expect(200, done);
});
}); });
})
describe('setHTML', function() { describe('setHTML', function () {
it('Sets the HTML', function(done) { it('Sets the HTML', function (done) {
api.get(endPoint('setHTML') + "&padID=" + testPadId + "&html=" + test.input) api.get(`${endPoint('setHTML')}&padID=${testPadId}&html=${test.input}`)
.expect(function(res) { .expect((res) => {
if(res.body.code !== 0) throw new Error("Error:" + testName) if (res.body.code !== 0) throw new Error(`Error:${testName}`);
}) })
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200, done) .expect(200, done);
});
}); });
})
describe('getHTML', function() { describe('getHTML', function () {
it('Gets back the HTML of a Pad', function(done) { it('Gets back the HTML of a Pad', function (done) {
api.get(endPoint('getHTML') + "&padID=" + testPadId) api.get(`${endPoint('getHTML')}&padID=${testPadId}`)
.expect(function(res) { .expect((res) => {
var receivedHtml = res.body.data.html; const receivedHtml = res.body.data.html;
if (receivedHtml !== test.expectedHTML) { if (receivedHtml !== test.expectedHTML) {
throw new Error(`HTML received from export is not the one we were expecting. throw new Error(`HTML received from export is not the one we were expecting.
Test Name: Test Name:
@ -103,15 +103,15 @@ describe(__filename, function() {
} }
}) })
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200, done) .expect(200, done);
});
}); });
})
describe('getText', function() { describe('getText', function () {
it('Gets back the Text of a Pad', function(done) { it('Gets back the Text of a Pad', function (done) {
api.get(endPoint('getText') + "&padID=" + testPadId) api.get(`${endPoint('getText')}&padID=${testPadId}`)
.expect(function(res) { .expect((res) => {
var receivedText = res.body.data.text; const receivedText = res.body.data.text;
if (receivedText !== test.expectedText) { if (receivedText !== test.expectedText) {
throw new Error(`Text received from export is not the one we were expecting. throw new Error(`Text received from export is not the one we were expecting.
Test Name: Test Name:
@ -128,33 +128,33 @@ describe(__filename, function() {
} }
}) })
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200, done) .expect(200, done);
});
}); });
})
}); });
}); });
var endPoint = function(point, version){ var endPoint = function (point, version) {
version = version || apiVersion; version = version || apiVersion;
return '/api/'+version+'/'+point+'?apikey='+apiKey; return `/api/${version}/${point}?apikey=${apiKey}`;
} };
function makeid() { function makeid() {
var text = ""; let text = '';
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for( var i=0; i < 5; i++ ){ for (let i = 0; i < 5; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length)); text += possible.charAt(Math.floor(Math.random() * possible.length));
} }
return text; return text;
} }
function generateLongText(){ function generateLongText() {
var text = ""; let text = '';
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for( var i=0; i < 80000; i++ ){ for (let i = 0; i < 80000; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length)); text += possible.charAt(Math.floor(Math.random() * possible.length));
} }
return text; return text;
@ -163,21 +163,18 @@ function generateLongText(){
// Need this to compare arrays (listSavedRevisions test) // Need this to compare arrays (listSavedRevisions test)
Array.prototype.equals = function (array) { Array.prototype.equals = function (array) {
// if the other array is a falsy value, return // if the other array is a falsy value, return
if (!array) if (!array) return false;
return false;
// compare lengths - can save a lot of time // compare lengths - can save a lot of time
if (this.length != array.length) if (this.length != array.length) return false;
return false; for (let i = 0, l = this.length; i < l; i++) {
for (var i = 0, l=this.length; i < l; i++) {
// Check if we have nested arrays // Check if we have nested arrays
if (this[i] instanceof Array && array[i] instanceof Array) { if (this[i] instanceof Array && array[i] instanceof Array) {
// recurse into the nested arrays // recurse into the nested arrays
if (!this[i].equals(array[i])) if (!this[i].equals(array[i])) return false;
return false;
} else if (this[i] != array[i]) { } else if (this[i] != array[i]) {
// Warning - two different object instances will never be equal: {x:20} != {x:20} // Warning - two different object instances will never be equal: {x:20} != {x:20}
return false; return false;
} }
} }
return true; return true;
} };

View file

@ -6,38 +6,38 @@
const assert = require('assert').strict; const assert = require('assert').strict;
const common = require('../../common'); const common = require('../../common');
const superagent = require(__dirname+'/../../../../src/node_modules/superagent'); const superagent = require(`${__dirname}/../../../../src/node_modules/superagent`);
const fs = require('fs'); const fs = require('fs');
const settings = require(__dirname+'/../../../../src/node/utils/Settings'); const settings = require(`${__dirname}/../../../../src/node/utils/Settings`);
const padManager = require(__dirname+'/../../../../src/node/db/PadManager'); const padManager = require(`${__dirname}/../../../../src/node/db/PadManager`);
const plugins = require(__dirname+'/../../../../src/static/js/pluginfw/plugin_defs'); const plugins = require(`${__dirname}/../../../../src/static/js/pluginfw/plugin_defs`);
const padText = fs.readFileSync("../tests/backend/specs/api/test.txt"); const padText = fs.readFileSync('../tests/backend/specs/api/test.txt');
const etherpadDoc = fs.readFileSync("../tests/backend/specs/api/test.etherpad"); const etherpadDoc = fs.readFileSync('../tests/backend/specs/api/test.etherpad');
const wordDoc = fs.readFileSync("../tests/backend/specs/api/test.doc"); const wordDoc = fs.readFileSync('../tests/backend/specs/api/test.doc');
const wordXDoc = fs.readFileSync("../tests/backend/specs/api/test.docx"); const wordXDoc = fs.readFileSync('../tests/backend/specs/api/test.docx');
const odtDoc = fs.readFileSync("../tests/backend/specs/api/test.odt"); const odtDoc = fs.readFileSync('../tests/backend/specs/api/test.odt');
const pdfDoc = fs.readFileSync("../tests/backend/specs/api/test.pdf"); const pdfDoc = fs.readFileSync('../tests/backend/specs/api/test.pdf');
let agent; let agent;
const apiKey = common.apiKey; const apiKey = common.apiKey;
var apiVersion = 1; const apiVersion = 1;
const testPadId = makeid(); const testPadId = makeid();
const testPadIdEnc = encodeURIComponent(testPadId); const testPadIdEnc = encodeURIComponent(testPadId);
describe(__filename, function() { describe(__filename, function () {
before(async function() { agent = await common.init(); }); before(async function () { agent = await common.init(); });
describe('Connectivity', function(){ describe('Connectivity', function () {
it('can connect', async function() { it('can connect', async function () {
await agent.get('/api/') await agent.get('/api/')
.expect(200) .expect(200)
.expect('Content-Type', /json/); .expect('Content-Type', /json/);
}); });
}); });
describe('API Versioning', function(){ describe('API Versioning', function () {
it('finds the version tag', async function() { it('finds the version tag', async function () {
await agent.get('/api/') await agent.get('/api/')
.expect(200) .expect(200)
.expect((res) => assert(res.body.currentVersion)); .expect((res) => assert(res.body.currentVersion));
@ -70,10 +70,10 @@ describe(__filename, function() {
curl -s -v --form file=@/home/jose/test.txt http://127.0.0.1:9001/p/foo/import curl -s -v --form file=@/home/jose/test.txt http://127.0.0.1:9001/p/foo/import
*/ */
describe('Imports and Exports', function(){ describe('Imports and Exports', function () {
const backups = {}; const backups = {};
beforeEach(async function() { beforeEach(async function () {
backups.hooks = {}; backups.hooks = {};
for (const hookName of ['preAuthorize', 'authenticate', 'authorize']) { for (const hookName of ['preAuthorize', 'authenticate', 'authorize']) {
backups.hooks[hookName] = plugins.hooks[hookName]; backups.hooks[hookName] = plugins.hooks[hookName];
@ -86,43 +86,43 @@ describe(__filename, function() {
settings.users = {user: {password: 'user-password'}}; settings.users = {user: {password: 'user-password'}};
}); });
afterEach(async function() { afterEach(async function () {
Object.assign(plugins.hooks, backups.hooks); Object.assign(plugins.hooks, backups.hooks);
// Note: This does not unset settings that were added. // Note: This does not unset settings that were added.
Object.assign(settings, backups.settings); Object.assign(settings, backups.settings);
}); });
it('creates a new Pad, imports content to it, checks that content', async function() { it('creates a new Pad, imports content to it, checks that content', async function () {
await agent.get(endPoint('createPad') + `&padID=${testPadId}`) await agent.get(`${endPoint('createPad')}&padID=${testPadId}`)
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect((res) => assert.equal(res.body.code, 0)); .expect((res) => assert.equal(res.body.code, 0));
await agent.post(`/p/${testPadId}/import`) await agent.post(`/p/${testPadId}/import`)
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
.expect(200); .expect(200);
await agent.get(endPoint('getText') + `&padID=${testPadId}`) await agent.get(`${endPoint('getText')}&padID=${testPadId}`)
.expect(200) .expect(200)
.expect((res) => assert.equal(res.body.data.text, padText.toString())); .expect((res) => assert.equal(res.body.data.text, padText.toString()));
}); });
it('gets read only pad Id and exports the html and text for this pad', async function(){ it('gets read only pad Id and exports the html and text for this pad', async function () {
let ro = await agent.get(endPoint('getReadOnlyID')+"&padID="+testPadId) const ro = await agent.get(`${endPoint('getReadOnlyID')}&padID=${testPadId}`)
.expect(200) .expect(200)
.expect((res) => assert.ok(JSON.parse(res.text).data.readOnlyID)); .expect((res) => assert.ok(JSON.parse(res.text).data.readOnlyID));
let readOnlyId = JSON.parse(ro.text).data.readOnlyID; const readOnlyId = JSON.parse(ro.text).data.readOnlyID;
await agent.get(`/p/${readOnlyId}/export/html`) await agent.get(`/p/${readOnlyId}/export/html`)
.expect(200) .expect(200)
.expect((res) => assert(res.text.indexOf("This is the") !== -1)); .expect((res) => assert(res.text.indexOf('This is the') !== -1));
await agent.get(`/p/${readOnlyId}/export/txt`) await agent.get(`/p/${readOnlyId}/export/txt`)
.expect(200) .expect(200)
.expect((res) => assert(res.text.indexOf("This is the") !== -1)); .expect((res) => assert(res.text.indexOf('This is the') !== -1));
}); });
describe('Import/Export tests requiring AbiWord/LibreOffice', function() { describe('Import/Export tests requiring AbiWord/LibreOffice', function () {
before(function() { before(function () {
if ((!settings.abiword || settings.abiword.indexOf('/') === -1) && if ((!settings.abiword || settings.abiword.indexOf('/') === -1) &&
(!settings.soffice || settings.soffice.indexOf('/') === -1)) { (!settings.soffice || settings.soffice.indexOf('/') === -1)) {
this.skip(); this.skip();
@ -131,21 +131,21 @@ describe(__filename, function() {
// For some reason word import does not work in testing.. // For some reason word import does not work in testing..
// TODO: fix support for .doc files.. // TODO: fix support for .doc files..
it('Tries to import .doc that uses soffice or abiword', async function() { it('Tries to import .doc that uses soffice or abiword', async function () {
await agent.post(`/p/${testPadId}/import`) await agent.post(`/p/${testPadId}/import`)
.attach('file', wordDoc, {filename: '/test.doc', contentType: 'application/msword'}) .attach('file', wordDoc, {filename: '/test.doc', contentType: 'application/msword'})
.expect(200) .expect(200)
.expect(/FrameCall\('undefined', 'ok'\);/); .expect(/FrameCall\('undefined', 'ok'\);/);
}); });
it('exports DOC', async function() { it('exports DOC', async function () {
await agent.get(`/p/${testPadId}/export/doc`) await agent.get(`/p/${testPadId}/export/doc`)
.buffer(true).parse(superagent.parse['application/octet-stream']) .buffer(true).parse(superagent.parse['application/octet-stream'])
.expect(200) .expect(200)
.expect((res) => assert(res.body.length >= 9000)); .expect((res) => assert(res.body.length >= 9000));
}); });
it('Tries to import .docx that uses soffice or abiword', async function() { it('Tries to import .docx that uses soffice or abiword', async function () {
await agent.post(`/p/${testPadId}/import`) await agent.post(`/p/${testPadId}/import`)
.attach('file', wordXDoc, { .attach('file', wordXDoc, {
filename: '/test.docx', filename: '/test.docx',
@ -155,44 +155,43 @@ describe(__filename, function() {
.expect(/FrameCall\('undefined', 'ok'\);/); .expect(/FrameCall\('undefined', 'ok'\);/);
}); });
it('exports DOC from imported DOCX', async function() { it('exports DOC from imported DOCX', async function () {
await agent.get(`/p/${testPadId}/export/doc`) await agent.get(`/p/${testPadId}/export/doc`)
.buffer(true).parse(superagent.parse['application/octet-stream']) .buffer(true).parse(superagent.parse['application/octet-stream'])
.expect(200) .expect(200)
.expect((res) => assert(res.body.length >= 9100)); .expect((res) => assert(res.body.length >= 9100));
}); });
it('Tries to import .pdf that uses soffice or abiword', async function() { it('Tries to import .pdf that uses soffice or abiword', async function () {
await agent.post(`/p/${testPadId}/import`) await agent.post(`/p/${testPadId}/import`)
.attach('file', pdfDoc, {filename: '/test.pdf', contentType: 'application/pdf'}) .attach('file', pdfDoc, {filename: '/test.pdf', contentType: 'application/pdf'})
.expect(200) .expect(200)
.expect(/FrameCall\('undefined', 'ok'\);/); .expect(/FrameCall\('undefined', 'ok'\);/);
}); });
it('exports PDF', async function() { it('exports PDF', async function () {
await agent.get(`/p/${testPadId}/export/pdf`) await agent.get(`/p/${testPadId}/export/pdf`)
.buffer(true).parse(superagent.parse['application/octet-stream']) .buffer(true).parse(superagent.parse['application/octet-stream'])
.expect(200) .expect(200)
.expect((res) => assert(res.body.length >= 1000)); .expect((res) => assert(res.body.length >= 1000));
}); });
it('Tries to import .odt that uses soffice or abiword', async function() { it('Tries to import .odt that uses soffice or abiword', async function () {
await agent.post(`/p/${testPadId}/import`) await agent.post(`/p/${testPadId}/import`)
.attach('file', odtDoc, {filename: '/test.odt', contentType: 'application/odt'}) .attach('file', odtDoc, {filename: '/test.odt', contentType: 'application/odt'})
.expect(200) .expect(200)
.expect(/FrameCall\('undefined', 'ok'\);/); .expect(/FrameCall\('undefined', 'ok'\);/);
}); });
it('exports ODT', async function() { it('exports ODT', async function () {
await agent.get(`/p/${testPadId}/export/odt`) await agent.get(`/p/${testPadId}/export/odt`)
.buffer(true).parse(superagent.parse['application/octet-stream']) .buffer(true).parse(superagent.parse['application/octet-stream'])
.expect(200) .expect(200)
.expect((res) => assert(res.body.length >= 7000)); .expect((res) => assert(res.body.length >= 7000));
}); });
}); // End of AbiWord/LibreOffice tests. }); // End of AbiWord/LibreOffice tests.
it('Tries to import .etherpad', async function() { it('Tries to import .etherpad', async function () {
await agent.post(`/p/${testPadId}/import`) await agent.post(`/p/${testPadId}/import`)
.attach('file', etherpadDoc, { .attach('file', etherpadDoc, {
filename: '/test.etherpad', filename: '/test.etherpad',
@ -202,21 +201,21 @@ describe(__filename, function() {
.expect(/FrameCall\('true', 'ok'\);/); .expect(/FrameCall\('true', 'ok'\);/);
}); });
it('exports Etherpad', async function() { it('exports Etherpad', async function () {
await agent.get(`/p/${testPadId}/export/etherpad`) await agent.get(`/p/${testPadId}/export/etherpad`)
.buffer(true).parse(superagent.parse.text) .buffer(true).parse(superagent.parse.text)
.expect(200) .expect(200)
.expect(/hello/); .expect(/hello/);
}); });
it('exports HTML for this Etherpad file', async function() { it('exports HTML for this Etherpad file', async function () {
await agent.get(`/p/${testPadId}/export/html`) await agent.get(`/p/${testPadId}/export/html`)
.expect(200) .expect(200)
.expect('content-type', 'text/html; charset=utf-8') .expect('content-type', 'text/html; charset=utf-8')
.expect(/<ul class="bullet"><li><ul class="bullet"><li>hello<\/ul><\/li><\/ul>/); .expect(/<ul class="bullet"><li><ul class="bullet"><li>hello<\/ul><\/li><\/ul>/);
}); });
it('Tries to import unsupported file type', async function() { it('Tries to import unsupported file type', async function () {
settings.allowUnknownFileEnds = false; settings.allowUnknownFileEnds = false;
await agent.post(`/p/${testPadId}/import`) await agent.post(`/p/${testPadId}/import`)
.attach('file', padText, {filename: '/test.xasdasdxx', contentType: 'weirdness/jobby'}) .attach('file', padText, {filename: '/test.xasdasdxx', contentType: 'weirdness/jobby'})
@ -224,7 +223,7 @@ describe(__filename, function() {
.expect((res) => assert.doesNotMatch(res.text, /FrameCall\('undefined', 'ok'\);/)); .expect((res) => assert.doesNotMatch(res.text, /FrameCall\('undefined', 'ok'\);/));
}); });
describe('Import authorization checks', function() { describe('Import authorization checks', function () {
let authorize; let authorize;
const deleteTestPad = async () => { const deleteTestPad = async () => {
@ -240,18 +239,18 @@ describe(__filename, function() {
return pad; return pad;
}; };
beforeEach(async function() { beforeEach(async function () {
await deleteTestPad(); await deleteTestPad();
settings.requireAuthorization = true; settings.requireAuthorization = true;
authorize = () => true; authorize = () => true;
plugins.hooks.authorize = [{hook_fn: (hookName, {req}, cb) => cb([authorize(req)])}]; plugins.hooks.authorize = [{hook_fn: (hookName, {req}, cb) => cb([authorize(req)])}];
}); });
afterEach(async function() { afterEach(async function () {
await deleteTestPad(); await deleteTestPad();
}); });
it('!authn !exist -> create', async function() { it('!authn !exist -> create', async function () {
await agent.post(`/p/${testPadIdEnc}/import`) await agent.post(`/p/${testPadIdEnc}/import`)
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
.expect(200); .expect(200);
@ -260,7 +259,7 @@ describe(__filename, function() {
assert.equal(pad.text(), padText.toString()); assert.equal(pad.text(), padText.toString());
}); });
it('!authn exist -> replace', async function() { it('!authn exist -> replace', async function () {
const pad = await createTestPad('before import'); const pad = await createTestPad('before import');
await agent.post(`/p/${testPadIdEnc}/import`) await agent.post(`/p/${testPadIdEnc}/import`)
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
@ -269,7 +268,7 @@ describe(__filename, function() {
assert.equal(pad.text(), padText.toString()); assert.equal(pad.text(), padText.toString());
}); });
it('authn anonymous !exist -> fail', async function() { it('authn anonymous !exist -> fail', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
await agent.post(`/p/${testPadIdEnc}/import`) await agent.post(`/p/${testPadIdEnc}/import`)
.attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'}) .attach('file', padText, {filename: '/test.txt', contentType: 'text/plain'})
@ -277,7 +276,7 @@ describe(__filename, function() {
assert(!(await padManager.doesPadExist(testPadId))); assert(!(await padManager.doesPadExist(testPadId)));
}); });
it('authn anonymous exist -> fail', async function() { it('authn anonymous exist -> fail', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
const pad = await createTestPad('before import\n'); const pad = await createTestPad('before import\n');
await agent.post(`/p/${testPadIdEnc}/import`) await agent.post(`/p/${testPadIdEnc}/import`)
@ -286,7 +285,7 @@ describe(__filename, function() {
assert.equal(pad.text(), 'before import\n'); assert.equal(pad.text(), 'before import\n');
}); });
it('authn user create !exist -> create', async function() { it('authn user create !exist -> create', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
await agent.post(`/p/${testPadIdEnc}/import`) await agent.post(`/p/${testPadIdEnc}/import`)
.auth('user', 'user-password') .auth('user', 'user-password')
@ -297,7 +296,7 @@ describe(__filename, function() {
assert.equal(pad.text(), padText.toString()); assert.equal(pad.text(), padText.toString());
}); });
it('authn user modify !exist -> fail', async function() { it('authn user modify !exist -> fail', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
authorize = () => 'modify'; authorize = () => 'modify';
await agent.post(`/p/${testPadIdEnc}/import`) await agent.post(`/p/${testPadIdEnc}/import`)
@ -307,7 +306,7 @@ describe(__filename, function() {
assert(!(await padManager.doesPadExist(testPadId))); assert(!(await padManager.doesPadExist(testPadId)));
}); });
it('authn user readonly !exist -> fail', async function() { it('authn user readonly !exist -> fail', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
authorize = () => 'readOnly'; authorize = () => 'readOnly';
await agent.post(`/p/${testPadIdEnc}/import`) await agent.post(`/p/${testPadIdEnc}/import`)
@ -317,7 +316,7 @@ describe(__filename, function() {
assert(!(await padManager.doesPadExist(testPadId))); assert(!(await padManager.doesPadExist(testPadId)));
}); });
it('authn user create exist -> replace', async function() { it('authn user create exist -> replace', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
const pad = await createTestPad('before import\n'); const pad = await createTestPad('before import\n');
await agent.post(`/p/${testPadIdEnc}/import`) await agent.post(`/p/${testPadIdEnc}/import`)
@ -327,7 +326,7 @@ describe(__filename, function() {
assert.equal(pad.text(), padText.toString()); assert.equal(pad.text(), padText.toString());
}); });
it('authn user modify exist -> replace', async function() { it('authn user modify exist -> replace', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
authorize = () => 'modify'; authorize = () => 'modify';
const pad = await createTestPad('before import\n'); const pad = await createTestPad('before import\n');
@ -338,7 +337,7 @@ describe(__filename, function() {
assert.equal(pad.text(), padText.toString()); assert.equal(pad.text(), padText.toString());
}); });
it('authn user readonly exist -> fail', async function() { it('authn user readonly exist -> fail', async function () {
const pad = await createTestPad('before import\n'); const pad = await createTestPad('before import\n');
settings.requireAuthentication = true; settings.requireAuthentication = true;
authorize = () => 'readOnly'; authorize = () => 'readOnly';
@ -353,19 +352,16 @@ describe(__filename, function() {
}); // End of tests. }); // End of tests.
var endPoint = function (point, version) {
var endPoint = function(point, version){
version = version || apiVersion; version = version || apiVersion;
return `/api/${version}/${point}?apikey=${apiKey}`; return `/api/${version}/${point}?apikey=${apiKey}`;
}; };
function makeid() { function makeid() {
var text = ""; let text = '';
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for( var i=0; i < 5; i++ ){ for (let i = 0; i < 5; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length)); text += possible.charAt(Math.floor(Math.random() * possible.length));
} }
return text; return text;

View file

@ -4,27 +4,27 @@
* Section "GLOBAL FUNCTIONS" in src/node/db/API.js * Section "GLOBAL FUNCTIONS" in src/node/db/API.js
*/ */
const common = require('../../common'); const common = require('../../common');
const supertest = require(__dirname+'/../../../../src/node_modules/supertest'); const supertest = require(`${__dirname}/../../../../src/node_modules/supertest`);
const settings = require(__dirname+'/../../../../src/node/utils/Settings'); const settings = require(`${__dirname}/../../../../src/node/utils/Settings`);
const api = supertest('http://'+settings.ip+":"+settings.port); const api = supertest(`http://${settings.ip}:${settings.port}`);
const apiKey = common.apiKey; const apiKey = common.apiKey;
var apiVersion = '1.2.14'; const apiVersion = '1.2.14';
describe(__filename, function() { describe(__filename, function () {
describe('Connectivity for instance-level API tests', function() { describe('Connectivity for instance-level API tests', function () {
it('can connect', function(done) { it('can connect', function (done) {
api.get('/api/') api.get('/api/')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200, done) .expect(200, done);
}); });
}); });
describe('getStats', function() { describe('getStats', function () {
it('Gets the stats of a running instance', function(done) { it('Gets the stats of a running instance', function (done) {
api.get(endPoint('getStats')) api.get(endPoint('getStats'))
.expect(function(res) { .expect((res) => {
if (res.body.code !== 0) throw new Error("getStats() failed"); if (res.body.code !== 0) throw new Error('getStats() failed');
if (!(('totalPads' in res.body.data) && (typeof res.body.data.totalPads === 'number'))) { if (!(('totalPads' in res.body.data) && (typeof res.body.data.totalPads === 'number'))) {
throw new Error(`Response to getStats() does not contain field totalPads, or it's not a number: ${JSON.stringify(res.body.data)}`); throw new Error(`Response to getStats() does not contain field totalPads, or it's not a number: ${JSON.stringify(res.body.data)}`);
@ -44,7 +44,7 @@ describe(__filename, function() {
}); });
}); });
var endPoint = function(point, version){ var endPoint = function (point, version) {
version = version || apiVersion; version = version || apiVersion;
return '/api/'+version+'/'+point+'?apikey='+apiKey; return `/api/${version}/${point}?apikey=${apiKey}`;
} };

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
const assert = require('assert').strict; const assert = require('assert').strict;
const common = require('../../common'); const common = require('../../common');
const supertest = require(__dirname + '/../../../../src/node_modules/supertest'); const supertest = require(`${__dirname}/../../../../src/node_modules/supertest`);
const settings = require(__dirname + '/../../../../src/node/utils/Settings'); const settings = require(`${__dirname}/../../../../src/node/utils/Settings`);
const api = supertest(`http://${settings.ip}:${settings.port}`); const api = supertest(`http://${settings.ip}:${settings.port}`);
const apiKey = common.apiKey; const apiKey = common.apiKey;
@ -11,9 +11,9 @@ let authorID = '';
let sessionID = ''; let sessionID = '';
let padID = makeid(); let padID = makeid();
describe(__filename, function() { describe(__filename, function () {
describe('API Versioning', function() { describe('API Versioning', function () {
it('errors if can not connect', async function() { it('errors if can not connect', async function () {
await api.get('/api/') await api.get('/api/')
.expect(200) .expect(200)
.expect((res) => { .expect((res) => {
@ -24,8 +24,8 @@ describe(__filename, function() {
}); });
// BEGIN GROUP AND AUTHOR TESTS // BEGIN GROUP AND AUTHOR TESTS
///////////////////////////////////// // ///////////////////////////////////
///////////////////////////////////// // ///////////////////////////////////
/* Tests performed /* Tests performed
-> createGroup() -- should return a groupID -> createGroup() -- should return a groupID
@ -53,8 +53,8 @@ describe(__filename, function() {
-> listPadsOfAuthor(authorID) -> listPadsOfAuthor(authorID)
*/ */
describe('API: Group creation and deletion', function() { describe('API: Group creation and deletion', function () {
it('createGroup', async function() { it('createGroup', async function () {
await api.get(endPoint('createGroup')) await api.get(endPoint('createGroup'))
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
@ -65,8 +65,8 @@ describe(__filename, function() {
}); });
}); });
it('listSessionsOfGroup for empty group', async function() { it('listSessionsOfGroup for empty group', async function () {
await api.get(endPoint('listSessionsOfGroup') + `&groupID=${groupID}`) await api.get(`${endPoint('listSessionsOfGroup')}&groupID=${groupID}`)
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect((res) => { .expect((res) => {
@ -75,8 +75,8 @@ describe(__filename, function() {
}); });
}); });
it('deleteGroup', async function() { it('deleteGroup', async function () {
await api.get(endPoint('deleteGroup') + `&groupID=${groupID}`) await api.get(`${endPoint('deleteGroup')}&groupID=${groupID}`)
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect((res) => { .expect((res) => {
@ -84,8 +84,8 @@ describe(__filename, function() {
}); });
}); });
it('createGroupIfNotExistsFor', async function() { it('createGroupIfNotExistsFor', async function () {
await api.get(endPoint('createGroupIfNotExistsFor') + '&groupMapper=management') await api.get(`${endPoint('createGroupIfNotExistsFor')}&groupMapper=management`)
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect((res) => { .expect((res) => {
@ -95,8 +95,8 @@ describe(__filename, function() {
}); });
}); });
describe('API: Author creation', function() { describe('API: Author creation', function () {
it('createGroup', async function() { it('createGroup', async function () {
await api.get(endPoint('createGroup')) await api.get(endPoint('createGroup'))
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
@ -107,7 +107,7 @@ describe(__filename, function() {
}); });
}); });
it('createAuthor', async function() { it('createAuthor', async function () {
await api.get(endPoint('createAuthor')) await api.get(endPoint('createAuthor'))
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
@ -117,8 +117,8 @@ describe(__filename, function() {
}); });
}); });
it('createAuthor with name', async function() { it('createAuthor with name', async function () {
await api.get(endPoint('createAuthor') + '&name=john') await api.get(`${endPoint('createAuthor')}&name=john`)
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect((res) => { .expect((res) => {
@ -128,8 +128,8 @@ describe(__filename, function() {
}); });
}); });
it('createAuthorIfNotExistsFor', async function() { it('createAuthorIfNotExistsFor', async function () {
await api.get(endPoint('createAuthorIfNotExistsFor') + '&authorMapper=chris') await api.get(`${endPoint('createAuthorIfNotExistsFor')}&authorMapper=chris`)
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect((res) => { .expect((res) => {
@ -138,8 +138,8 @@ describe(__filename, function() {
}); });
}); });
it('getAuthorName', async function() { it('getAuthorName', async function () {
await api.get(endPoint('getAuthorName') + `&authorID=${authorID}`) await api.get(`${endPoint('getAuthorName')}&authorID=${authorID}`)
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect((res) => { .expect((res) => {
@ -149,10 +149,10 @@ describe(__filename, function() {
}); });
}); });
describe('API: Sessions', function() { describe('API: Sessions', function () {
it('createSession', async function() { it('createSession', async function () {
await api.get(endPoint('createSession') + await api.get(`${endPoint('createSession')
`&authorID=${authorID}&groupID=${groupID}&validUntil=999999999999`) }&authorID=${authorID}&groupID=${groupID}&validUntil=999999999999`)
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect((res) => { .expect((res) => {
@ -162,8 +162,8 @@ describe(__filename, function() {
}); });
}); });
it('getSessionInfo', async function() { it('getSessionInfo', async function () {
await api.get(endPoint('getSessionInfo') + `&sessionID=${sessionID}`) await api.get(`${endPoint('getSessionInfo')}&sessionID=${sessionID}`)
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect((res) => { .expect((res) => {
@ -174,8 +174,8 @@ describe(__filename, function() {
}); });
}); });
it('listSessionsOfGroup', async function() { it('listSessionsOfGroup', async function () {
await api.get(endPoint('listSessionsOfGroup') + `&groupID=${groupID}`) await api.get(`${endPoint('listSessionsOfGroup')}&groupID=${groupID}`)
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect((res) => { .expect((res) => {
@ -184,8 +184,8 @@ describe(__filename, function() {
}); });
}); });
it('deleteSession', async function() { it('deleteSession', async function () {
await api.get(endPoint('deleteSession') + `&sessionID=${sessionID}`) await api.get(`${endPoint('deleteSession')}&sessionID=${sessionID}`)
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect((res) => { .expect((res) => {
@ -193,8 +193,8 @@ describe(__filename, function() {
}); });
}); });
it('getSessionInfo of deleted session', async function() { it('getSessionInfo of deleted session', async function () {
await api.get(endPoint('getSessionInfo') + `&sessionID=${sessionID}`) await api.get(`${endPoint('getSessionInfo')}&sessionID=${sessionID}`)
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect((res) => { .expect((res) => {
@ -203,9 +203,9 @@ describe(__filename, function() {
}); });
}); });
describe('API: Group pad management', function() { describe('API: Group pad management', function () {
it('listPads', async function() { it('listPads', async function () {
await api.get(endPoint('listPads') + `&groupID=${groupID}`) await api.get(`${endPoint('listPads')}&groupID=${groupID}`)
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect((res) => { .expect((res) => {
@ -214,8 +214,8 @@ describe(__filename, function() {
}); });
}); });
it('createGroupPad', async function() { it('createGroupPad', async function () {
await api.get(endPoint('createGroupPad') + `&groupID=${groupID}&padName=${padID}`) await api.get(`${endPoint('createGroupPad')}&groupID=${groupID}&padName=${padID}`)
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect((res) => { .expect((res) => {
@ -224,8 +224,8 @@ describe(__filename, function() {
}); });
}); });
it('listPads after creating a group pad', async function() { it('listPads after creating a group pad', async function () {
await api.get(endPoint('listPads') + `&groupID=${groupID}`) await api.get(`${endPoint('listPads')}&groupID=${groupID}`)
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect((res) => { .expect((res) => {
@ -235,9 +235,9 @@ describe(__filename, function() {
}); });
}); });
describe('API: Pad security', function() { describe('API: Pad security', function () {
it('getPublicStatus', async function() { it('getPublicStatus', async function () {
await api.get(endPoint('getPublicStatus') + `&padID=${padID}`) await api.get(`${endPoint('getPublicStatus')}&padID=${padID}`)
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect((res) => { .expect((res) => {
@ -246,8 +246,8 @@ describe(__filename, function() {
}); });
}); });
it('setPublicStatus', async function() { it('setPublicStatus', async function () {
await api.get(endPoint('setPublicStatus') + `&padID=${padID}&publicStatus=true`) await api.get(`${endPoint('setPublicStatus')}&padID=${padID}&publicStatus=true`)
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect((res) => { .expect((res) => {
@ -255,8 +255,8 @@ describe(__filename, function() {
}); });
}); });
it('getPublicStatus after changing public status', async function() { it('getPublicStatus after changing public status', async function () {
await api.get(endPoint('getPublicStatus') + `&padID=${padID}`) await api.get(`${endPoint('getPublicStatus')}&padID=${padID}`)
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect((res) => { .expect((res) => {
@ -267,12 +267,12 @@ describe(__filename, function() {
}); });
// NOT SURE HOW TO POPULAT THIS /-_-\ // NOT SURE HOW TO POPULAT THIS /-_-\
/////////////////////////////////////// // /////////////////////////////////////
/////////////////////////////////////// // /////////////////////////////////////
describe('API: Misc', function() { describe('API: Misc', function () {
it('listPadsOfAuthor', async function() { it('listPadsOfAuthor', async function () {
await api.get(endPoint('listPadsOfAuthor') + `&authorID=${authorID}`) await api.get(`${endPoint('listPadsOfAuthor')}&authorID=${authorID}`)
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect((res) => { .expect((res) => {
@ -284,8 +284,7 @@ describe(__filename, function() {
}); });
const endPoint = function (point) {
const endPoint = function(point) {
return `/api/${apiVersion}/${point}?apikey=${apiKey}`; return `/api/${apiVersion}/${point}?apikey=${apiKey}`;
}; };

View file

@ -1,21 +1,21 @@
var assert = require('assert') const assert = require('assert');
os = require('os'), os = require('os'),
fs = require('fs'), fs = require('fs'),
path = require('path'), path = require('path'),
TidyHtml = null, TidyHtml = null,
Settings = null; Settings = null;
var npm = require("../../../../src/node_modules/npm/lib/npm.js"); const npm = require('../../../../src/node_modules/npm/lib/npm.js');
var nodeify = require('../../../../src/node_modules/nodeify'); const nodeify = require('../../../../src/node_modules/nodeify');
describe(__filename, function() { describe(__filename, function () {
describe('tidyHtml', function() { describe('tidyHtml', function () {
before(function(done) { before(function (done) {
npm.load({}, function(err) { npm.load({}, (err) => {
assert.ok(!err); assert.ok(!err);
TidyHtml = require('../../../../src/node/utils/TidyHtml'); TidyHtml = require('../../../../src/node/utils/TidyHtml');
Settings = require('../../../../src/node/utils/Settings'); Settings = require('../../../../src/node/utils/Settings');
return done() return done();
}); });
}); });
@ -23,7 +23,7 @@ describe(__filename, function() {
return nodeify(TidyHtml.tidy(file), callback); return nodeify(TidyHtml.tidy(file), callback);
} }
it('Tidies HTML', function(done) { it('Tidies HTML', function (done) {
// If the user hasn't configured Tidy, we skip this tests as it's required for this test // If the user hasn't configured Tidy, we skip this tests as it's required for this test
if (!Settings.tidyHtml) { if (!Settings.tidyHtml) {
this.skip(); this.skip();
@ -32,15 +32,15 @@ describe(__filename, function() {
// Try to tidy up a bad HTML file // Try to tidy up a bad HTML file
const tmpDir = os.tmpdir(); const tmpDir = os.tmpdir();
var tmpFile = path.join(tmpDir, 'tmp_' + (Math.floor(Math.random() * 1000000)) + '.html') const tmpFile = path.join(tmpDir, `tmp_${Math.floor(Math.random() * 1000000)}.html`);
fs.writeFileSync(tmpFile, '<html><body><p>a paragraph</p><li>List without outer UL</li>trailing closing p</p></body></html>'); fs.writeFileSync(tmpFile, '<html><body><p>a paragraph</p><li>List without outer UL</li>trailing closing p</p></body></html>');
tidy(tmpFile, function(err){ tidy(tmpFile, (err) => {
assert.ok(!err); assert.ok(!err);
// Read the file again // Read the file again
var cleanedHtml = fs.readFileSync(tmpFile).toString(); const cleanedHtml = fs.readFileSync(tmpFile).toString();
var expectedHtml = [ const expectedHtml = [
'<title></title>', '<title></title>',
'</head>', '</head>',
'<body>', '<body>',
@ -57,13 +57,13 @@ describe(__filename, function() {
}); });
}); });
it('can deal with errors', function(done) { it('can deal with errors', function (done) {
// If the user hasn't configured Tidy, we skip this tests as it's required for this test // If the user hasn't configured Tidy, we skip this tests as it's required for this test
if (!Settings.tidyHtml) { if (!Settings.tidyHtml) {
this.skip(); this.skip();
} }
tidy('/some/none/existing/file.html', function(err) { tidy('/some/none/existing/file.html', (err) => {
assert.ok(err); assert.ok(err);
return done(); return done();
}); });

View file

@ -21,11 +21,11 @@ let agent;
* @param {URI} resource resource URI * @param {URI} resource resource URI
* @returns {boolean} if it is plaintext * @returns {boolean} if it is plaintext
*/ */
function isPlaintextResponse(fileContent, resource){ function isPlaintextResponse(fileContent, resource) {
// callback=require.define&v=1234 // callback=require.define&v=1234
const query = url.parse(resource)['query']; const query = url.parse(resource).query;
// require.define // require.define
const jsonp = queryString.parse(query)['callback']; const jsonp = queryString.parse(query).callback;
// returns true if the first letters in fileContent equal the content of `jsonp` // returns true if the first letters in fileContent equal the content of `jsonp`
return fileContent.substring(0, jsonp.length) === jsonp; return fileContent.substring(0, jsonp.length) === jsonp;
@ -36,40 +36,39 @@ function isPlaintextResponse(fileContent, resource){
* *
* @param {Request} request * @param {Request} request
*/ */
function disableAutoDeflate(request){ function disableAutoDeflate(request) {
request._shouldUnzip = function(){ request._shouldUnzip = function () {
return false return false;
} };
} }
describe(__filename, function() { describe(__filename, function () {
const backups = {}; const backups = {};
const fantasyEncoding = 'brainwaves'; // non-working encoding until https://github.com/visionmedia/superagent/pull/1560 is resolved const fantasyEncoding = 'brainwaves'; // non-working encoding until https://github.com/visionmedia/superagent/pull/1560 is resolved
const packages = [ const packages = [
"/javascripts/lib/ep_etherpad-lite/static/js/ace2_common.js?callback=require.define" '/javascripts/lib/ep_etherpad-lite/static/js/ace2_common.js?callback=require.define',
, "/javascripts/lib/ep_etherpad-lite/static/js/ace2_inner.js?callback=require.define" '/javascripts/lib/ep_etherpad-lite/static/js/ace2_inner.js?callback=require.define',
, "/javascripts/lib/ep_etherpad-lite/static/js/pad.js?callback=require.define" '/javascripts/lib/ep_etherpad-lite/static/js/pad.js?callback=require.define',
, "/javascripts/lib/ep_etherpad-lite/static/js/timeslider.js?callback=require.define" '/javascripts/lib/ep_etherpad-lite/static/js/timeslider.js?callback=require.define',
]; ];
before(async function() { before(async function () {
agent = await common.init(); agent = await common.init();
}); });
beforeEach(async function() { beforeEach(async function () {
backups.settings = {}; backups.settings = {};
backups.settings['minify'] = settings.minify; backups.settings.minify = settings.minify;
}); });
afterEach(async function() { afterEach(async function () {
Object.assign(settings, backups.settings); Object.assign(settings, backups.settings);
}); });
context('when minify is false', function(){ context('when minify is false', function () {
before(async function() { before(async function () {
settings.minify = false; settings.minify = false;
}); });
it('gets packages uncompressed without Accept-Encoding gzip', async function() { it('gets packages uncompressed without Accept-Encoding gzip', async function () {
await Promise.all(packages.map(async (resource) => { await Promise.all(packages.map(async (resource) => agent.get(resource)
return agent.get(resource)
.set('Accept-Encoding', fantasyEncoding) .set('Accept-Encoding', fantasyEncoding)
.use(disableAutoDeflate) .use(disableAutoDeflate)
.then((res) => { .then((res) => {
@ -77,13 +76,11 @@ describe(__filename, function() {
assert.equal(res.header['content-encoding'], undefined); assert.equal(res.header['content-encoding'], undefined);
assert.equal(isPlaintextResponse(res.text, resource), true); assert.equal(isPlaintextResponse(res.text, resource), true);
return; return;
}) })));
})) });
})
it('gets packages compressed with Accept-Encoding gzip', async function() { it('gets packages compressed with Accept-Encoding gzip', async function () {
await Promise.all(packages.map(async (resource) => { await Promise.all(packages.map(async (resource) => agent.get(resource)
return agent.get(resource)
.set('Accept-Encoding', 'gzip') .set('Accept-Encoding', 'gzip')
.use(disableAutoDeflate) .use(disableAutoDeflate)
.then((res) => { .then((res) => {
@ -91,36 +88,28 @@ describe(__filename, function() {
assert.equal(res.header['content-encoding'], 'gzip'); assert.equal(res.header['content-encoding'], 'gzip');
assert.equal(isPlaintextResponse(res.text, resource), false); assert.equal(isPlaintextResponse(res.text, resource), false);
return; return;
}) })));
})) });
})
it('does not cache content-encoding headers', async function(){ it('does not cache content-encoding headers', async function () {
await agent.get(packages[0]) await agent.get(packages[0])
.set('Accept-Encoding', fantasyEncoding) .set('Accept-Encoding', fantasyEncoding)
.then((res) => { .then((res) => assert.equal(res.header['content-encoding'], undefined));
return assert.equal(res.header['content-encoding'], undefined);
});
await agent.get(packages[0]) await agent.get(packages[0])
.set('Accept-Encoding', 'gzip') .set('Accept-Encoding', 'gzip')
.then((res) => { .then((res) => assert.equal(res.header['content-encoding'], 'gzip'));
return assert.equal(res.header['content-encoding'], 'gzip');
});
await agent.get(packages[0]) await agent.get(packages[0])
.set('Accept-Encoding', fantasyEncoding) .set('Accept-Encoding', fantasyEncoding)
.then((res) => { .then((res) => assert.equal(res.header['content-encoding'], undefined));
return assert.equal(res.header['content-encoding'], undefined);
}); });
})
}); });
context('when minify is true', function(){ context('when minify is true', function () {
before(async function() { before(async function () {
settings.minify = true; settings.minify = true;
}); });
it('gets packages uncompressed without Accept-Encoding gzip', async function() { it('gets packages uncompressed without Accept-Encoding gzip', async function () {
await Promise.all(packages.map(async (resource) => { await Promise.all(packages.map(async (resource) => agent.get(resource)
return agent.get(resource)
.set('Accept-Encoding', fantasyEncoding) .set('Accept-Encoding', fantasyEncoding)
.use(disableAutoDeflate) .use(disableAutoDeflate)
.then((res) => { .then((res) => {
@ -128,13 +117,11 @@ describe(__filename, function() {
assert.equal(res.header['content-encoding'], undefined); assert.equal(res.header['content-encoding'], undefined);
assert.equal(isPlaintextResponse(res.text, resource), true); assert.equal(isPlaintextResponse(res.text, resource), true);
return; return;
}) })));
})) });
})
it('gets packages compressed with Accept-Encoding gzip', async function() { it('gets packages compressed with Accept-Encoding gzip', async function () {
await Promise.all(packages.map(async (resource) => { await Promise.all(packages.map(async (resource) => agent.get(resource)
return agent.get(resource)
.set('Accept-Encoding', 'gzip') .set('Accept-Encoding', 'gzip')
.use(disableAutoDeflate) .use(disableAutoDeflate)
.then((res) => { .then((res) => {
@ -142,27 +129,19 @@ describe(__filename, function() {
assert.equal(res.header['content-encoding'], 'gzip'); assert.equal(res.header['content-encoding'], 'gzip');
assert.equal(isPlaintextResponse(res.text, resource), false); assert.equal(isPlaintextResponse(res.text, resource), false);
return; return;
}) })));
})) });
})
it('does not cache content-encoding headers', async function(){ it('does not cache content-encoding headers', async function () {
await agent.get(packages[0]) await agent.get(packages[0])
.set('Accept-Encoding', fantasyEncoding) .set('Accept-Encoding', fantasyEncoding)
.then((res) => { .then((res) => assert.equal(res.header['content-encoding'], undefined));
return assert.equal(res.header['content-encoding'], undefined);
});
await agent.get(packages[0]) await agent.get(packages[0])
.set('Accept-Encoding', 'gzip') .set('Accept-Encoding', 'gzip')
.then((res) => { .then((res) => assert.equal(res.header['content-encoding'], 'gzip'));
return assert.equal(res.header['content-encoding'], 'gzip');
});
await agent.get(packages[0]) await agent.get(packages[0])
.set('Accept-Encoding', fantasyEncoding) .set('Accept-Encoding', fantasyEncoding)
.then((res) => { .then((res) => assert.equal(res.header['content-encoding'], undefined));
return assert.equal(res.header['content-encoding'], undefined);
}); });
})
}); });
}); });

View file

@ -1,24 +1,24 @@
const Changeset = require("../../../src/static/js/Changeset"); const Changeset = require('../../../src/static/js/Changeset');
const contentcollector = require("../../../src/static/js/contentcollector"); const contentcollector = require('../../../src/static/js/contentcollector');
const AttributePool = require("../../../src/static/js/AttributePool"); const AttributePool = require('../../../src/static/js/AttributePool');
const cheerio = require("../../../src/node_modules/cheerio"); const cheerio = require('../../../src/node_modules/cheerio');
const util = require('util'); const util = require('util');
const tests = { const tests = {
nestedLi:{ nestedLi: {
description: "Complex nested Li", description: 'Complex nested Li',
html: '<!doctype html><html><body><ol><li>one</li><li><ol><li>1.1</li></ol></li><li>two</li></ol></body></html>', html: '<!doctype html><html><body><ol><li>one</li><li><ol><li>1.1</li></ol></li><li>two</li></ol></body></html>',
expectedLineAttribs : [ expectedLineAttribs: [
'*0*1*2*3+1+3', '*0*4*2*5+1+3', '*0*1*2*5+1+3' '*0*1*2*3+1+3', '*0*4*2*5+1+3', '*0*1*2*5+1+3',
], ],
expectedText: [ expectedText: [
'*one', '*1.1', '*two' '*one', '*1.1', '*two',
] ],
}, },
complexNest:{ complexNest: {
description: "Complex list of different types", description: 'Complex list of different types',
html: '<!doctype html><html><body><ul class="bullet"><li>one</li><li>two</li><li>0</li><li>1</li><li>2<ul class="bullet"><li>3</li><li>4</li></ul></li></ul><ol class="number"><li>item<ol class="number"><li>item1</li><li>item2</li></ol></li></ol></body></html>', html: '<!doctype html><html><body><ul class="bullet"><li>one</li><li>two</li><li>0</li><li>1</li><li>2<ul class="bullet"><li>3</li><li>4</li></ul></li></ul><ol class="number"><li>item<ol class="number"><li>item1</li><li>item2</li></ol></li></ol></body></html>',
expectedLineAttribs : [ expectedLineAttribs: [
'*0*1*2+1+3', '*0*1*2+1+3',
'*0*1*2+1+3', '*0*1*2+1+3',
'*0*1*2+1+1', '*0*1*2+1+1',
@ -28,113 +28,116 @@ const tests = {
'*0*3*2+1+1', '*0*3*2+1+1',
'*0*4*2*5+1+4', '*0*4*2*5+1+4',
'*0*6*2*7+1+5', '*0*6*2*7+1+5',
'*0*6*2*7+1+5' '*0*6*2*7+1+5',
], ],
expectedText: [ expectedText: [
'*one', '*two', '*one',
'*0', '*1', '*two',
'*2', '*3', '*0',
'*4', '*item', '*1',
'*item1', '*item2' '*2',
] '*3',
'*4',
'*item',
'*item1',
'*item2',
],
}, },
ul: { ul: {
description : "Tests if uls properly get attributes", description: 'Tests if uls properly get attributes',
html : "<html><body><ul><li>a</li><li>b</li></ul><div>div</div><p>foo</p></body></html>", html: '<html><body><ul><li>a</li><li>b</li></ul><div>div</div><p>foo</p></body></html>',
expectedLineAttribs : [ '*0*1*2+1+1', '*0*1*2+1+1', '+3' , '+3'], expectedLineAttribs: ['*0*1*2+1+1', '*0*1*2+1+1', '+3', '+3'],
expectedText: ["*a","*b", "div", "foo"] expectedText: ['*a', '*b', 'div', 'foo'],
} },
,
ulIndented: { ulIndented: {
description : "Tests if indented uls properly get attributes", description: 'Tests if indented uls properly get attributes',
html : "<html><body><ul><li>a</li><ul><li>b</li></ul><li>a</li></ul><p>foo</p></body></html>", html: '<html><body><ul><li>a</li><ul><li>b</li></ul><li>a</li></ul><p>foo</p></body></html>',
expectedLineAttribs : [ '*0*1*2+1+1', '*0*3*2+1+1', '*0*1*2+1+1', '+3' ], expectedLineAttribs: ['*0*1*2+1+1', '*0*3*2+1+1', '*0*1*2+1+1', '+3'],
expectedText: ["*a","*b", "*a", "foo"] expectedText: ['*a', '*b', '*a', 'foo'],
}, },
ol: { ol: {
description : "Tests if ols properly get line numbers when in a normal OL", description: 'Tests if ols properly get line numbers when in a normal OL',
html : "<html><body><ol><li>a</li><li>b</li><li>c</li></ol><p>test</p></body></html>", html: '<html><body><ol><li>a</li><li>b</li><li>c</li></ol><p>test</p></body></html>',
expectedLineAttribs : ['*0*1*2*3+1+1', '*0*1*2*3+1+1', '*0*1*2*3+1+1', '+4'], expectedLineAttribs: ['*0*1*2*3+1+1', '*0*1*2*3+1+1', '*0*1*2*3+1+1', '+4'],
expectedText: ["*a","*b","*c", "test"], expectedText: ['*a', '*b', '*c', 'test'],
noteToSelf: "Ensure empty P does not induce line attribute marker, wont this break the editor?" noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?',
}
,
lineDoBreakInOl:{
description : "A single completely empty line break within an ol should reset count if OL is closed off..",
html : "<html><body><ol><li>should be 1</li></ol><p>hello</p><ol><li>should be 1</li><li>should be 2</li></ol><p></p></body></html>",
expectedLineAttribs : [ '*0*1*2*3+1+b', '+5', '*0*1*2*4+1+b', '*0*1*2*4+1+b', '' ],
expectedText: ["*should be 1","hello", "*should be 1","*should be 2", ""],
noteToSelf: "Shouldn't include attribute marker in the <p> line"
}, },
bulletListInOL:{ lineDoBreakInOl: {
description : "A bullet within an OL should not change numbering..", description: 'A single completely empty line break within an ol should reset count if OL is closed off..',
html : "<html><body><ol><li>should be 1</li><ul><li>should be a bullet</li></ul><li>should be 2</li></ol><p></p></body></html>", html: '<html><body><ol><li>should be 1</li></ol><p>hello</p><ol><li>should be 1</li><li>should be 2</li></ol><p></p></body></html>',
expectedLineAttribs : [ '*0*1*2*3+1+b', '*0*4*2*3+1+i', '*0*1*2*5+1+b', '' ], expectedLineAttribs: ['*0*1*2*3+1+b', '+5', '*0*1*2*4+1+b', '*0*1*2*4+1+b', ''],
expectedText: ["*should be 1","*should be a bullet","*should be 2", ""] expectedText: ['*should be 1', 'hello', '*should be 1', '*should be 2', ''],
noteToSelf: "Shouldn't include attribute marker in the <p> line",
}, },
testP:{ bulletListInOL: {
description : "A single <p></p> should create a new line", description: 'A bullet within an OL should not change numbering..',
html : "<html><body><p></p><p></p></body></html>", html: '<html><body><ol><li>should be 1</li><ul><li>should be a bullet</li></ul><li>should be 2</li></ol><p></p></body></html>',
expectedLineAttribs : [ '', ''], expectedLineAttribs: ['*0*1*2*3+1+b', '*0*4*2*3+1+i', '*0*1*2*5+1+b', ''],
expectedText: ["", ""], expectedText: ['*should be 1', '*should be a bullet', '*should be 2', ''],
noteToSelf: "<p></p>should create a line break but not break numbering" },
testP: {
description: 'A single <p></p> should create a new line',
html: '<html><body><p></p><p></p></body></html>',
expectedLineAttribs: ['', ''],
expectedText: ['', ''],
noteToSelf: '<p></p>should create a line break but not break numbering',
}, },
nestedOl: { nestedOl: {
description : "Tests if ols properly get line numbers when in a normal OL", description: 'Tests if ols properly get line numbers when in a normal OL',
html : "<html><body>a<ol><li>b<ol><li>c</li></ol></ol>notlist<p>foo</p></body></html>", html: '<html><body>a<ol><li>b<ol><li>c</li></ol></ol>notlist<p>foo</p></body></html>',
expectedLineAttribs : [ '+1', '*0*1*2*3+1+1', '*0*4*2*5+1+1', '+7', '+3' ], expectedLineAttribs: ['+1', '*0*1*2*3+1+1', '*0*4*2*5+1+1', '+7', '+3'],
expectedText: ["a","*b","*c", "notlist", "foo"], expectedText: ['a', '*b', '*c', 'notlist', 'foo'],
noteToSelf: "Ensure empty P does not induce line attribute marker, wont this break the editor?" noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?',
}, },
nestedOl: { nestedOl: {
description : "First item being an UL then subsequent being OL will fail", description: 'First item being an UL then subsequent being OL will fail',
html : "<html><body><ul><li>a<ol><li>b</li><li>c</li></ol></li></ul></body></html>", html: '<html><body><ul><li>a<ol><li>b</li><li>c</li></ol></li></ul></body></html>',
expectedLineAttribs : [ '+1', '*0*1*2*3+1+1', '*0*4*2*5+1+1' ], expectedLineAttribs: ['+1', '*0*1*2*3+1+1', '*0*4*2*5+1+1'],
expectedText: ["a","*b","*c"], expectedText: ['a', '*b', '*c'],
noteToSelf: "Ensure empty P does not induce line attribute marker, wont this break the editor?", noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?',
disabled: true disabled: true,
}, },
lineDontBreakOL:{ lineDontBreakOL: {
description : "A single completely empty line break within an ol should NOT reset count", description: 'A single completely empty line break within an ol should NOT reset count',
html : "<html><body><ol><li>should be 1</li><p></p><li>should be 2</li><li>should be 3</li></ol><p></p></body></html>", html: '<html><body><ol><li>should be 1</li><p></p><li>should be 2</li><li>should be 3</li></ol><p></p></body></html>',
expectedLineAttribs : [ ], expectedLineAttribs: [],
expectedText: ["*should be 1","*should be 2","*should be 3"], expectedText: ['*should be 1', '*should be 2', '*should be 3'],
noteToSelf: "<p></p>should create a line break but not break numbering -- This is what I can't get working!", noteToSelf: "<p></p>should create a line break but not break numbering -- This is what I can't get working!",
disabled: true disabled: true,
} },
} };
describe(__filename, function() { describe(__filename, function () {
for (let test in tests) { for (const test in tests) {
let testObj = tests[test]; const testObj = tests[test];
describe(test, function() { describe(test, function () {
if (testObj.disabled) { if (testObj.disabled) {
return xit("DISABLED:", test, function(done){ return xit('DISABLED:', test, function (done) {
done(); done();
}) });
} }
it(testObj.description, function(done) { it(testObj.description, function (done) {
var $ = cheerio.load(testObj.html); // Load HTML into Cheerio const $ = cheerio.load(testObj.html); // Load HTML into Cheerio
var doc = $('html')[0]; // Creates a dom-like representation of HTML const doc = $('html')[0]; // Creates a dom-like representation of HTML
// Create an empty attribute pool // Create an empty attribute pool
var apool = new AttributePool(); const apool = new AttributePool();
// Convert a dom tree into a list of lines and attribute liens // Convert a dom tree into a list of lines and attribute liens
// using the content collector object // using the content collector object
var cc = contentcollector.makeContentCollector(true, null, apool); const cc = contentcollector.makeContentCollector(true, null, apool);
cc.collectContent(doc); cc.collectContent(doc);
var result = cc.finish(); const result = cc.finish();
var recievedAttributes = result.lineAttribs; const recievedAttributes = result.lineAttribs;
var expectedAttributes = testObj.expectedLineAttribs; const expectedAttributes = testObj.expectedLineAttribs;
var recievedText = new Array(result.lines) const recievedText = new Array(result.lines);
var expectedText = testObj.expectedText; const expectedText = testObj.expectedText;
// Check recieved text matches the expected text // Check recieved text matches the expected text
if (arraysEqual(recievedText[0], expectedText)) { if (arraysEqual(recievedText[0], expectedText)) {
// console.log("PASS: Recieved Text did match Expected Text\nRecieved:", recievedText[0], "\nExpected:", testObj.expectedText) // console.log("PASS: Recieved Text did match Expected Text\nRecieved:", recievedText[0], "\nExpected:", testObj.expectedText)
} else { } else {
console.error("FAIL: Recieved Text did not match Expected Text\nRecieved:", recievedText[0], "\nExpected:", testObj.expectedText) console.error('FAIL: Recieved Text did not match Expected Text\nRecieved:', recievedText[0], '\nExpected:', testObj.expectedText);
throw new Error(); throw new Error();
} }
@ -143,9 +146,9 @@ describe(__filename, function() {
// console.log("PASS: Recieved Attributes matched Expected Attributes"); // console.log("PASS: Recieved Attributes matched Expected Attributes");
done(); done();
} else { } else {
console.error("FAIL", test, testObj.description); console.error('FAIL', test, testObj.description);
console.error("FAIL: Recieved Attributes did not match Expected Attributes\nRecieved: ", recievedAttributes, "\nExpected: ", expectedAttributes) console.error('FAIL: Recieved Attributes did not match Expected Attributes\nRecieved: ', recievedAttributes, '\nExpected: ', expectedAttributes);
console.error("FAILING HTML", testObj.html); console.error('FAILING HTML', testObj.html);
throw new Error(); throw new Error();
} }
}); });
@ -154,8 +157,6 @@ describe(__filename, function() {
}); });
function arraysEqual(a, b) { function arraysEqual(a, b) {
if (a === b) return true; if (a === b) return true;
if (a == null || b == null) return false; if (a == null || b == null) return false;
@ -166,7 +167,7 @@ function arraysEqual(a, b) {
// Please note that calling sort on an array will modify that array. // Please note that calling sort on an array will modify that array.
// you might want to clone your array first. // you might want to clone your array first.
for (var i = 0; i < a.length; ++i) { for (let i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) return false; if (a[i] !== b[i]) return false;
} }
return true; return true;

View file

@ -1,6 +1,6 @@
/* global __dirname, __filename, afterEach, beforeEach, describe, it, process, require */ /* global __dirname, __filename, afterEach, beforeEach, describe, it, process, require */
function m(mod) { return __dirname + '/../../../src/' + mod; } function m(mod) { return `${__dirname}/../../../src/${mod}`; }
const assert = require('assert').strict; const assert = require('assert').strict;
const common = require('../common'); const common = require('../common');
@ -10,13 +10,13 @@ const sinon = require(m('node_modules/sinon'));
const logger = common.logger; const logger = common.logger;
describe(__filename, function() { describe(__filename, function () {
const hookName = 'testHook'; const hookName = 'testHook';
const hookFnName = 'testPluginFileName:testHookFunctionName'; const hookFnName = 'testPluginFileName:testHookFunctionName';
let testHooks; // Convenience shorthand for plugins.hooks[hookName]. let testHooks; // Convenience shorthand for plugins.hooks[hookName].
let hook; // Convenience shorthand for plugins.hooks[hookName][0]. let hook; // Convenience shorthand for plugins.hooks[hookName][0].
beforeEach(async function() { beforeEach(async function () {
// Make sure these are not already set so that we don't accidentally step on someone else's // Make sure these are not already set so that we don't accidentally step on someone else's
// toes: // toes:
assert(plugins.hooks[hookName] == null); assert(plugins.hooks[hookName] == null);
@ -30,15 +30,14 @@ describe(__filename, function() {
testHooks = plugins.hooks[hookName]; testHooks = plugins.hooks[hookName];
}); });
afterEach(async function() { afterEach(async function () {
sinon.restore(); sinon.restore();
delete plugins.hooks[hookName]; delete plugins.hooks[hookName];
delete hooks.deprecationNotices[hookName]; delete hooks.deprecationNotices[hookName];
delete hooks.exportedForTestingOnly.deprecationWarned[hookFnName]; delete hooks.exportedForTestingOnly.deprecationWarned[hookFnName];
}); });
const makeHook = (ret) => { const makeHook = (ret) => ({
return {
hook_name: hookName, hook_name: hookName,
// Many tests will likely want to change this. Unfortunately, we can't use a convenience // Many tests will likely want to change this. Unfortunately, we can't use a convenience
// wrapper like `(...args) => hookFn(..args)` because the hooks look at Function.length and // wrapper like `(...args) => hookFn(..args)` because the hooks look at Function.length and
@ -46,8 +45,7 @@ describe(__filename, function() {
hook_fn: (hn, ctx, cb) => cb(ret), hook_fn: (hn, ctx, cb) => cb(ret),
hook_fn_name: hookFnName, hook_fn_name: hookFnName,
part: {plugin: 'testPluginName'}, part: {plugin: 'testPluginName'},
}; });
};
// Hook functions that should work for both synchronous and asynchronous hooks. // Hook functions that should work for both synchronous and asynchronous hooks.
const supportedSyncHookFunctions = [ const supportedSyncHookFunctions = [
@ -95,30 +93,30 @@ describe(__filename, function() {
}, },
]; ];
describe('callHookFnSync', function() { describe('callHookFnSync', function () {
const callHookFnSync = hooks.exportedForTestingOnly.callHookFnSync; // Convenience shorthand. const callHookFnSync = hooks.exportedForTestingOnly.callHookFnSync; // Convenience shorthand.
describe('basic behavior', function() { describe('basic behavior', function () {
it('passes hook name', async function() { it('passes hook name', async function () {
hook.hook_fn = (hn) => { assert.equal(hn, hookName); }; hook.hook_fn = (hn) => { assert.equal(hn, hookName); };
callHookFnSync(hook); callHookFnSync(hook);
}); });
it('passes context', async function() { it('passes context', async function () {
for (const val of ['value', null, undefined]) { for (const val of ['value', null, undefined]) {
hook.hook_fn = (hn, ctx) => { assert.equal(ctx, val); }; hook.hook_fn = (hn, ctx) => { assert.equal(ctx, val); };
callHookFnSync(hook, val); callHookFnSync(hook, val);
} }
}); });
it('returns the value provided to the callback', async function() { it('returns the value provided to the callback', async function () {
for (const val of ['value', null, undefined]) { for (const val of ['value', null, undefined]) {
hook.hook_fn = (hn, ctx, cb) => { cb(ctx); }; hook.hook_fn = (hn, ctx, cb) => { cb(ctx); };
assert.equal(callHookFnSync(hook, val), val); assert.equal(callHookFnSync(hook, val), val);
} }
}); });
it('returns the value returned by the hook function', async function() { it('returns the value returned by the hook function', async function () {
for (const val of ['value', null, undefined]) { for (const val of ['value', null, undefined]) {
// Must not have the cb parameter otherwise returning undefined will error. // Must not have the cb parameter otherwise returning undefined will error.
hook.hook_fn = (hn, ctx) => ctx; hook.hook_fn = (hn, ctx) => ctx;
@ -126,17 +124,17 @@ describe(__filename, function() {
} }
}); });
it('does not catch exceptions', async function() { it('does not catch exceptions', async function () {
hook.hook_fn = () => { throw new Error('test exception'); }; hook.hook_fn = () => { throw new Error('test exception'); };
assert.throws(() => callHookFnSync(hook), {message: 'test exception'}); assert.throws(() => callHookFnSync(hook), {message: 'test exception'});
}); });
it('callback returns undefined', async function() { it('callback returns undefined', async function () {
hook.hook_fn = (hn, ctx, cb) => { assert.equal(cb('foo'), undefined); }; hook.hook_fn = (hn, ctx, cb) => { assert.equal(cb('foo'), undefined); };
callHookFnSync(hook); callHookFnSync(hook);
}); });
it('checks for deprecation', async function() { it('checks for deprecation', async function () {
sinon.stub(console, 'warn'); sinon.stub(console, 'warn');
hooks.deprecationNotices[hookName] = 'test deprecation'; hooks.deprecationNotices[hookName] = 'test deprecation';
callHookFnSync(hook); callHookFnSync(hook);
@ -146,9 +144,9 @@ describe(__filename, function() {
}); });
}); });
describe('supported hook function styles', function() { describe('supported hook function styles', function () {
for (const tc of supportedSyncHookFunctions) { for (const tc of supportedSyncHookFunctions) {
it(tc.name, async function() { it(tc.name, async function () {
sinon.stub(console, 'warn'); sinon.stub(console, 'warn');
sinon.stub(console, 'error'); sinon.stub(console, 'error');
hook.hook_fn = tc.fn; hook.hook_fn = tc.fn;
@ -164,7 +162,7 @@ describe(__filename, function() {
} }
}); });
describe('bad hook function behavior (other than double settle)', function() { describe('bad hook function behavior (other than double settle)', function () {
const promise1 = Promise.resolve('val1'); const promise1 = Promise.resolve('val1');
const promise2 = Promise.resolve('val2'); const promise2 = Promise.resolve('val2');
@ -192,7 +190,7 @@ describe(__filename, function() {
]; ];
for (const tc of testCases) { for (const tc of testCases) {
it(tc.name, async function() { it(tc.name, async function () {
sinon.stub(console, 'error'); sinon.stub(console, 'error');
hook.hook_fn = tc.fn; hook.hook_fn = tc.fn;
assert.equal(callHookFnSync(hook), tc.wantVal); assert.equal(callHookFnSync(hook), tc.wantVal);
@ -204,8 +202,8 @@ describe(__filename, function() {
// Test various ways a hook might attempt to settle twice. (Examples: call the callback a second // Test various ways a hook might attempt to settle twice. (Examples: call the callback a second
// time, or call the callback and then return a value.) // time, or call the callback and then return a value.)
describe('bad hook function behavior (double settle)', function() { describe('bad hook function behavior (double settle)', function () {
beforeEach(function() { beforeEach(function () {
sinon.stub(console, 'error'); sinon.stub(console, 'error');
}); });
@ -243,7 +241,7 @@ describe(__filename, function() {
// settle would complicate the tests, and it is sufficient to test only double settles. // settle would complicate the tests, and it is sufficient to test only double settles.
if (step1.async && step2.async) continue; if (step1.async && step2.async) continue;
it(`${step1.name} then ${step2.name} (diff. outcomes) -> log+throw`, async function() { it(`${step1.name} then ${step2.name} (diff. outcomes) -> log+throw`, async function () {
hook.hook_fn = (hn, ctx, cb) => { hook.hook_fn = (hn, ctx, cb) => {
step1.fn(cb, new Error(ctx.ret1), ctx.ret1); step1.fn(cb, new Error(ctx.ret1), ctx.ret1);
return step2.fn(cb, new Error(ctx.ret2), ctx.ret2); return step2.fn(cb, new Error(ctx.ret2), ctx.ret2);
@ -306,7 +304,7 @@ describe(__filename, function() {
// doesn't, so skip those cases. // doesn't, so skip those cases.
if (step1.rejects !== step2.rejects) continue; if (step1.rejects !== step2.rejects) continue;
it(`${step1.name} then ${step2.name} (same outcome) -> only log`, async function() { it(`${step1.name} then ${step2.name} (same outcome) -> only log`, async function () {
const err = new Error('val'); const err = new Error('val');
hook.hook_fn = (hn, ctx, cb) => { hook.hook_fn = (hn, ctx, cb) => {
step1.fn(cb, err, 'val'); step1.fn(cb, err, 'val');
@ -329,66 +327,66 @@ describe(__filename, function() {
}); });
}); });
describe('hooks.callAll', function() { describe('hooks.callAll', function () {
describe('basic behavior', function() { describe('basic behavior', function () {
it('calls all in order', async function() { it('calls all in order', async function () {
testHooks.length = 0; testHooks.length = 0;
testHooks.push(makeHook(1), makeHook(2), makeHook(3)); testHooks.push(makeHook(1), makeHook(2), makeHook(3));
assert.deepEqual(hooks.callAll(hookName), [1, 2, 3]); assert.deepEqual(hooks.callAll(hookName), [1, 2, 3]);
}); });
it('passes hook name', async function() { it('passes hook name', async function () {
hook.hook_fn = (hn) => { assert.equal(hn, hookName); }; hook.hook_fn = (hn) => { assert.equal(hn, hookName); };
hooks.callAll(hookName); hooks.callAll(hookName);
}); });
it('undefined context -> {}', async function() { it('undefined context -> {}', async function () {
hook.hook_fn = (hn, ctx) => { assert.deepEqual(ctx, {}); }; hook.hook_fn = (hn, ctx) => { assert.deepEqual(ctx, {}); };
hooks.callAll(hookName); hooks.callAll(hookName);
}); });
it('null context -> {}', async function() { it('null context -> {}', async function () {
hook.hook_fn = (hn, ctx) => { assert.deepEqual(ctx, {}); }; hook.hook_fn = (hn, ctx) => { assert.deepEqual(ctx, {}); };
hooks.callAll(hookName, null); hooks.callAll(hookName, null);
}); });
it('context unmodified', async function() { it('context unmodified', async function () {
const wantContext = {}; const wantContext = {};
hook.hook_fn = (hn, ctx) => { assert.equal(ctx, wantContext); }; hook.hook_fn = (hn, ctx) => { assert.equal(ctx, wantContext); };
hooks.callAll(hookName, wantContext); hooks.callAll(hookName, wantContext);
}); });
}); });
describe('result processing', function() { describe('result processing', function () {
it('no registered hooks (undefined) -> []', async function() { it('no registered hooks (undefined) -> []', async function () {
delete plugins.hooks.testHook; delete plugins.hooks.testHook;
assert.deepEqual(hooks.callAll(hookName), []); assert.deepEqual(hooks.callAll(hookName), []);
}); });
it('no registered hooks (empty list) -> []', async function() { it('no registered hooks (empty list) -> []', async function () {
testHooks.length = 0; testHooks.length = 0;
assert.deepEqual(hooks.callAll(hookName), []); assert.deepEqual(hooks.callAll(hookName), []);
}); });
it('flattens one level', async function() { it('flattens one level', async function () {
testHooks.length = 0; testHooks.length = 0;
testHooks.push(makeHook(1), makeHook([2]), makeHook([[3]])); testHooks.push(makeHook(1), makeHook([2]), makeHook([[3]]));
assert.deepEqual(hooks.callAll(hookName), [1, 2, [3]]); assert.deepEqual(hooks.callAll(hookName), [1, 2, [3]]);
}); });
it('filters out undefined', async function() { it('filters out undefined', async function () {
testHooks.length = 0; testHooks.length = 0;
testHooks.push(makeHook(), makeHook([2]), makeHook([[3]])); testHooks.push(makeHook(), makeHook([2]), makeHook([[3]]));
assert.deepEqual(hooks.callAll(hookName), [2, [3]]); assert.deepEqual(hooks.callAll(hookName), [2, [3]]);
}); });
it('preserves null', async function() { it('preserves null', async function () {
testHooks.length = 0; testHooks.length = 0;
testHooks.push(makeHook(null), makeHook([2]), makeHook([[3]])); testHooks.push(makeHook(null), makeHook([2]), makeHook([[3]]));
assert.deepEqual(hooks.callAll(hookName), [null, 2, [3]]); assert.deepEqual(hooks.callAll(hookName), [null, 2, [3]]);
}); });
it('all undefined -> []', async function() { it('all undefined -> []', async function () {
testHooks.length = 0; testHooks.length = 0;
testHooks.push(makeHook(), makeHook()); testHooks.push(makeHook(), makeHook());
assert.deepEqual(hooks.callAll(hookName), []); assert.deepEqual(hooks.callAll(hookName), []);
@ -396,23 +394,23 @@ describe(__filename, function() {
}); });
}); });
describe('callHookFnAsync', function() { describe('callHookFnAsync', function () {
const callHookFnAsync = hooks.exportedForTestingOnly.callHookFnAsync; // Convenience shorthand. const callHookFnAsync = hooks.exportedForTestingOnly.callHookFnAsync; // Convenience shorthand.
describe('basic behavior', function() { describe('basic behavior', function () {
it('passes hook name', async function() { it('passes hook name', async function () {
hook.hook_fn = (hn) => { assert.equal(hn, hookName); }; hook.hook_fn = (hn) => { assert.equal(hn, hookName); };
await callHookFnAsync(hook); await callHookFnAsync(hook);
}); });
it('passes context', async function() { it('passes context', async function () {
for (const val of ['value', null, undefined]) { for (const val of ['value', null, undefined]) {
hook.hook_fn = (hn, ctx) => { assert.equal(ctx, val); }; hook.hook_fn = (hn, ctx) => { assert.equal(ctx, val); };
await callHookFnAsync(hook, val); await callHookFnAsync(hook, val);
} }
}); });
it('returns the value provided to the callback', async function() { it('returns the value provided to the callback', async function () {
for (const val of ['value', null, undefined]) { for (const val of ['value', null, undefined]) {
hook.hook_fn = (hn, ctx, cb) => { cb(ctx); }; hook.hook_fn = (hn, ctx, cb) => { cb(ctx); };
assert.equal(await callHookFnAsync(hook, val), val); assert.equal(await callHookFnAsync(hook, val), val);
@ -420,7 +418,7 @@ describe(__filename, function() {
} }
}); });
it('returns the value returned by the hook function', async function() { it('returns the value returned by the hook function', async function () {
for (const val of ['value', null, undefined]) { for (const val of ['value', null, undefined]) {
// Must not have the cb parameter otherwise returning undefined will never resolve. // Must not have the cb parameter otherwise returning undefined will never resolve.
hook.hook_fn = (hn, ctx) => ctx; hook.hook_fn = (hn, ctx) => ctx;
@ -429,27 +427,27 @@ describe(__filename, function() {
} }
}); });
it('rejects if it throws an exception', async function() { it('rejects if it throws an exception', async function () {
hook.hook_fn = () => { throw new Error('test exception'); }; hook.hook_fn = () => { throw new Error('test exception'); };
await assert.rejects(callHookFnAsync(hook), {message: 'test exception'}); await assert.rejects(callHookFnAsync(hook), {message: 'test exception'});
}); });
it('rejects if rejected Promise passed to callback', async function() { it('rejects if rejected Promise passed to callback', async function () {
hook.hook_fn = (hn, ctx, cb) => cb(Promise.reject(new Error('test exception'))); hook.hook_fn = (hn, ctx, cb) => cb(Promise.reject(new Error('test exception')));
await assert.rejects(callHookFnAsync(hook), {message: 'test exception'}); await assert.rejects(callHookFnAsync(hook), {message: 'test exception'});
}); });
it('rejects if rejected Promise returned', async function() { it('rejects if rejected Promise returned', async function () {
hook.hook_fn = (hn, ctx, cb) => Promise.reject(new Error('test exception')); hook.hook_fn = (hn, ctx, cb) => Promise.reject(new Error('test exception'));
await assert.rejects(callHookFnAsync(hook), {message: 'test exception'}); await assert.rejects(callHookFnAsync(hook), {message: 'test exception'});
}); });
it('callback returns undefined', async function() { it('callback returns undefined', async function () {
hook.hook_fn = (hn, ctx, cb) => { assert.equal(cb('foo'), undefined); }; hook.hook_fn = (hn, ctx, cb) => { assert.equal(cb('foo'), undefined); };
await callHookFnAsync(hook); await callHookFnAsync(hook);
}); });
it('checks for deprecation', async function() { it('checks for deprecation', async function () {
sinon.stub(console, 'warn'); sinon.stub(console, 'warn');
hooks.deprecationNotices[hookName] = 'test deprecation'; hooks.deprecationNotices[hookName] = 'test deprecation';
await callHookFnAsync(hook); await callHookFnAsync(hook);
@ -459,7 +457,7 @@ describe(__filename, function() {
}); });
}); });
describe('supported hook function styles', function() { describe('supported hook function styles', function () {
const supportedHookFunctions = supportedSyncHookFunctions.concat([ const supportedHookFunctions = supportedSyncHookFunctions.concat([
{ {
name: 'legacy async cb', name: 'legacy async cb',
@ -541,7 +539,7 @@ describe(__filename, function() {
]); ]);
for (const tc of supportedSyncHookFunctions.concat(supportedHookFunctions)) { for (const tc of supportedSyncHookFunctions.concat(supportedHookFunctions)) {
it(tc.name, async function() { it(tc.name, async function () {
sinon.stub(console, 'warn'); sinon.stub(console, 'warn');
sinon.stub(console, 'error'); sinon.stub(console, 'error');
hook.hook_fn = tc.fn; hook.hook_fn = tc.fn;
@ -559,8 +557,8 @@ describe(__filename, function() {
// Test various ways a hook might attempt to settle twice. (Examples: call the callback a second // Test various ways a hook might attempt to settle twice. (Examples: call the callback a second
// time, or call the callback and then return a value.) // time, or call the callback and then return a value.)
describe('bad hook function behavior (double settle)', function() { describe('bad hook function behavior (double settle)', function () {
beforeEach(function() { beforeEach(function () {
sinon.stub(console, 'error'); sinon.stub(console, 'error');
}); });
@ -688,7 +686,7 @@ describe(__filename, function() {
// There can't be a second step if the first step is to return or throw. // There can't be a second step if the first step is to return or throw.
if (step1.name.startsWith('return ') || step1.name === 'throw') continue; if (step1.name.startsWith('return ') || step1.name === 'throw') continue;
for (const step2 of behaviors) { for (const step2 of behaviors) {
it(`${step1.name} then ${step2.name} (diff. outcomes) -> log+throw`, async function() { it(`${step1.name} then ${step2.name} (diff. outcomes) -> log+throw`, async function () {
hook.hook_fn = (hn, ctx, cb) => { hook.hook_fn = (hn, ctx, cb) => {
step1.fn(cb, new Error(ctx.ret1), ctx.ret1); step1.fn(cb, new Error(ctx.ret1), ctx.ret1);
return step2.fn(cb, new Error(ctx.ret2), ctx.ret2); return step2.fn(cb, new Error(ctx.ret2), ctx.ret2);
@ -729,8 +727,8 @@ describe(__filename, function() {
} }
} }
assert.equal(console.error.callCount, 1, assert.equal(console.error.callCount, 1,
'Got errors:\n' + `Got errors:\n${
console.error.getCalls().map((call) => call.args[0]).join('\n')); console.error.getCalls().map((call) => call.args[0]).join('\n')}`);
assert.match(console.error.getCall(0).args[0], /DOUBLE SETTLE BUG/); assert.match(console.error.getCall(0).args[0], /DOUBLE SETTLE BUG/);
assert(asyncErr instanceof Error); assert(asyncErr instanceof Error);
assert.match(asyncErr.message, /DOUBLE SETTLE BUG/); assert.match(asyncErr.message, /DOUBLE SETTLE BUG/);
@ -741,7 +739,7 @@ describe(__filename, function() {
// doesn't, so skip those cases. // doesn't, so skip those cases.
if (step1.rejects !== step2.rejects) continue; if (step1.rejects !== step2.rejects) continue;
it(`${step1.name} then ${step2.name} (same outcome) -> only log`, async function() { it(`${step1.name} then ${step2.name} (same outcome) -> only log`, async function () {
const err = new Error('val'); const err = new Error('val');
hook.hook_fn = (hn, ctx, cb) => { hook.hook_fn = (hn, ctx, cb) => {
step1.fn(cb, err, 'val'); step1.fn(cb, err, 'val');
@ -764,9 +762,9 @@ describe(__filename, function() {
}); });
}); });
describe('hooks.aCallAll', function() { describe('hooks.aCallAll', function () {
describe('basic behavior', function() { describe('basic behavior', function () {
it('calls all asynchronously, returns values in order', async function() { it('calls all asynchronously, returns values in order', async function () {
testHooks.length = 0; // Delete the boilerplate hook -- this test doesn't use it. testHooks.length = 0; // Delete the boilerplate hook -- this test doesn't use it.
let nextIndex = 0; let nextIndex = 0;
const hookPromises = []; const hookPromises = [];
@ -800,35 +798,35 @@ describe(__filename, function() {
assert.deepEqual(await p, [0, 1]); assert.deepEqual(await p, [0, 1]);
}); });
it('passes hook name', async function() { it('passes hook name', async function () {
hook.hook_fn = async (hn) => { assert.equal(hn, hookName); }; hook.hook_fn = async (hn) => { assert.equal(hn, hookName); };
await hooks.aCallAll(hookName); await hooks.aCallAll(hookName);
}); });
it('undefined context -> {}', async function() { it('undefined context -> {}', async function () {
hook.hook_fn = async (hn, ctx) => { assert.deepEqual(ctx, {}); }; hook.hook_fn = async (hn, ctx) => { assert.deepEqual(ctx, {}); };
await hooks.aCallAll(hookName); await hooks.aCallAll(hookName);
}); });
it('null context -> {}', async function() { it('null context -> {}', async function () {
hook.hook_fn = async (hn, ctx) => { assert.deepEqual(ctx, {}); }; hook.hook_fn = async (hn, ctx) => { assert.deepEqual(ctx, {}); };
await hooks.aCallAll(hookName, null); await hooks.aCallAll(hookName, null);
}); });
it('context unmodified', async function() { it('context unmodified', async function () {
const wantContext = {}; const wantContext = {};
hook.hook_fn = async (hn, ctx) => { assert.equal(ctx, wantContext); }; hook.hook_fn = async (hn, ctx) => { assert.equal(ctx, wantContext); };
await hooks.aCallAll(hookName, wantContext); await hooks.aCallAll(hookName, wantContext);
}); });
}); });
describe('aCallAll callback', function() { describe('aCallAll callback', function () {
it('exception in callback rejects', async function() { it('exception in callback rejects', async function () {
const p = hooks.aCallAll(hookName, {}, () => { throw new Error('test exception'); }); const p = hooks.aCallAll(hookName, {}, () => { throw new Error('test exception'); });
await assert.rejects(p, {message: 'test exception'}); await assert.rejects(p, {message: 'test exception'});
}); });
it('propagates error on exception', async function() { it('propagates error on exception', async function () {
hook.hook_fn = () => { throw new Error('test exception'); }; hook.hook_fn = () => { throw new Error('test exception'); };
await hooks.aCallAll(hookName, {}, (err) => { await hooks.aCallAll(hookName, {}, (err) => {
assert(err instanceof Error); assert(err instanceof Error);
@ -836,54 +834,54 @@ describe(__filename, function() {
}); });
}); });
it('propagages null error on success', async function() { it('propagages null error on success', async function () {
await hooks.aCallAll(hookName, {}, (err) => { await hooks.aCallAll(hookName, {}, (err) => {
assert(err == null, `got non-null error: ${err}`); assert(err == null, `got non-null error: ${err}`);
}); });
}); });
it('propagages results on success', async function() { it('propagages results on success', async function () {
hook.hook_fn = () => 'val'; hook.hook_fn = () => 'val';
await hooks.aCallAll(hookName, {}, (err, results) => { await hooks.aCallAll(hookName, {}, (err, results) => {
assert.deepEqual(results, ['val']); assert.deepEqual(results, ['val']);
}); });
}); });
it('returns callback return value', async function() { it('returns callback return value', async function () {
assert.equal(await hooks.aCallAll(hookName, {}, () => 'val'), 'val'); assert.equal(await hooks.aCallAll(hookName, {}, () => 'val'), 'val');
}); });
}); });
describe('result processing', function() { describe('result processing', function () {
it('no registered hooks (undefined) -> []', async function() { it('no registered hooks (undefined) -> []', async function () {
delete plugins.hooks[hookName]; delete plugins.hooks[hookName];
assert.deepEqual(await hooks.aCallAll(hookName), []); assert.deepEqual(await hooks.aCallAll(hookName), []);
}); });
it('no registered hooks (empty list) -> []', async function() { it('no registered hooks (empty list) -> []', async function () {
testHooks.length = 0; testHooks.length = 0;
assert.deepEqual(await hooks.aCallAll(hookName), []); assert.deepEqual(await hooks.aCallAll(hookName), []);
}); });
it('flattens one level', async function() { it('flattens one level', async function () {
testHooks.length = 0; testHooks.length = 0;
testHooks.push(makeHook(1), makeHook([2]), makeHook([[3]])); testHooks.push(makeHook(1), makeHook([2]), makeHook([[3]]));
assert.deepEqual(await hooks.aCallAll(hookName), [1, 2, [3]]); assert.deepEqual(await hooks.aCallAll(hookName), [1, 2, [3]]);
}); });
it('filters out undefined', async function() { it('filters out undefined', async function () {
testHooks.length = 0; testHooks.length = 0;
testHooks.push(makeHook(), makeHook([2]), makeHook([[3]]), makeHook(Promise.resolve())); testHooks.push(makeHook(), makeHook([2]), makeHook([[3]]), makeHook(Promise.resolve()));
assert.deepEqual(await hooks.aCallAll(hookName), [2, [3]]); assert.deepEqual(await hooks.aCallAll(hookName), [2, [3]]);
}); });
it('preserves null', async function() { it('preserves null', async function () {
testHooks.length = 0; testHooks.length = 0;
testHooks.push(makeHook(null), makeHook([2]), makeHook(Promise.resolve(null))); testHooks.push(makeHook(null), makeHook([2]), makeHook(Promise.resolve(null)));
assert.deepEqual(await hooks.aCallAll(hookName), [null, 2, null]); assert.deepEqual(await hooks.aCallAll(hookName), [null, 2, null]);
}); });
it('all undefined -> []', async function() { it('all undefined -> []', async function () {
testHooks.length = 0; testHooks.length = 0;
testHooks.push(makeHook(), makeHook(Promise.resolve())); testHooks.push(makeHook(), makeHook(Promise.resolve()));
assert.deepEqual(await hooks.aCallAll(hookName), []); assert.deepEqual(await hooks.aCallAll(hookName), []);

View file

@ -1,10 +1,10 @@
function m(mod) { return __dirname + '/../../../src/' + mod; } function m(mod) { return `${__dirname}/../../../src/${mod}`; }
const assert = require('assert').strict; const assert = require('assert').strict;
const promises = require(m('node/utils/promises')); const promises = require(m('node/utils/promises'));
describe(__filename, function() { describe(__filename, function () {
describe('promises.timesLimit', function() { describe('promises.timesLimit', function () {
let wantIndex = 0; let wantIndex = 0;
const testPromises = []; const testPromises = [];
const makePromise = (index) => { const makePromise = (index) => {
@ -25,18 +25,18 @@ describe(__filename, function() {
const concurrency = 7; const concurrency = 7;
const timesLimitPromise = promises.timesLimit(total, concurrency, makePromise); const timesLimitPromise = promises.timesLimit(total, concurrency, makePromise);
it('honors concurrency', async function() { it('honors concurrency', async function () {
assert.equal(wantIndex, concurrency); assert.equal(wantIndex, concurrency);
}); });
it('creates another when one completes', async function() { it('creates another when one completes', async function () {
const {promise, resolve} = testPromises.shift(); const {promise, resolve} = testPromises.shift();
resolve(); resolve();
await promise; await promise;
assert.equal(wantIndex, concurrency + 1); assert.equal(wantIndex, concurrency + 1);
}); });
it('creates the expected total number of promises', async function() { it('creates the expected total number of promises', async function () {
while (testPromises.length > 0) { while (testPromises.length > 0) {
// Resolve them in random order to ensure that the resolution order doesn't matter. // Resolve them in random order to ensure that the resolution order doesn't matter.
const i = Math.floor(Math.random() * Math.floor(testPromises.length)); const i = Math.floor(Math.random() * Math.floor(testPromises.length));
@ -47,11 +47,11 @@ describe(__filename, function() {
assert.equal(wantIndex, total); assert.equal(wantIndex, total);
}); });
it('resolves', async function() { it('resolves', async function () {
await timesLimitPromise; await timesLimitPromise;
}); });
it('does not create too many promises if total < concurrency', async function() { it('does not create too many promises if total < concurrency', async function () {
wantIndex = 0; wantIndex = 0;
assert.equal(testPromises.length, 0); assert.equal(testPromises.length, 0);
const total = 7; const total = 7;
@ -66,21 +66,21 @@ describe(__filename, function() {
assert.equal(wantIndex, total); assert.equal(wantIndex, total);
}); });
it('accepts total === 0, concurrency > 0', async function() { it('accepts total === 0, concurrency > 0', async function () {
wantIndex = 0; wantIndex = 0;
assert.equal(testPromises.length, 0); assert.equal(testPromises.length, 0);
await promises.timesLimit(0, concurrency, makePromise); await promises.timesLimit(0, concurrency, makePromise);
assert.equal(wantIndex, 0); assert.equal(wantIndex, 0);
}); });
it('accepts total === 0, concurrency === 0', async function() { it('accepts total === 0, concurrency === 0', async function () {
wantIndex = 0; wantIndex = 0;
assert.equal(testPromises.length, 0); assert.equal(testPromises.length, 0);
await promises.timesLimit(0, 0, makePromise); await promises.timesLimit(0, 0, makePromise);
assert.equal(wantIndex, 0); assert.equal(wantIndex, 0);
}); });
it('rejects total > 0, concurrency === 0', async function() { it('rejects total > 0, concurrency === 0', async function () {
await assert.rejects(promises.timesLimit(total, 0, makePromise), RangeError); await assert.rejects(promises.timesLimit(total, 0, makePromise), RangeError);
}); });
}); });

View file

@ -1,6 +1,6 @@
/* global __dirname, __filename, afterEach, before, beforeEach, clearTimeout, describe, it, require, setTimeout */ /* global __dirname, __filename, afterEach, before, beforeEach, clearTimeout, describe, it, require, setTimeout */
function m(mod) { return __dirname + '/../../../src/' + mod; } function m(mod) { return `${__dirname}/../../../src/${mod}`; }
const assert = require('assert').strict; const assert = require('assert').strict;
const common = require('../common'); const common = require('../common');
@ -50,9 +50,7 @@ const getSocketEvent = async (socket, event) => {
const connect = async (res) => { const connect = async (res) => {
// Convert the `set-cookie` header(s) into a `cookie` header. // Convert the `set-cookie` header(s) into a `cookie` header.
const resCookies = (res == null) ? {} : setCookieParser.parse(res, {map: true}); const resCookies = (res == null) ? {} : setCookieParser.parse(res, {map: true});
const reqCookieHdr = Object.entries(resCookies).map(([name, cookie]) => { const reqCookieHdr = Object.entries(resCookies).map(([name, cookie]) => `${name}=${encodeURIComponent(cookie.value)}`).join('; ');
return `${name}=${encodeURIComponent(cookie.value)}`;
}).join('; ');
logger.debug('socket.io connecting...'); logger.debug('socket.io connecting...');
const socket = io(`${common.baseUrl}/`, { const socket = io(`${common.baseUrl}/`, {
@ -91,7 +89,7 @@ const handshake = async (socket, padID) => {
return msg; return msg;
}; };
describe(__filename, function() { describe(__filename, function () {
let agent; let agent;
let authorize; let authorize;
const backups = {}; const backups = {};
@ -106,8 +104,8 @@ describe(__filename, function() {
}; };
let socket; let socket;
before(async function() { agent = await common.init(); }); before(async function () { agent = await common.init(); });
beforeEach(async function() { beforeEach(async function () {
backups.hooks = {}; backups.hooks = {};
for (const hookName of ['preAuthorize', 'authenticate', 'authorize']) { for (const hookName of ['preAuthorize', 'authenticate', 'authorize']) {
backups.hooks[hookName] = plugins.hooks[hookName]; backups.hooks[hookName] = plugins.hooks[hookName];
@ -126,12 +124,10 @@ describe(__filename, function() {
}; };
assert(socket == null); assert(socket == null);
authorize = () => true; authorize = () => true;
plugins.hooks.authorize = [{hook_fn: (hookName, {req}, cb) => { plugins.hooks.authorize = [{hook_fn: (hookName, {req}, cb) => cb([authorize(req)])}];
return cb([authorize(req)]);
}}];
await cleanUpPads(); await cleanUpPads();
}); });
afterEach(async function() { afterEach(async function () {
if (socket) socket.close(); if (socket) socket.close();
socket = null; socket = null;
await cleanUpPads(); await cleanUpPads();
@ -139,32 +135,32 @@ describe(__filename, function() {
Object.assign(settings, backups.settings); Object.assign(settings, backups.settings);
}); });
describe('Normal accesses', function() { describe('Normal accesses', function () {
it('!authn anonymous cookie /p/pad -> 200, ok', async function() { it('!authn anonymous cookie /p/pad -> 200, ok', async function () {
const res = await agent.get('/p/pad').expect(200); const res = await agent.get('/p/pad').expect(200);
socket = await connect(res); socket = await connect(res);
const clientVars = await handshake(socket, 'pad'); const clientVars = await handshake(socket, 'pad');
assert.equal(clientVars.type, 'CLIENT_VARS'); assert.equal(clientVars.type, 'CLIENT_VARS');
}); });
it('!authn !cookie -> ok', async function() { it('!authn !cookie -> ok', async function () {
socket = await connect(null); socket = await connect(null);
const clientVars = await handshake(socket, 'pad'); const clientVars = await handshake(socket, 'pad');
assert.equal(clientVars.type, 'CLIENT_VARS'); assert.equal(clientVars.type, 'CLIENT_VARS');
}); });
it('!authn user /p/pad -> 200, ok', async function() { it('!authn user /p/pad -> 200, ok', async function () {
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
socket = await connect(res); socket = await connect(res);
const clientVars = await handshake(socket, 'pad'); const clientVars = await handshake(socket, 'pad');
assert.equal(clientVars.type, 'CLIENT_VARS'); assert.equal(clientVars.type, 'CLIENT_VARS');
}); });
it('authn user /p/pad -> 200, ok', async function() { it('authn user /p/pad -> 200, ok', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
socket = await connect(res); socket = await connect(res);
const clientVars = await handshake(socket, 'pad'); const clientVars = await handshake(socket, 'pad');
assert.equal(clientVars.type, 'CLIENT_VARS'); assert.equal(clientVars.type, 'CLIENT_VARS');
}); });
it('authz user /p/pad -> 200, ok', async function() { it('authz user /p/pad -> 200, ok', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
settings.requireAuthorization = true; settings.requireAuthorization = true;
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
@ -172,7 +168,7 @@ describe(__filename, function() {
const clientVars = await handshake(socket, 'pad'); const clientVars = await handshake(socket, 'pad');
assert.equal(clientVars.type, 'CLIENT_VARS'); assert.equal(clientVars.type, 'CLIENT_VARS');
}); });
it('supports pad names with characters that must be percent-encoded', async function() { it('supports pad names with characters that must be percent-encoded', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
// requireAuthorization is set to true here to guarantee that the user's padAuthorizations // requireAuthorization is set to true here to guarantee that the user's padAuthorizations
// object is populated. Technically this isn't necessary because the user's padAuthorizations // object is populated. Technically this isn't necessary because the user's padAuthorizations
@ -187,8 +183,8 @@ describe(__filename, function() {
}); });
}); });
describe('Abnormal access attempts', function() { describe('Abnormal access attempts', function () {
it('authn anonymous /p/pad -> 401, error', async function() { it('authn anonymous /p/pad -> 401, error', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
const res = await agent.get('/p/pad').expect(401); const res = await agent.get('/p/pad').expect(401);
// Despite the 401, try to create the pad via a socket.io connection anyway. // Despite the 401, try to create the pad via a socket.io connection anyway.
@ -196,13 +192,13 @@ describe(__filename, function() {
const message = await handshake(socket, 'pad'); const message = await handshake(socket, 'pad');
assert.equal(message.accessStatus, 'deny'); assert.equal(message.accessStatus, 'deny');
}); });
it('authn !cookie -> error', async function() { it('authn !cookie -> error', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
socket = await connect(null); socket = await connect(null);
const message = await handshake(socket, 'pad'); const message = await handshake(socket, 'pad');
assert.equal(message.accessStatus, 'deny'); assert.equal(message.accessStatus, 'deny');
}); });
it('authorization bypass attempt -> error', async function() { it('authorization bypass attempt -> error', async function () {
// Only allowed to access /p/pad. // Only allowed to access /p/pad.
authorize = (req) => req.path === '/p/pad'; authorize = (req) => req.path === '/p/pad';
settings.requireAuthentication = true; settings.requireAuthentication = true;
@ -216,13 +212,13 @@ describe(__filename, function() {
}); });
}); });
describe('Authorization levels via authorize hook', function() { describe('Authorization levels via authorize hook', function () {
beforeEach(async function() { beforeEach(async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
settings.requireAuthorization = true; settings.requireAuthorization = true;
}); });
it("level='create' -> can create", async function() { it("level='create' -> can create", async function () {
authorize = () => 'create'; authorize = () => 'create';
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
socket = await connect(res); socket = await connect(res);
@ -230,7 +226,7 @@ describe(__filename, function() {
assert.equal(clientVars.type, 'CLIENT_VARS'); assert.equal(clientVars.type, 'CLIENT_VARS');
assert.equal(clientVars.data.readonly, false); assert.equal(clientVars.data.readonly, false);
}); });
it('level=true -> can create', async function() { it('level=true -> can create', async function () {
authorize = () => true; authorize = () => true;
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
socket = await connect(res); socket = await connect(res);
@ -238,7 +234,7 @@ describe(__filename, function() {
assert.equal(clientVars.type, 'CLIENT_VARS'); assert.equal(clientVars.type, 'CLIENT_VARS');
assert.equal(clientVars.data.readonly, false); assert.equal(clientVars.data.readonly, false);
}); });
it("level='modify' -> can modify", async function() { it("level='modify' -> can modify", async function () {
await padManager.getPad('pad'); // Create the pad. await padManager.getPad('pad'); // Create the pad.
authorize = () => 'modify'; authorize = () => 'modify';
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
@ -247,7 +243,7 @@ describe(__filename, function() {
assert.equal(clientVars.type, 'CLIENT_VARS'); assert.equal(clientVars.type, 'CLIENT_VARS');
assert.equal(clientVars.data.readonly, false); assert.equal(clientVars.data.readonly, false);
}); });
it("level='create' settings.editOnly=true -> unable to create", async function() { it("level='create' settings.editOnly=true -> unable to create", async function () {
authorize = () => 'create'; authorize = () => 'create';
settings.editOnly = true; settings.editOnly = true;
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
@ -255,7 +251,7 @@ describe(__filename, function() {
const message = await handshake(socket, 'pad'); const message = await handshake(socket, 'pad');
assert.equal(message.accessStatus, 'deny'); assert.equal(message.accessStatus, 'deny');
}); });
it("level='modify' settings.editOnly=false -> unable to create", async function() { it("level='modify' settings.editOnly=false -> unable to create", async function () {
authorize = () => 'modify'; authorize = () => 'modify';
settings.editOnly = false; settings.editOnly = false;
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
@ -263,14 +259,14 @@ describe(__filename, function() {
const message = await handshake(socket, 'pad'); const message = await handshake(socket, 'pad');
assert.equal(message.accessStatus, 'deny'); assert.equal(message.accessStatus, 'deny');
}); });
it("level='readOnly' -> unable to create", async function() { it("level='readOnly' -> unable to create", async function () {
authorize = () => 'readOnly'; authorize = () => 'readOnly';
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
socket = await connect(res); socket = await connect(res);
const message = await handshake(socket, 'pad'); const message = await handshake(socket, 'pad');
assert.equal(message.accessStatus, 'deny'); assert.equal(message.accessStatus, 'deny');
}); });
it("level='readOnly' -> unable to modify", async function() { it("level='readOnly' -> unable to modify", async function () {
await padManager.getPad('pad'); // Create the pad. await padManager.getPad('pad'); // Create the pad.
authorize = () => 'readOnly'; authorize = () => 'readOnly';
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
@ -281,12 +277,12 @@ describe(__filename, function() {
}); });
}); });
describe('Authorization levels via user settings', function() { describe('Authorization levels via user settings', function () {
beforeEach(async function() { beforeEach(async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
}); });
it('user.canCreate = true -> can create and modify', async function() { it('user.canCreate = true -> can create and modify', async function () {
settings.users.user.canCreate = true; settings.users.user.canCreate = true;
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
socket = await connect(res); socket = await connect(res);
@ -294,21 +290,21 @@ describe(__filename, function() {
assert.equal(clientVars.type, 'CLIENT_VARS'); assert.equal(clientVars.type, 'CLIENT_VARS');
assert.equal(clientVars.data.readonly, false); assert.equal(clientVars.data.readonly, false);
}); });
it('user.canCreate = false -> unable to create', async function() { it('user.canCreate = false -> unable to create', async function () {
settings.users.user.canCreate = false; settings.users.user.canCreate = false;
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
socket = await connect(res); socket = await connect(res);
const message = await handshake(socket, 'pad'); const message = await handshake(socket, 'pad');
assert.equal(message.accessStatus, 'deny'); assert.equal(message.accessStatus, 'deny');
}); });
it('user.readOnly = true -> unable to create', async function() { it('user.readOnly = true -> unable to create', async function () {
settings.users.user.readOnly = true; settings.users.user.readOnly = true;
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
socket = await connect(res); socket = await connect(res);
const message = await handshake(socket, 'pad'); const message = await handshake(socket, 'pad');
assert.equal(message.accessStatus, 'deny'); assert.equal(message.accessStatus, 'deny');
}); });
it('user.readOnly = true -> unable to modify', async function() { it('user.readOnly = true -> unable to modify', async function () {
await padManager.getPad('pad'); // Create the pad. await padManager.getPad('pad'); // Create the pad.
settings.users.user.readOnly = true; settings.users.user.readOnly = true;
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
@ -317,7 +313,7 @@ describe(__filename, function() {
assert.equal(clientVars.type, 'CLIENT_VARS'); assert.equal(clientVars.type, 'CLIENT_VARS');
assert.equal(clientVars.data.readonly, true); assert.equal(clientVars.data.readonly, true);
}); });
it('user.readOnly = false -> can create and modify', async function() { it('user.readOnly = false -> can create and modify', async function () {
settings.users.user.readOnly = false; settings.users.user.readOnly = false;
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
socket = await connect(res); socket = await connect(res);
@ -325,7 +321,7 @@ describe(__filename, function() {
assert.equal(clientVars.type, 'CLIENT_VARS'); assert.equal(clientVars.type, 'CLIENT_VARS');
assert.equal(clientVars.data.readonly, false); assert.equal(clientVars.data.readonly, false);
}); });
it('user.readOnly = true, user.canCreate = true -> unable to create', async function() { it('user.readOnly = true, user.canCreate = true -> unable to create', async function () {
settings.users.user.canCreate = true; settings.users.user.canCreate = true;
settings.users.user.readOnly = true; settings.users.user.readOnly = true;
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
@ -335,13 +331,13 @@ describe(__filename, function() {
}); });
}); });
describe('Authorization level interaction between authorize hook and user settings', function() { describe('Authorization level interaction between authorize hook and user settings', function () {
beforeEach(async function() { beforeEach(async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
settings.requireAuthorization = true; settings.requireAuthorization = true;
}); });
it('authorize hook does not elevate level from user settings', async function() { it('authorize hook does not elevate level from user settings', async function () {
settings.users.user.readOnly = true; settings.users.user.readOnly = true;
authorize = () => 'create'; authorize = () => 'create';
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200); const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
@ -349,7 +345,7 @@ describe(__filename, function() {
const message = await handshake(socket, 'pad'); const message = await handshake(socket, 'pad');
assert.equal(message.accessStatus, 'deny'); assert.equal(message.accessStatus, 'deny');
}); });
it('user settings does not elevate level from authorize hook', async function() { it('user settings does not elevate level from authorize hook', async function () {
settings.users.user.readOnly = false; settings.users.user.readOnly = false;
settings.users.user.canCreate = true; settings.users.user.canCreate = true;
authorize = () => 'readOnly'; authorize = () => 'readOnly';

View file

@ -1,11 +1,11 @@
const common = require('../common'); const common = require('../common');
const settings = require('ep_etherpad-lite/node/utils/Settings'); const settings = require('ep_etherpad-lite/node/utils/Settings');
describe(__filename, function() { describe(__filename, function () {
let agent; let agent;
const backups = {}; const backups = {};
before(async function() { agent = await common.init(); }); before(async function () { agent = await common.init(); });
beforeEach(async function() { beforeEach(async function () {
backups.settings = {}; backups.settings = {};
for (const setting of ['requireAuthentication', 'requireAuthorization']) { for (const setting of ['requireAuthentication', 'requireAuthorization']) {
backups.settings[setting] = settings[setting]; backups.settings[setting] = settings[setting];
@ -13,12 +13,12 @@ describe(__filename, function() {
settings.requireAuthentication = false; settings.requireAuthentication = false;
settings.requireAuthorization = false; settings.requireAuthorization = false;
}); });
afterEach(async function() { afterEach(async function () {
Object.assign(settings, backups.settings); Object.assign(settings, backups.settings);
}); });
describe('/javascript', function() { describe('/javascript', function () {
it('/javascript -> 200', async function() { it('/javascript -> 200', async function () {
await agent.get('/javascript').expect(200); await agent.get('/javascript').expect(200);
}); });
}); });

View file

@ -1,19 +1,19 @@
/* global __dirname, __filename, Buffer, afterEach, before, beforeEach, describe, it, require */ /* global __dirname, __filename, Buffer, afterEach, before, beforeEach, describe, it, require */
function m(mod) { return __dirname + '/../../../src/' + mod; } function m(mod) { return `${__dirname}/../../../src/${mod}`; }
const assert = require('assert').strict; const assert = require('assert').strict;
const common = require('../common'); const common = require('../common');
const plugins = require(m('static/js/pluginfw/plugin_defs')); const plugins = require(m('static/js/pluginfw/plugin_defs'));
const settings = require(m('node/utils/Settings')); const settings = require(m('node/utils/Settings'));
describe(__filename, function() { describe(__filename, function () {
let agent; let agent;
const backups = {}; const backups = {};
const authHookNames = ['preAuthorize', 'authenticate', 'authorize']; const authHookNames = ['preAuthorize', 'authenticate', 'authorize'];
const failHookNames = ['preAuthzFailure', 'authnFailure', 'authzFailure', 'authFailure']; const failHookNames = ['preAuthzFailure', 'authnFailure', 'authzFailure', 'authFailure'];
before(async function() { agent = await common.init(); }); before(async function () { agent = await common.init(); });
beforeEach(async function() { beforeEach(async function () {
backups.hooks = {}; backups.hooks = {};
for (const hookName of authHookNames.concat(failHookNames)) { for (const hookName of authHookNames.concat(failHookNames)) {
backups.hooks[hookName] = plugins.hooks[hookName]; backups.hooks[hookName] = plugins.hooks[hookName];
@ -30,76 +30,76 @@ describe(__filename, function() {
user: {password: 'user-password'}, user: {password: 'user-password'},
}; };
}); });
afterEach(async function() { afterEach(async function () {
Object.assign(plugins.hooks, backups.hooks); Object.assign(plugins.hooks, backups.hooks);
Object.assign(settings, backups.settings); Object.assign(settings, backups.settings);
}); });
describe('webaccess: without plugins', function() { describe('webaccess: without plugins', function () {
it('!authn !authz anonymous / -> 200', async function() { it('!authn !authz anonymous / -> 200', async function () {
settings.requireAuthentication = false; settings.requireAuthentication = false;
settings.requireAuthorization = false; settings.requireAuthorization = false;
await agent.get('/').expect(200); await agent.get('/').expect(200);
}); });
it('!authn !authz anonymous /admin/ -> 401', async function() { it('!authn !authz anonymous /admin/ -> 401', async function () {
settings.requireAuthentication = false; settings.requireAuthentication = false;
settings.requireAuthorization = false; settings.requireAuthorization = false;
await agent.get('/admin/').expect(401); await agent.get('/admin/').expect(401);
}); });
it('authn !authz anonymous / -> 401', async function() { it('authn !authz anonymous / -> 401', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
settings.requireAuthorization = false; settings.requireAuthorization = false;
await agent.get('/').expect(401); await agent.get('/').expect(401);
}); });
it('authn !authz user / -> 200', async function() { it('authn !authz user / -> 200', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
settings.requireAuthorization = false; settings.requireAuthorization = false;
await agent.get('/').auth('user', 'user-password').expect(200); await agent.get('/').auth('user', 'user-password').expect(200);
}); });
it('authn !authz user /admin/ -> 403', async function() { it('authn !authz user /admin/ -> 403', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
settings.requireAuthorization = false; settings.requireAuthorization = false;
await agent.get('/admin/').auth('user', 'user-password').expect(403); await agent.get('/admin/').auth('user', 'user-password').expect(403);
}); });
it('authn !authz admin / -> 200', async function() { it('authn !authz admin / -> 200', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
settings.requireAuthorization = false; settings.requireAuthorization = false;
await agent.get('/').auth('admin', 'admin-password').expect(200); await agent.get('/').auth('admin', 'admin-password').expect(200);
}); });
it('authn !authz admin /admin/ -> 200', async function() { it('authn !authz admin /admin/ -> 200', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
settings.requireAuthorization = false; settings.requireAuthorization = false;
await agent.get('/admin/').auth('admin', 'admin-password').expect(200); await agent.get('/admin/').auth('admin', 'admin-password').expect(200);
}); });
it('authn authz user / -> 403', async function() { it('authn authz user / -> 403', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
settings.requireAuthorization = true; settings.requireAuthorization = true;
await agent.get('/').auth('user', 'user-password').expect(403); await agent.get('/').auth('user', 'user-password').expect(403);
}); });
it('authn authz user /admin/ -> 403', async function() { it('authn authz user /admin/ -> 403', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
settings.requireAuthorization = true; settings.requireAuthorization = true;
await agent.get('/admin/').auth('user', 'user-password').expect(403); await agent.get('/admin/').auth('user', 'user-password').expect(403);
}); });
it('authn authz admin / -> 200', async function() { it('authn authz admin / -> 200', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
settings.requireAuthorization = true; settings.requireAuthorization = true;
await agent.get('/').auth('admin', 'admin-password').expect(200); await agent.get('/').auth('admin', 'admin-password').expect(200);
}); });
it('authn authz admin /admin/ -> 200', async function() { it('authn authz admin /admin/ -> 200', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
settings.requireAuthorization = true; settings.requireAuthorization = true;
await agent.get('/admin/').auth('admin', 'admin-password').expect(200); await agent.get('/admin/').auth('admin', 'admin-password').expect(200);
}); });
describe('login fails if password is nullish', function() { describe('login fails if password is nullish', function () {
for (const adminPassword of [undefined, null]) { for (const adminPassword of [undefined, null]) {
// https://tools.ietf.org/html/rfc7617 says that the username and password are sent as // https://tools.ietf.org/html/rfc7617 says that the username and password are sent as
// base64(username + ':' + password), but there's nothing stopping a malicious user from // base64(username + ':' + password), but there's nothing stopping a malicious user from
// sending just base64(username) (no colon). The lack of colon could throw off credential // sending just base64(username) (no colon). The lack of colon could throw off credential
// parsing, resulting in successful comparisons against a null or undefined password. // parsing, resulting in successful comparisons against a null or undefined password.
for (const creds of ['admin', 'admin:']) { for (const creds of ['admin', 'admin:']) {
it(`admin password: ${adminPassword} credentials: ${creds}`, async function() { it(`admin password: ${adminPassword} credentials: ${creds}`, async function () {
settings.users.admin.password = adminPassword; settings.users.admin.password = adminPassword;
const encCreds = Buffer.from(creds).toString('base64'); const encCreds = Buffer.from(creds).toString('base64');
await agent.get('/admin/').set('Authorization', `Basic ${encCreds}`).expect(401); await agent.get('/admin/').set('Authorization', `Basic ${encCreds}`).expect(401);
@ -109,7 +109,7 @@ describe(__filename, function() {
}); });
}); });
describe('webaccess: preAuthorize, authenticate, and authorize hooks', function() { describe('webaccess: preAuthorize, authenticate, and authorize hooks', function () {
let callOrder; let callOrder;
const Handler = class { const Handler = class {
constructor(hookName, suffix) { constructor(hookName, suffix) {
@ -134,7 +134,7 @@ describe(__filename, function() {
}; };
const handlers = {}; const handlers = {};
beforeEach(async function() { beforeEach(async function () {
callOrder = []; callOrder = [];
for (const hookName of authHookNames) { for (const hookName of authHookNames) {
// Create two handlers for each hook to test deferral to the next function. // Create two handlers for each hook to test deferral to the next function.
@ -145,38 +145,38 @@ describe(__filename, function() {
} }
}); });
describe('preAuthorize', function() { describe('preAuthorize', function () {
beforeEach(async function() { beforeEach(async function () {
settings.requireAuthentication = false; settings.requireAuthentication = false;
settings.requireAuthorization = false; settings.requireAuthorization = false;
}); });
it('defers if it returns []', async function() { it('defers if it returns []', async function () {
await agent.get('/').expect(200); await agent.get('/').expect(200);
// Note: The preAuthorize hook always runs even if requireAuthorization is false. // Note: The preAuthorize hook always runs even if requireAuthorization is false.
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1']); assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1']);
}); });
it('bypasses authenticate and authorize hooks when true is returned', async function() { it('bypasses authenticate and authorize hooks when true is returned', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
settings.requireAuthorization = true; settings.requireAuthorization = true;
handlers.preAuthorize[0].innerHandle = () => [true]; handlers.preAuthorize[0].innerHandle = () => [true];
await agent.get('/').expect(200); await agent.get('/').expect(200);
assert.deepEqual(callOrder, ['preAuthorize_0']); assert.deepEqual(callOrder, ['preAuthorize_0']);
}); });
it('bypasses authenticate and authorize hooks when false is returned', async function() { it('bypasses authenticate and authorize hooks when false is returned', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
settings.requireAuthorization = true; settings.requireAuthorization = true;
handlers.preAuthorize[0].innerHandle = () => [false]; handlers.preAuthorize[0].innerHandle = () => [false];
await agent.get('/').expect(403); await agent.get('/').expect(403);
assert.deepEqual(callOrder, ['preAuthorize_0']); assert.deepEqual(callOrder, ['preAuthorize_0']);
}); });
it('bypasses authenticate and authorize hooks for static content, defers', async function() { it('bypasses authenticate and authorize hooks for static content, defers', async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
settings.requireAuthorization = true; settings.requireAuthorization = true;
await agent.get('/static/robots.txt').expect(200); await agent.get('/static/robots.txt').expect(200);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1']); assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1']);
}); });
it('cannot grant access to /admin', async function() { it('cannot grant access to /admin', async function () {
handlers.preAuthorize[0].innerHandle = () => [true]; handlers.preAuthorize[0].innerHandle = () => [true];
await agent.get('/admin/').expect(401); await agent.get('/admin/').expect(401);
// Notes: // Notes:
@ -184,15 +184,17 @@ describe(__filename, function() {
// 'true' entries are ignored for /admin/* requests. // 'true' entries are ignored for /admin/* requests.
// * The authenticate hook always runs for /admin/* requests even if // * The authenticate hook always runs for /admin/* requests even if
// settings.requireAuthentication is false. // settings.requireAuthentication is false.
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', assert.deepEqual(callOrder, ['preAuthorize_0',
'authenticate_0', 'authenticate_1']); 'preAuthorize_1',
'authenticate_0',
'authenticate_1']);
}); });
it('can deny access to /admin', async function() { it('can deny access to /admin', async function () {
handlers.preAuthorize[0].innerHandle = () => [false]; handlers.preAuthorize[0].innerHandle = () => [false];
await agent.get('/admin/').auth('admin', 'admin-password').expect(403); await agent.get('/admin/').auth('admin', 'admin-password').expect(403);
assert.deepEqual(callOrder, ['preAuthorize_0']); assert.deepEqual(callOrder, ['preAuthorize_0']);
}); });
it('runs preAuthzFailure hook when access is denied', async function() { it('runs preAuthzFailure hook when access is denied', async function () {
handlers.preAuthorize[0].innerHandle = () => [false]; handlers.preAuthorize[0].innerHandle = () => [false];
let called = false; let called = false;
plugins.hooks.preAuthzFailure = [{hook_fn: (hookName, {req, res}, cb) => { plugins.hooks.preAuthzFailure = [{hook_fn: (hookName, {req, res}, cb) => {
@ -207,149 +209,177 @@ describe(__filename, function() {
await agent.get('/admin/').auth('admin', 'admin-password').expect(200, 'injected'); await agent.get('/admin/').auth('admin', 'admin-password').expect(200, 'injected');
assert(called); assert(called);
}); });
it('returns 500 if an exception is thrown', async function() { it('returns 500 if an exception is thrown', async function () {
handlers.preAuthorize[0].innerHandle = () => { throw new Error('exception test'); }; handlers.preAuthorize[0].innerHandle = () => { throw new Error('exception test'); };
await agent.get('/').expect(500); await agent.get('/').expect(500);
}); });
}); });
describe('authenticate', function() { describe('authenticate', function () {
beforeEach(async function() { beforeEach(async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
settings.requireAuthorization = false; settings.requireAuthorization = false;
}); });
it('is not called if !requireAuthentication and not /admin/*', async function() { it('is not called if !requireAuthentication and not /admin/*', async function () {
settings.requireAuthentication = false; settings.requireAuthentication = false;
await agent.get('/').expect(200); await agent.get('/').expect(200);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1']); assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1']);
}); });
it('is called if !requireAuthentication and /admin/*', async function() { it('is called if !requireAuthentication and /admin/*', async function () {
settings.requireAuthentication = false; settings.requireAuthentication = false;
await agent.get('/admin/').expect(401); await agent.get('/admin/').expect(401);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', assert.deepEqual(callOrder, ['preAuthorize_0',
'authenticate_0', 'authenticate_1']); 'preAuthorize_1',
'authenticate_0',
'authenticate_1']);
}); });
it('defers if empty list returned', async function() { it('defers if empty list returned', async function () {
await agent.get('/').expect(401); await agent.get('/').expect(401);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', assert.deepEqual(callOrder, ['preAuthorize_0',
'authenticate_0', 'authenticate_1']); 'preAuthorize_1',
'authenticate_0',
'authenticate_1']);
}); });
it('does not defer if return [true], 200', async function() { it('does not defer if return [true], 200', async function () {
handlers.authenticate[0].innerHandle = (req) => { req.session.user = {}; return [true]; }; handlers.authenticate[0].innerHandle = (req) => { req.session.user = {}; return [true]; };
await agent.get('/').expect(200); await agent.get('/').expect(200);
// Note: authenticate_1 was not called because authenticate_0 handled it. // Note: authenticate_1 was not called because authenticate_0 handled it.
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', 'authenticate_0']); assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', 'authenticate_0']);
}); });
it('does not defer if return [false], 401', async function() { it('does not defer if return [false], 401', async function () {
handlers.authenticate[0].innerHandle = (req) => [false]; handlers.authenticate[0].innerHandle = (req) => [false];
await agent.get('/').expect(401); await agent.get('/').expect(401);
// Note: authenticate_1 was not called because authenticate_0 handled it. // Note: authenticate_1 was not called because authenticate_0 handled it.
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', 'authenticate_0']); assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', 'authenticate_0']);
}); });
it('falls back to HTTP basic auth', async function() { it('falls back to HTTP basic auth', async function () {
await agent.get('/').auth('user', 'user-password').expect(200); await agent.get('/').auth('user', 'user-password').expect(200);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', assert.deepEqual(callOrder, ['preAuthorize_0',
'authenticate_0', 'authenticate_1']); 'preAuthorize_1',
'authenticate_0',
'authenticate_1']);
}); });
it('passes settings.users in context', async function() { it('passes settings.users in context', async function () {
handlers.authenticate[0].checkContext = ({users}) => { handlers.authenticate[0].checkContext = ({users}) => {
assert.equal(users, settings.users); assert.equal(users, settings.users);
}; };
await agent.get('/').expect(401); await agent.get('/').expect(401);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', assert.deepEqual(callOrder, ['preAuthorize_0',
'authenticate_0', 'authenticate_1']); 'preAuthorize_1',
'authenticate_0',
'authenticate_1']);
}); });
it('passes user, password in context if provided', async function() { it('passes user, password in context if provided', async function () {
handlers.authenticate[0].checkContext = ({username, password}) => { handlers.authenticate[0].checkContext = ({username, password}) => {
assert.equal(username, 'user'); assert.equal(username, 'user');
assert.equal(password, 'user-password'); assert.equal(password, 'user-password');
}; };
await agent.get('/').auth('user', 'user-password').expect(200); await agent.get('/').auth('user', 'user-password').expect(200);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', assert.deepEqual(callOrder, ['preAuthorize_0',
'authenticate_0', 'authenticate_1']); 'preAuthorize_1',
'authenticate_0',
'authenticate_1']);
}); });
it('does not pass user, password in context if not provided', async function() { it('does not pass user, password in context if not provided', async function () {
handlers.authenticate[0].checkContext = ({username, password}) => { handlers.authenticate[0].checkContext = ({username, password}) => {
assert(username == null); assert(username == null);
assert(password == null); assert(password == null);
}; };
await agent.get('/').expect(401); await agent.get('/').expect(401);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', assert.deepEqual(callOrder, ['preAuthorize_0',
'authenticate_0', 'authenticate_1']); 'preAuthorize_1',
'authenticate_0',
'authenticate_1']);
}); });
it('errors if req.session.user is not created', async function() { it('errors if req.session.user is not created', async function () {
handlers.authenticate[0].innerHandle = () => [true]; handlers.authenticate[0].innerHandle = () => [true];
await agent.get('/').expect(500); await agent.get('/').expect(500);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', 'authenticate_0']); assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', 'authenticate_0']);
}); });
it('returns 500 if an exception is thrown', async function() { it('returns 500 if an exception is thrown', async function () {
handlers.authenticate[0].innerHandle = () => { throw new Error('exception test'); }; handlers.authenticate[0].innerHandle = () => { throw new Error('exception test'); };
await agent.get('/').expect(500); await agent.get('/').expect(500);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', 'authenticate_0']); assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', 'authenticate_0']);
}); });
}); });
describe('authorize', function() { describe('authorize', function () {
beforeEach(async function() { beforeEach(async function () {
settings.requireAuthentication = true; settings.requireAuthentication = true;
settings.requireAuthorization = true; settings.requireAuthorization = true;
}); });
it('is not called if !requireAuthorization (non-/admin)', async function() { it('is not called if !requireAuthorization (non-/admin)', async function () {
settings.requireAuthorization = false; settings.requireAuthorization = false;
await agent.get('/').auth('user', 'user-password').expect(200); await agent.get('/').auth('user', 'user-password').expect(200);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', assert.deepEqual(callOrder, ['preAuthorize_0',
'authenticate_0', 'authenticate_1']); 'preAuthorize_1',
'authenticate_0',
'authenticate_1']);
}); });
it('is not called if !requireAuthorization (/admin)', async function() { it('is not called if !requireAuthorization (/admin)', async function () {
settings.requireAuthorization = false; settings.requireAuthorization = false;
await agent.get('/admin/').auth('admin', 'admin-password').expect(200); await agent.get('/admin/').auth('admin', 'admin-password').expect(200);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', assert.deepEqual(callOrder, ['preAuthorize_0',
'authenticate_0', 'authenticate_1']); 'preAuthorize_1',
'authenticate_0',
'authenticate_1']);
}); });
it('defers if empty list returned', async function() { it('defers if empty list returned', async function () {
await agent.get('/').auth('user', 'user-password').expect(403); await agent.get('/').auth('user', 'user-password').expect(403);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', assert.deepEqual(callOrder, ['preAuthorize_0',
'authenticate_0', 'authenticate_1', 'preAuthorize_1',
'authorize_0', 'authorize_1']); 'authenticate_0',
'authenticate_1',
'authorize_0',
'authorize_1']);
}); });
it('does not defer if return [true], 200', async function() { it('does not defer if return [true], 200', async function () {
handlers.authorize[0].innerHandle = () => [true]; handlers.authorize[0].innerHandle = () => [true];
await agent.get('/').auth('user', 'user-password').expect(200); await agent.get('/').auth('user', 'user-password').expect(200);
// Note: authorize_1 was not called because authorize_0 handled it. // Note: authorize_1 was not called because authorize_0 handled it.
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', assert.deepEqual(callOrder, ['preAuthorize_0',
'authenticate_0', 'authenticate_1', 'preAuthorize_1',
'authenticate_0',
'authenticate_1',
'authorize_0']); 'authorize_0']);
}); });
it('does not defer if return [false], 403', async function() { it('does not defer if return [false], 403', async function () {
handlers.authorize[0].innerHandle = (req) => [false]; handlers.authorize[0].innerHandle = (req) => [false];
await agent.get('/').auth('user', 'user-password').expect(403); await agent.get('/').auth('user', 'user-password').expect(403);
// Note: authorize_1 was not called because authorize_0 handled it. // Note: authorize_1 was not called because authorize_0 handled it.
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', assert.deepEqual(callOrder, ['preAuthorize_0',
'authenticate_0', 'authenticate_1', 'preAuthorize_1',
'authenticate_0',
'authenticate_1',
'authorize_0']); 'authorize_0']);
}); });
it('passes req.path in context', async function() { it('passes req.path in context', async function () {
handlers.authorize[0].checkContext = ({resource}) => { handlers.authorize[0].checkContext = ({resource}) => {
assert.equal(resource, '/'); assert.equal(resource, '/');
}; };
await agent.get('/').auth('user', 'user-password').expect(403); await agent.get('/').auth('user', 'user-password').expect(403);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', assert.deepEqual(callOrder, ['preAuthorize_0',
'authenticate_0', 'authenticate_1', 'preAuthorize_1',
'authorize_0', 'authorize_1']); 'authenticate_0',
'authenticate_1',
'authorize_0',
'authorize_1']);
}); });
it('returns 500 if an exception is thrown', async function() { it('returns 500 if an exception is thrown', async function () {
handlers.authorize[0].innerHandle = () => { throw new Error('exception test'); }; handlers.authorize[0].innerHandle = () => { throw new Error('exception test'); };
await agent.get('/').auth('user', 'user-password').expect(500); await agent.get('/').auth('user', 'user-password').expect(500);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', assert.deepEqual(callOrder, ['preAuthorize_0',
'authenticate_0', 'authenticate_1', 'preAuthorize_1',
'authenticate_0',
'authenticate_1',
'authorize_0']); 'authorize_0']);
}); });
}); });
}); });
describe('webaccess: authnFailure, authzFailure, authFailure hooks', function() { describe('webaccess: authnFailure, authzFailure, authFailure hooks', function () {
const Handler = class { const Handler = class {
constructor(hookName) { constructor(hookName) {
this.hookName = hookName; this.hookName = hookName;
@ -372,7 +402,7 @@ describe(__filename, function() {
}; };
const handlers = {}; const handlers = {};
beforeEach(function() { beforeEach(function () {
failHookNames.forEach((hookName) => { failHookNames.forEach((hookName) => {
const handler = new Handler(hookName); const handler = new Handler(hookName);
handlers[hookName] = handler; handlers[hookName] = handler;
@ -383,61 +413,61 @@ describe(__filename, function() {
}); });
// authn failure tests // authn failure tests
it('authn fail, no hooks handle -> 401', async function() { it('authn fail, no hooks handle -> 401', async function () {
await agent.get('/').expect(401); await agent.get('/').expect(401);
assert(handlers['authnFailure'].called); assert(handlers.authnFailure.called);
assert(!handlers['authzFailure'].called); assert(!handlers.authzFailure.called);
assert(handlers['authFailure'].called); assert(handlers.authFailure.called);
}); });
it('authn fail, authnFailure handles', async function() { it('authn fail, authnFailure handles', async function () {
handlers['authnFailure'].shouldHandle = true; handlers.authnFailure.shouldHandle = true;
await agent.get('/').expect(200, 'authnFailure'); await agent.get('/').expect(200, 'authnFailure');
assert(handlers['authnFailure'].called); assert(handlers.authnFailure.called);
assert(!handlers['authzFailure'].called); assert(!handlers.authzFailure.called);
assert(!handlers['authFailure'].called); assert(!handlers.authFailure.called);
}); });
it('authn fail, authFailure handles', async function() { it('authn fail, authFailure handles', async function () {
handlers['authFailure'].shouldHandle = true; handlers.authFailure.shouldHandle = true;
await agent.get('/').expect(200, 'authFailure'); await agent.get('/').expect(200, 'authFailure');
assert(handlers['authnFailure'].called); assert(handlers.authnFailure.called);
assert(!handlers['authzFailure'].called); assert(!handlers.authzFailure.called);
assert(handlers['authFailure'].called); assert(handlers.authFailure.called);
}); });
it('authnFailure trumps authFailure', async function() { it('authnFailure trumps authFailure', async function () {
handlers['authnFailure'].shouldHandle = true; handlers.authnFailure.shouldHandle = true;
handlers['authFailure'].shouldHandle = true; handlers.authFailure.shouldHandle = true;
await agent.get('/').expect(200, 'authnFailure'); await agent.get('/').expect(200, 'authnFailure');
assert(handlers['authnFailure'].called); assert(handlers.authnFailure.called);
assert(!handlers['authFailure'].called); assert(!handlers.authFailure.called);
}); });
// authz failure tests // authz failure tests
it('authz fail, no hooks handle -> 403', async function() { it('authz fail, no hooks handle -> 403', async function () {
await agent.get('/').auth('user', 'user-password').expect(403); await agent.get('/').auth('user', 'user-password').expect(403);
assert(!handlers['authnFailure'].called); assert(!handlers.authnFailure.called);
assert(handlers['authzFailure'].called); assert(handlers.authzFailure.called);
assert(handlers['authFailure'].called); assert(handlers.authFailure.called);
}); });
it('authz fail, authzFailure handles', async function() { it('authz fail, authzFailure handles', async function () {
handlers['authzFailure'].shouldHandle = true; handlers.authzFailure.shouldHandle = true;
await agent.get('/').auth('user', 'user-password').expect(200, 'authzFailure'); await agent.get('/').auth('user', 'user-password').expect(200, 'authzFailure');
assert(!handlers['authnFailure'].called); assert(!handlers.authnFailure.called);
assert(handlers['authzFailure'].called); assert(handlers.authzFailure.called);
assert(!handlers['authFailure'].called); assert(!handlers.authFailure.called);
}); });
it('authz fail, authFailure handles', async function() { it('authz fail, authFailure handles', async function () {
handlers['authFailure'].shouldHandle = true; handlers.authFailure.shouldHandle = true;
await agent.get('/').auth('user', 'user-password').expect(200, 'authFailure'); await agent.get('/').auth('user', 'user-password').expect(200, 'authFailure');
assert(!handlers['authnFailure'].called); assert(!handlers.authnFailure.called);
assert(handlers['authzFailure'].called); assert(handlers.authzFailure.called);
assert(handlers['authFailure'].called); assert(handlers.authFailure.called);
}); });
it('authzFailure trumps authFailure', async function() { it('authzFailure trumps authFailure', async function () {
handlers['authzFailure'].shouldHandle = true; handlers.authzFailure.shouldHandle = true;
handlers['authFailure'].shouldHandle = true; handlers.authFailure.shouldHandle = true;
await agent.get('/').auth('user', 'user-password').expect(200, 'authzFailure'); await agent.get('/').auth('user', 'user-password').expect(200, 'authzFailure');
assert(handlers['authzFailure'].called); assert(handlers.authzFailure.called);
assert(!handlers['authFailure'].called); assert(!handlers.authFailure.called);
}); });
}); });
}); });

View file

@ -12,16 +12,16 @@
* back to a default) * back to a default)
*/ */
var jsonminify = require(__dirname+"/../../src/node_modules/jsonminify"); const jsonminify = require(`${__dirname}/../../src/node_modules/jsonminify`);
const fs = require('fs'); const fs = require('fs');
function loadSettings(){ function loadSettings() {
var settingsStr = fs.readFileSync(__dirname+"/../../settings.json.docker").toString(); let settingsStr = fs.readFileSync(`${__dirname}/../../settings.json.docker`).toString();
// try to parse the settings // try to parse the settings
try { try {
if(settingsStr) { if (settingsStr) {
settingsStr = jsonminify(settingsStr).replace(",]","]").replace(",}","}"); settingsStr = jsonminify(settingsStr).replace(',]', ']').replace(',}', '}');
var settings = JSON.parse(settingsStr); const settings = JSON.parse(settingsStr);
// custom settings for running in a container // custom settings for running in a container
settings.ip = 'localhost'; settings.ip = 'localhost';
@ -29,8 +29,8 @@ function loadSettings(){
return settings; return settings;
} }
}catch(e){ } catch (e) {
console.error("whoops something is bad with settings"); console.error('whoops something is bad with settings');
} }
} }

View file

@ -5,34 +5,34 @@
* TODO: unify those two files, and merge in a single one. * TODO: unify those two files, and merge in a single one.
*/ */
const supertest = require(__dirname+'/../../../../src/node_modules/supertest'); const supertest = require(`${__dirname}/../../../../src/node_modules/supertest`);
const settings = require(__dirname+'/../../loadSettings').loadSettings(); const settings = require(`${__dirname}/../../loadSettings`).loadSettings();
const api = supertest('http://'+settings.ip+":"+settings.port); const api = supertest(`http://${settings.ip}:${settings.port}`);
var apiVersion = 1; const apiVersion = 1;
describe('Connectivity', function(){ describe('Connectivity', function () {
it('can connect', function(done) { it('can connect', function (done) {
api.get('/api/') api.get('/api/')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200, done) .expect(200, done);
}); });
}) });
describe('API Versioning', function(){ describe('API Versioning', function () {
it('finds the version tag', function(done) { it('finds the version tag', function (done) {
api.get('/api/') api.get('/api/')
.expect(function(res){ .expect((res) => {
if (!res.body.currentVersion) throw new Error("No version set in API"); if (!res.body.currentVersion) throw new Error('No version set in API');
return; return;
}) })
.expect(200, done) .expect(200, done);
}); });
}) });
describe('Permission', function(){ describe('Permission', function () {
it('errors with invalid APIKey', function(done) { it('errors with invalid APIKey', function (done) {
api.get('/api/'+apiVersion+'/createPad?apikey=wrong_password&padID=test') api.get(`/api/${apiVersion}/createPad?apikey=wrong_password&padID=test`)
.expect(401, done) .expect(401, done);
}); });
}) });

View file

@ -1,65 +1,65 @@
var helper = {}; var helper = {};
(function(){ (function () {
var $iframe, jsLibraries = {}; let $iframe; const
jsLibraries = {};
helper.init = function(cb){ helper.init = function (cb) {
$.get('/static/js/jquery.js').done(function(code){ $.get('/static/js/jquery.js').done((code) => {
// make sure we don't override existing jquery // make sure we don't override existing jquery
jsLibraries["jquery"] = "if(typeof $ === 'undefined') {\n" + code + "\n}"; jsLibraries.jquery = `if(typeof $ === 'undefined') {\n${code}\n}`;
$.get('/tests/frontend/lib/sendkeys.js').done(function(code){ $.get('/tests/frontend/lib/sendkeys.js').done((code) => {
jsLibraries["sendkeys"] = code; jsLibraries.sendkeys = code;
cb(); cb();
}); });
}); });
} };
helper.randomString = function randomString(len) { helper.randomString = function randomString(len) {
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
var randomstring = ''; let randomstring = '';
for (var i = 0; i < len; i++) for (let i = 0; i < len; i++) {
{ const rnum = Math.floor(Math.random() * chars.length);
var rnum = Math.floor(Math.random() * chars.length);
randomstring += chars.substring(rnum, rnum + 1); randomstring += chars.substring(rnum, rnum + 1);
} }
return randomstring; return randomstring;
} };
var getFrameJQuery = function($iframe){ const getFrameJQuery = function ($iframe) {
/* /*
I tried over 9000 ways to inject javascript into iframes. I tried over 9000 ways to inject javascript into iframes.
This is the only way I found that worked in IE 7+8+9, FF and Chrome This is the only way I found that worked in IE 7+8+9, FF and Chrome
*/ */
var win = $iframe[0].contentWindow; const win = $iframe[0].contentWindow;
var doc = win.document; const doc = win.document;
//IE 8+9 Hack to make eval appear // IE 8+9 Hack to make eval appear
//http://stackoverflow.com/questions/2720444/why-does-this-window-object-not-have-the-eval-function // http://stackoverflow.com/questions/2720444/why-does-this-window-object-not-have-the-eval-function
win.execScript && win.execScript("null"); win.execScript && win.execScript('null');
win.eval(jsLibraries["jquery"]); win.eval(jsLibraries.jquery);
win.eval(jsLibraries["sendkeys"]); win.eval(jsLibraries.sendkeys);
win.$.window = win; win.$.window = win;
win.$.document = doc; win.$.document = doc;
return win.$; return win.$;
} };
helper.clearSessionCookies = function(){ helper.clearSessionCookies = function () {
// Expire cookies, so author and language are changed after reloading the pad. // 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 // 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 = '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=/'; 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 // Can only happen when the iframe exists, so we're doing it separately from other cookies
helper.clearPadPrefCookie = function(){ helper.clearPadPrefCookie = function () {
helper.padChrome$.document.cookie = 'prefsHttp=;expires=Thu, 01 Jan 1970 00:00:00 GMT'; helper.padChrome$.document.cookie = 'prefsHttp=;expires=Thu, 01 Jan 1970 00:00:00 GMT';
} };
// Overwrite all prefs in pad cookie. Assumes http, not https. // Overwrite all prefs in pad cookie. Assumes http, not https.
// //
@ -67,65 +67,64 @@ var helper = {};
// seem to have independent cookies, UNLESS we put path=/ here (which we don't). // 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 // I don't fully understand it, but this function seems to properly simulate
// padCookie.setPref in the client code // padCookie.setPref in the client code
helper.setPadPrefCookie = function(prefs){ helper.setPadPrefCookie = function (prefs) {
helper.padChrome$.document.cookie = ("prefsHttp=" + escape(JSON.stringify(prefs)) + ";expires=Thu, 01 Jan 3000 00:00:00 GMT"); 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 // Functionality for knowing what key event type is required for tests
var evtType = "keydown"; let evtType = 'keydown';
// if it's IE require keypress // if it's IE require keypress
if(window.navigator.userAgent.indexOf("MSIE") > -1){ if (window.navigator.userAgent.indexOf('MSIE') > -1) {
evtType = "keypress"; evtType = 'keypress';
} }
// Edge also requires keypress. // Edge also requires keypress.
if(window.navigator.userAgent.indexOf("Edge") > -1){ if (window.navigator.userAgent.indexOf('Edge') > -1) {
evtType = "keypress"; evtType = 'keypress';
} }
// Opera also requires keypress. // Opera also requires keypress.
if(window.navigator.userAgent.indexOf("OPR") > -1){ if (window.navigator.userAgent.indexOf('OPR') > -1) {
evtType = "keypress"; evtType = 'keypress';
} }
helper.evtType = evtType; helper.evtType = evtType;
// @todo needs fixing asap // @todo needs fixing asap
// newPad occasionally timeouts, might be a problem with ready/onload code during page setup // newPad occasionally timeouts, might be a problem with ready/onload code during page setup
// This ensures that tests run regardless of this problem // This ensures that tests run regardless of this problem
helper.retry = 0 helper.retry = 0;
helper.newPad = function(cb, padName){ helper.newPad = function (cb, padName) {
//build opts object // build opts object
var opts = {clearCookies: true} let opts = {clearCookies: true};
if(typeof cb === 'function'){ if (typeof cb === 'function') {
opts.cb = cb opts.cb = cb;
} else { } else {
opts = _.defaults(cb, opts); opts = _.defaults(cb, opts);
} }
// if opts.params is set we manipulate the URL to include URL parameters IE ?foo=Bah. // if opts.params is set we manipulate the URL to include URL parameters IE ?foo=Bah.
if(opts.params){ if (opts.params) {
var encodedParams = "?" + $.param(opts.params); var encodedParams = `?${$.param(opts.params)}`;
} }
//clear cookies // clear cookies
if(opts.clearCookies){ if (opts.clearCookies) {
helper.clearSessionCookies(); helper.clearSessionCookies();
} }
if(!padName) if (!padName) padName = `FRONTEND_TEST_${helper.randomString(20)}`;
padName = "FRONTEND_TEST_" + helper.randomString(20); $iframe = $(`<iframe src='/p/${padName}${encodedParams || ''}'></iframe>`);
$iframe = $("<iframe src='/p/" + padName + (encodedParams || '') + "'></iframe>");
// needed for retry // needed for retry
let origPadName = padName; const origPadName = padName;
//clean up inner iframe references // clean up inner iframe references
helper.padChrome$ = helper.padOuter$ = helper.padInner$ = null; helper.padChrome$ = helper.padOuter$ = helper.padInner$ = null;
//remove old iframe // remove old iframe
$("#iframe-container iframe").remove(); $('#iframe-container iframe').remove();
//set new iframe // set new iframe
$("#iframe-container").append($iframe); $('#iframe-container').append($iframe);
$iframe.one('load', function(){ $iframe.one('load', () => {
helper.padChrome$ = getFrameJQuery($('#iframe-container iframe')); helper.padChrome$ = getFrameJQuery($('#iframe-container iframe'));
if (opts.clearCookies) { if (opts.clearCookies) {
helper.clearPadPrefCookie(); helper.clearPadPrefCookie();
@ -133,13 +132,11 @@ var helper = {};
if (opts.padPrefs) { if (opts.padPrefs) {
helper.setPadPrefCookie(opts.padPrefs); helper.setPadPrefCookie(opts.padPrefs);
} }
helper.waitFor(function(){ helper.waitFor(() => !$iframe.contents().find('#editorloadingbox').is(':visible'), 10000).done(() => {
return !$iframe.contents().find("#editorloadingbox").is(":visible");
}, 10000).done(function(){
helper.padOuter$ = getFrameJQuery(helper.padChrome$('iframe[name="ace_outer"]')); helper.padOuter$ = getFrameJQuery(helper.padChrome$('iframe[name="ace_outer"]'));
helper.padInner$ = getFrameJQuery( helper.padOuter$('iframe[name="ace_inner"]')); helper.padInner$ = getFrameJQuery(helper.padOuter$('iframe[name="ace_inner"]'));
//disable all animations, this makes tests faster and easier // disable all animations, this makes tests faster and easier
helper.padChrome$.fx.off = true; helper.padChrome$.fx.off = true;
helper.padOuter$.fx.off = true; helper.padOuter$.fx.off = true;
helper.padInner$.fx.off = true; helper.padInner$.fx.off = true;
@ -165,23 +162,23 @@ var helper = {};
// listen for server messages // listen for server messages
helper.spyOnSocketIO(); helper.spyOnSocketIO();
opts.cb(); opts.cb();
}).fail(function(){ }).fail(() => {
if (helper.retry > 3) { if (helper.retry > 3) {
throw new Error("Pad never loaded"); throw new Error('Pad never loaded');
} }
helper.retry++; helper.retry++;
helper.newPad(cb,origPadName); helper.newPad(cb, origPadName);
}); });
}); });
return padName; return padName;
} };
helper.waitFor = function(conditionFunc, timeoutTime = 1900, intervalTime = 10) { helper.waitFor = function (conditionFunc, timeoutTime = 1900, intervalTime = 10) {
var deferred = $.Deferred(); const deferred = $.Deferred();
const _fail = deferred.fail.bind(deferred); const _fail = deferred.fail.bind(deferred);
var listenForFail = false; let listenForFail = false;
deferred.fail = (...args) => { deferred.fail = (...args) => {
listenForFail = true; listenForFail = true;
return _fail(...args); return _fail(...args);
@ -202,10 +199,10 @@ var helper = {};
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
clearInterval(intervalCheck); clearInterval(intervalCheck);
var error = new Error("wait for condition never became true " + conditionFunc.toString()); const error = new Error(`wait for condition never became true ${conditionFunc.toString()}`);
deferred.reject(error); deferred.reject(error);
if(!listenForFail){ if (!listenForFail) {
throw error; throw error;
} }
}, timeoutTime); }, timeoutTime);
@ -222,56 +219,56 @@ var helper = {};
* @returns {Promise} * @returns {Promise}
* *
*/ */
helper.waitForPromise = async function(...args) { helper.waitForPromise = async function (...args) {
// Note: waitFor() has a strange API: On timeout it rejects, but it also throws an uncatchable // Note: waitFor() has a strange API: On timeout it rejects, but it also throws an uncatchable
// exception unless .fail() has been called. That uncatchable exception is disabled here by // exception unless .fail() has been called. That uncatchable exception is disabled here by
// passing a no-op function to .fail(). // passing a no-op function to .fail().
return await this.waitFor(...args).fail(() => {}); return await this.waitFor(...args).fail(() => {});
}; };
helper.selectLines = function($startLine, $endLine, startOffset, endOffset){ helper.selectLines = function ($startLine, $endLine, startOffset, endOffset) {
// if no offset is provided, use beginning of start line and end of end line // if no offset is provided, use beginning of start line and end of end line
startOffset = startOffset || 0; startOffset = startOffset || 0;
endOffset = endOffset === undefined ? $endLine.text().length : endOffset; endOffset = endOffset === undefined ? $endLine.text().length : endOffset;
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var selection = inner$.document.getSelection(); const selection = inner$.document.getSelection();
var range = selection.getRangeAt(0); const range = selection.getRangeAt(0);
var start = getTextNodeAndOffsetOf($startLine, startOffset); const start = getTextNodeAndOffsetOf($startLine, startOffset);
var end = getTextNodeAndOffsetOf($endLine, endOffset); const end = getTextNodeAndOffsetOf($endLine, endOffset);
range.setStart(start.node, start.offset); range.setStart(start.node, start.offset);
range.setEnd(end.node, end.offset); range.setEnd(end.node, end.offset);
selection.removeAllRanges(); selection.removeAllRanges();
selection.addRange(range); selection.addRange(range);
} };
var getTextNodeAndOffsetOf = function($targetLine, targetOffsetAtLine){ var getTextNodeAndOffsetOf = function ($targetLine, targetOffsetAtLine) {
var $textNodes = $targetLine.find('*').contents().filter(function(){ const $textNodes = $targetLine.find('*').contents().filter(function () {
return this.nodeType === Node.TEXT_NODE; return this.nodeType === Node.TEXT_NODE;
}); });
// search node where targetOffsetAtLine is reached, and its 'inner offset' // search node where targetOffsetAtLine is reached, and its 'inner offset'
var textNodeWhereOffsetIs = null; let textNodeWhereOffsetIs = null;
var offsetBeforeTextNode = 0; let offsetBeforeTextNode = 0;
var offsetInsideTextNode = 0; let offsetInsideTextNode = 0;
$textNodes.each(function(index, element){ $textNodes.each((index, element) => {
var elementTotalOffset = element.textContent.length; const elementTotalOffset = element.textContent.length;
textNodeWhereOffsetIs = element; textNodeWhereOffsetIs = element;
offsetInsideTextNode = targetOffsetAtLine - offsetBeforeTextNode; offsetInsideTextNode = targetOffsetAtLine - offsetBeforeTextNode;
var foundTextNode = offsetBeforeTextNode + elementTotalOffset >= targetOffsetAtLine; const foundTextNode = offsetBeforeTextNode + elementTotalOffset >= targetOffsetAtLine;
if (foundTextNode){ if (foundTextNode) {
return false; //stop .each by returning false return false; // stop .each by returning false
} }
offsetBeforeTextNode += elementTotalOffset; offsetBeforeTextNode += elementTotalOffset;
}); });
// edge cases // edge cases
if (textNodeWhereOffsetIs === null){ if (textNodeWhereOffsetIs === null) {
// there was no text node inside $targetLine, so it is an empty line (<br>). // there was no text node inside $targetLine, so it is an empty line (<br>).
// Use beginning of line // Use beginning of line
textNodeWhereOffsetIs = $targetLine.get(0); textNodeWhereOffsetIs = $targetLine.get(0);
@ -279,16 +276,16 @@ var helper = {};
} }
// avoid errors if provided targetOffsetAtLine is higher than line offset (maxOffset). // avoid errors if provided targetOffsetAtLine is higher than line offset (maxOffset).
// Use max allowed instead // Use max allowed instead
var maxOffset = textNodeWhereOffsetIs.textContent.length; const maxOffset = textNodeWhereOffsetIs.textContent.length;
offsetInsideTextNode = Math.min(offsetInsideTextNode, maxOffset); offsetInsideTextNode = Math.min(offsetInsideTextNode, maxOffset);
return { return {
node: textNodeWhereOffsetIs, node: textNodeWhereOffsetIs,
offset: offsetInsideTextNode, offset: offsetInsideTextNode,
}; };
} };
/* Ensure console.log doesn't blow up in IE, ugly but ok for a test framework imho*/ /* Ensure console.log doesn't blow up in IE, ugly but ok for a test framework imho*/
window.console = window.console || {}; window.console = window.console || {};
window.console.log = window.console.log || function(){} window.console.log = window.console.log || function () {};
})() })();

View file

@ -2,22 +2,19 @@
* Spys on socket.io messages and saves them into several arrays * Spys on socket.io messages and saves them into several arrays
* that are visible in tests * that are visible in tests
*/ */
helper.spyOnSocketIO = function (){ helper.spyOnSocketIO = function () {
helper.contentWindow().pad.socket.on('message', function(msg){ helper.contentWindow().pad.socket.on('message', (msg) => {
if (msg.type == "COLLABROOM") { if (msg.type == 'COLLABROOM') {
if (msg.data.type == 'ACCEPT_COMMIT') { if (msg.data.type == 'ACCEPT_COMMIT') {
helper.commits.push(msg); helper.commits.push(msg);
} } else if (msg.data.type == 'USER_NEWINFO') {
else if (msg.data.type == 'USER_NEWINFO') { helper.userInfos.push(msg);
helper.userInfos.push(msg) } else if (msg.data.type == 'CHAT_MESSAGE') {
} helper.chatMessages.push(msg);
else if (msg.data.type == 'CHAT_MESSAGE') {
helper.chatMessages.push(msg)
} }
} }
}) });
} };
/** /**
* Makes an edit via `sendkeys` to the position of the caret and ensures ACCEPT_COMMIT * Makes an edit via `sendkeys` to the position of the caret and ensures ACCEPT_COMMIT
@ -31,14 +28,12 @@ helper.spyOnSocketIO = function (){
* @todo needs to support writing to a specified caret position * @todo needs to support writing to a specified caret position
* *
*/ */
helper.edit = async function(message, line){ helper.edit = async function (message, line) {
let editsNum = helper.commits.length; const editsNum = helper.commits.length;
line = line ? line - 1 : 0; line = line ? line - 1 : 0;
helper.linesDiv()[line].sendkeys(message); helper.linesDiv()[line].sendkeys(message);
return helper.waitForPromise(function(){ return helper.waitForPromise(() => editsNum + 1 === helper.commits.length);
return editsNum + 1 === helper.commits.length; };
})
}
/** /**
* The pad text as an array of divs * The pad text as an array of divs
@ -48,11 +43,11 @@ helper.edit = async function(message, line){
* *
* @returns {Array.<HTMLElement>} array of divs * @returns {Array.<HTMLElement>} array of divs
*/ */
helper.linesDiv = function(){ helper.linesDiv = function () {
return helper.padInner$('.ace-line').map(function(){ return helper.padInner$('.ace-line').map(function () {
return $(this) return $(this);
}).get() }).get();
} };
/** /**
* The pad text as an array of lines * The pad text as an array of lines
@ -60,18 +55,18 @@ helper.linesDiv = function(){
* *
* @returns {Array.<string>} lines of text * @returns {Array.<string>} lines of text
*/ */
helper.textLines = function(){ helper.textLines = function () {
return helper.linesDiv().map((div) => div.text()); return helper.linesDiv().map((div) => div.text());
} };
/** /**
* The default pad text transmitted via `clientVars` * The default pad text transmitted via `clientVars`
* *
* @returns {string} * @returns {string}
*/ */
helper.defaultText = function(){ helper.defaultText = function () {
return helper.padChrome$.window.clientVars.collab_client_vars.initialAttributedText.text; return helper.padChrome$.window.clientVars.collab_client_vars.initialAttributedText.text;
} };
/** /**
* Sends a chat `message` via `sendKeys` * Sends a chat `message` via `sendKeys`
@ -87,25 +82,23 @@ helper.defaultText = function(){
* @param {string} message the chat message to be sent * @param {string} message the chat message to be sent
* @returns {Promise} * @returns {Promise}
*/ */
helper.sendChatMessage = function(message){ helper.sendChatMessage = function (message) {
let noOfChatMessages = helper.chatMessages.length; const noOfChatMessages = helper.chatMessages.length;
helper.padChrome$("#chatinput").sendkeys(message) helper.padChrome$('#chatinput').sendkeys(message);
return helper.waitForPromise(function(){ return helper.waitForPromise(() => noOfChatMessages + 1 === helper.chatMessages.length);
return noOfChatMessages + 1 === helper.chatMessages.length; };
})
}
/** /**
* Opens the settings menu if its hidden via button * Opens the settings menu if its hidden via button
* *
* @returns {Promise} * @returns {Promise}
*/ */
helper.showSettings = function() { helper.showSettings = function () {
if(!helper.isSettingsShown()){ if (!helper.isSettingsShown()) {
helper.settingsButton().click() helper.settingsButton().click();
return helper.waitForPromise(function(){return helper.isSettingsShown(); },2000); return helper.waitForPromise(() => helper.isSettingsShown(), 2000);
} }
} };
/** /**
* Hide the settings menu if its open via button * Hide the settings menu if its open via button
@ -113,12 +106,12 @@ helper.showSettings = function() {
* @returns {Promise} * @returns {Promise}
* @todo untested * @todo untested
*/ */
helper.hideSettings = function() { helper.hideSettings = function () {
if(helper.isSettingsShown()){ if (helper.isSettingsShown()) {
helper.settingsButton().click() helper.settingsButton().click();
return helper.waitForPromise(function(){return !helper.isSettingsShown(); },2000); return helper.waitForPromise(() => !helper.isSettingsShown(), 2000);
} }
} };
/** /**
* Makes the chat window sticky via settings menu if the settings menu is * Makes the chat window sticky via settings menu if the settings menu is
@ -126,15 +119,13 @@ helper.hideSettings = function() {
* *
* @returns {Promise} * @returns {Promise}
*/ */
helper.enableStickyChatviaSettings = function() { helper.enableStickyChatviaSettings = function () {
var stickyChat = helper.padChrome$('#options-stickychat'); const stickyChat = helper.padChrome$('#options-stickychat');
if(helper.isSettingsShown() && !stickyChat.is(':checked')) { if (helper.isSettingsShown() && !stickyChat.is(':checked')) {
stickyChat.click(); stickyChat.click();
return helper.waitForPromise(function(){ return helper.waitForPromise(() => helper.isChatboxSticky(), 2000);
return helper.isChatboxSticky();
},2000);
} }
} };
/** /**
* Unsticks the chat window via settings menu if the settings menu is open * Unsticks the chat window via settings menu if the settings menu is open
@ -142,13 +133,13 @@ helper.enableStickyChatviaSettings = function() {
* *
* @returns {Promise} * @returns {Promise}
*/ */
helper.disableStickyChatviaSettings = function() { helper.disableStickyChatviaSettings = function () {
var stickyChat = helper.padChrome$('#options-stickychat'); const stickyChat = helper.padChrome$('#options-stickychat');
if(helper.isSettingsShown() && stickyChat.is(':checked')) { if (helper.isSettingsShown() && stickyChat.is(':checked')) {
stickyChat.click(); stickyChat.click();
return helper.waitForPromise(function(){return !helper.isChatboxSticky()},2000); return helper.waitForPromise(() => !helper.isChatboxSticky(), 2000);
} }
} };
/** /**
* Makes the chat window sticky via an icon on the top right of the chat * Makes the chat window sticky via an icon on the top right of the chat
@ -156,13 +147,13 @@ helper.disableStickyChatviaSettings = function() {
* *
* @returns {Promise} * @returns {Promise}
*/ */
helper.enableStickyChatviaIcon = function() { helper.enableStickyChatviaIcon = function () {
var stickyChat = helper.padChrome$('#titlesticky'); const stickyChat = helper.padChrome$('#titlesticky');
if(helper.isChatboxShown() && !helper.isChatboxSticky()) { if (helper.isChatboxShown() && !helper.isChatboxSticky()) {
stickyChat.click(); stickyChat.click();
return helper.waitForPromise(function(){return helper.isChatboxSticky()},2000); return helper.waitForPromise(() => helper.isChatboxSticky(), 2000);
} }
} };
/** /**
* Disables the stickyness of the chat window via an icon on the * Disables the stickyness of the chat window via an icon on the
@ -170,12 +161,12 @@ helper.enableStickyChatviaIcon = function() {
* *
* @returns {Promise} * @returns {Promise}
*/ */
helper.disableStickyChatviaIcon = function() { helper.disableStickyChatviaIcon = function () {
if(helper.isChatboxShown() && helper.isChatboxSticky()) { if (helper.isChatboxShown() && helper.isChatboxSticky()) {
helper.titlecross().click() helper.titlecross().click();
return helper.waitForPromise(function(){return !helper.isChatboxSticky()},2000); return helper.waitForPromise(() => !helper.isChatboxSticky(), 2000);
} }
} };
/** /**
* Sets the src-attribute of the main iframe to the timeslider * Sets the src-attribute of the main iframe to the timeslider
@ -189,15 +180,14 @@ helper.disableStickyChatviaIcon = function() {
* @todo for some reason this does only work the first time, you cannot * @todo for some reason this does only work the first time, you cannot
* goto rev 0 and then via the same method to rev 5. Use buttons instead * goto rev 0 and then via the same method to rev 5. Use buttons instead
*/ */
helper.gotoTimeslider = function(revision){ helper.gotoTimeslider = function (revision) {
revision = Number.isInteger(revision) ? '#'+revision : ''; revision = Number.isInteger(revision) ? `#${revision}` : '';
var iframe = $('#iframe-container iframe'); const iframe = $('#iframe-container iframe');
iframe.attr('src', iframe.attr('src')+'/timeslider' + revision); iframe.attr('src', `${iframe.attr('src')}/timeslider${revision}`);
return helper.waitForPromise(function(){ return helper.waitForPromise(() => helper.timesliderTimerTime() &&
return helper.timesliderTimerTime() !Number.isNaN(new Date(helper.timesliderTimerTime()).getTime()), 10000);
&& !Number.isNaN(new Date(helper.timesliderTimerTime()).getTime()) },10000); };
}
/** /**
* Clicks in the timeslider at a specific offset * Clicks in the timeslider at a specific offset
@ -206,24 +196,24 @@ helper.gotoTimeslider = function(revision){
* @todo no mousemove test * @todo no mousemove test
* @param {number} X coordinate * @param {number} X coordinate
*/ */
helper.sliderClick = function(X){ helper.sliderClick = function (X) {
let sliderBar = helper.sliderBar() const sliderBar = helper.sliderBar();
let edown = new jQuery.Event('mousedown'); const edown = new jQuery.Event('mousedown');
let eup = new jQuery.Event('mouseup'); const eup = new jQuery.Event('mouseup');
edown.clientX = eup.clientX = X; edown.clientX = eup.clientX = X;
edown.clientY = eup.clientY = sliderBar.offset().top; edown.clientY = eup.clientY = sliderBar.offset().top;
sliderBar.trigger(edown); sliderBar.trigger(edown);
sliderBar.trigger(eup); sliderBar.trigger(eup);
} };
/** /**
* The timeslider text as an array of lines * The timeslider text as an array of lines
* *
* @returns {Array.<string>} lines of text * @returns {Array.<string>} lines of text
*/ */
helper.timesliderTextLines = function(){ helper.timesliderTextLines = function () {
return helper.contentWindow().$('.ace-line').map(function(){ return helper.contentWindow().$('.ace-line').map(function () {
return $(this).text() return $(this).text();
}).get() }).get();
} };

View file

@ -3,9 +3,9 @@
* *
* @returns {HTMLElement} contentWindow * @returns {HTMLElement} contentWindow
*/ */
helper.contentWindow = function(){ helper.contentWindow = function () {
return $('#iframe-container iframe')[0].contentWindow; return $('#iframe-container iframe')[0].contentWindow;
} };
/** /**
* Opens the chat unless it is already open via an * Opens the chat unless it is already open via an
@ -13,117 +13,118 @@ helper.contentWindow = function(){
* *
* @returns {Promise} * @returns {Promise}
*/ */
helper.showChat = function(){ helper.showChat = function () {
var chaticon = helper.chatIcon(); const chaticon = helper.chatIcon();
if(chaticon.hasClass('visible')) { if (chaticon.hasClass('visible')) {
chaticon.click() chaticon.click();
return helper.waitForPromise(function(){return !chaticon.hasClass('visible'); },2000) return helper.waitForPromise(() => !chaticon.hasClass('visible'), 2000);
} }
} };
/** /**
* Closes the chat window if it is shown and not sticky * Closes the chat window if it is shown and not sticky
* *
* @returns {Promise} * @returns {Promise}
*/ */
helper.hideChat = function(){ helper.hideChat = function () {
if(helper.isChatboxShown() && !helper.isChatboxSticky()) { if (helper.isChatboxShown() && !helper.isChatboxSticky()) {
helper.titlecross().click() helper.titlecross().click();
return helper.waitForPromise(function(){return !helper.isChatboxShown(); },2000); return helper.waitForPromise(() => !helper.isChatboxShown(), 2000);
} }
} };
/** /**
* Gets the chat icon from the bottom right of the page * Gets the chat icon from the bottom right of the page
* *
* @returns {HTMLElement} the chat icon * @returns {HTMLElement} the chat icon
*/ */
helper.chatIcon = function(){return helper.padChrome$('#chaticon')} helper.chatIcon = function () { return helper.padChrome$('#chaticon'); };
/** /**
* The chat messages from the UI * The chat messages from the UI
* *
* @returns {Array.<HTMLElement>} * @returns {Array.<HTMLElement>}
*/ */
helper.chatTextParagraphs = function(){return helper.padChrome$('#chattext').children("p")} helper.chatTextParagraphs = function () { return helper.padChrome$('#chattext').children('p'); };
/** /**
* Returns true if the chat box is sticky * Returns true if the chat box is sticky
* *
* @returns {boolean} stickyness of the chat box * @returns {boolean} stickyness of the chat box
*/ */
helper.isChatboxSticky = function() { helper.isChatboxSticky = function () {
return helper.padChrome$('#chatbox').hasClass('stickyChat'); return helper.padChrome$('#chatbox').hasClass('stickyChat');
} };
/** /**
* Returns true if the chat box is shown * Returns true if the chat box is shown
* *
* @returns {boolean} visibility of the chat box * @returns {boolean} visibility of the chat box
*/ */
helper.isChatboxShown = function() { helper.isChatboxShown = function () {
return helper.padChrome$('#chatbox').hasClass('visible'); return helper.padChrome$('#chatbox').hasClass('visible');
} };
/** /**
* Gets the settings menu * Gets the settings menu
* *
* @returns {HTMLElement} the settings menu * @returns {HTMLElement} the settings menu
*/ */
helper.settingsMenu = function(){return helper.padChrome$('#settings') }; helper.settingsMenu = function () { return helper.padChrome$('#settings'); };
/** /**
* Gets the settings button * Gets the settings button
* *
* @returns {HTMLElement} the settings button * @returns {HTMLElement} the settings button
*/ */
helper.settingsButton = function(){return helper.padChrome$("button[data-l10n-id='pad.toolbar.settings.title']") } helper.settingsButton = function () { return helper.padChrome$("button[data-l10n-id='pad.toolbar.settings.title']"); };
/** /**
* Gets the titlecross icon * Gets the titlecross icon
* *
* @returns {HTMLElement} the titlecross icon * @returns {HTMLElement} the titlecross icon
*/ */
helper.titlecross = function(){return helper.padChrome$('#titlecross')} helper.titlecross = function () { return helper.padChrome$('#titlecross'); };
/** /**
* Returns true if the settings menu is visible * Returns true if the settings menu is visible
* *
* @returns {boolean} is the settings menu shown? * @returns {boolean} is the settings menu shown?
*/ */
helper.isSettingsShown = function() { helper.isSettingsShown = function () {
return helper.padChrome$('#settings').hasClass('popup-show'); return helper.padChrome$('#settings').hasClass('popup-show');
} };
/** /**
* Gets the timer div of a timeslider that has the datetime of the revision * Gets the timer div of a timeslider that has the datetime of the revision
* *
* @returns {HTMLElement} timer * @returns {HTMLElement} timer
*/ */
helper.timesliderTimer = function(){ helper.timesliderTimer = function () {
if(typeof helper.contentWindow().$ == 'function'){ if (typeof helper.contentWindow().$ === 'function') {
return helper.contentWindow().$('#timer') } return helper.contentWindow().$('#timer');
} }
};
/** /**
* Gets the time of the revision on a timeslider * Gets the time of the revision on a timeslider
* *
* @returns {HTMLElement} timer * @returns {HTMLElement} timer
*/ */
helper.timesliderTimerTime = function(){ helper.timesliderTimerTime = function () {
if(helper.timesliderTimer()){ if (helper.timesliderTimer()) {
return helper.timesliderTimer().text() return helper.timesliderTimer().text();
} }
} };
/** /**
* The ui-slidar-bar element in the timeslider * The ui-slidar-bar element in the timeslider
* *
* @returns {HTMLElement} * @returns {HTMLElement}
*/ */
helper.sliderBar = function(){ helper.sliderBar = function () {
return helper.contentWindow().$('#ui-slider-bar') return helper.contentWindow().$('#ui-slider-bar');
} };
/** /**
* revision_date element * revision_date element
@ -131,9 +132,9 @@ helper.sliderBar = function(){
* *
* @returns {HTMLElement} * @returns {HTMLElement}
*/ */
helper.revisionDateElem = function(){ helper.revisionDateElem = function () {
return helper.contentWindow().$('#revision_date').text(); return helper.contentWindow().$('#revision_date').text();
} };
/** /**
* revision_label element * revision_label element
@ -141,6 +142,6 @@ helper.revisionDateElem = function(){
* *
* @returns {HTMLElement} * @returns {HTMLElement}
*/ */
helper.revisionLabelElem = function(){ helper.revisionLabelElem = function () {
return helper.contentWindow().$('#revision_label') return helper.contentWindow().$('#revision_label');
} };

View file

@ -1,11 +1,10 @@
$(function(){ $(() => {
function stringifyException(exception) {
function stringifyException(exception){ let err = exception.stack || exception.toString();
var err = exception.stack || exception.toString();
// FF / Opera do not add the message // FF / Opera do not add the message
if (!~err.indexOf(exception.message)) { if (!~err.indexOf(exception.message)) {
err = exception.message + '\n' + err; err = `${exception.message}\n${err}`;
} }
// <=IE7 stringifies to [Object Error]. Since it can be overloaded, we // <=IE7 stringifies to [Object Error]. Since it can be overloaded, we
@ -14,57 +13,57 @@ $(function(){
// Safari doesn't give you a stack. Let's at least provide a source line. // Safari doesn't give you a stack. Let's at least provide a source line.
if (!exception.stack && exception.sourceURL && exception.line !== undefined) { if (!exception.stack && exception.sourceURL && exception.line !== undefined) {
err += "\n(" + exception.sourceURL + ":" + exception.line + ")"; err += `\n(${exception.sourceURL}:${exception.line})`;
} }
return err; return err;
} }
function CustomRunner(runner) { function CustomRunner(runner) {
var stats = { suites: 0, tests: 0, passes: 0, pending: 0, failures: 0 }; const stats = {suites: 0, tests: 0, passes: 0, pending: 0, failures: 0};
if (!runner) return; if (!runner) return;
runner.on('start', function(){ runner.on('start', () => {
stats.start = new Date; stats.start = new Date();
}); });
runner.on('suite', function(suite){ runner.on('suite', (suite) => {
suite.root || stats.suites++; suite.root || stats.suites++;
if (suite.root) return; if (suite.root) return;
append(suite.title); append(suite.title);
level++; level++;
}); });
runner.on('suite end', function(suite){ runner.on('suite end', (suite) => {
if (suite.root) return; if (suite.root) return;
level--; level--;
if(level == 0) { if (level == 0) {
append(""); append('');
} }
}); });
// Scroll down test display after each test // Scroll down test display after each test
let mochaEl = $('#mocha')[0]; const mochaEl = $('#mocha')[0];
runner.on('test', function(){ runner.on('test', () => {
mochaEl.scrollTop = mochaEl.scrollHeight; mochaEl.scrollTop = mochaEl.scrollHeight;
}); });
// max time a test is allowed to run // max time a test is allowed to run
// TODO this should be lowered once timeslider_revision.js is faster // TODO this should be lowered once timeslider_revision.js is faster
var killTimeout; let killTimeout;
runner.on('test end', function(){ runner.on('test end', () => {
stats.tests++; stats.tests++;
}); });
runner.on('pass', function(test){ runner.on('pass', (test) => {
if(killTimeout) clearTimeout(killTimeout); if (killTimeout) clearTimeout(killTimeout);
killTimeout = setTimeout(function(){ killTimeout = setTimeout(() => {
append("FINISHED - [red]no test started since 3 minutes, tests stopped[clear]"); append('FINISHED - [red]no test started since 3 minutes, tests stopped[clear]');
}, 60000 * 3); }, 60000 * 3);
var medium = test.slow() / 2; const medium = test.slow() / 2;
test.speed = test.duration > test.slow() test.speed = test.duration > test.slow()
? 'slow' ? 'slow'
: test.duration > medium : test.duration > medium
@ -72,107 +71,104 @@ $(function(){
: 'fast'; : 'fast';
stats.passes++; stats.passes++;
append("->","[green]PASSED[clear] :", test.title," ",test.duration,"ms"); append('->', '[green]PASSED[clear] :', test.title, ' ', test.duration, 'ms');
}); });
runner.on('fail', function(test, err){ runner.on('fail', (test, err) => {
if(killTimeout) clearTimeout(killTimeout); if (killTimeout) clearTimeout(killTimeout);
killTimeout = setTimeout(function(){ killTimeout = setTimeout(() => {
append("FINISHED - [red]no test started since 3 minutes, tests stopped[clear]"); append('FINISHED - [red]no test started since 3 minutes, tests stopped[clear]');
}, 60000 * 3); }, 60000 * 3);
stats.failures++; stats.failures++;
test.err = err; test.err = err;
append("->","[red]FAILED[clear] :", test.title, stringifyException(test.err)); append('->', '[red]FAILED[clear] :', test.title, stringifyException(test.err));
}); });
runner.on('pending', function(test){ runner.on('pending', (test) => {
if(killTimeout) clearTimeout(killTimeout); if (killTimeout) clearTimeout(killTimeout);
killTimeout = setTimeout(function(){ killTimeout = setTimeout(() => {
append("FINISHED - [red]no test started since 3 minutes, tests stopped[clear]"); append('FINISHED - [red]no test started since 3 minutes, tests stopped[clear]');
}, 60000 * 3); }, 60000 * 3);
stats.pending++; stats.pending++;
append("->","[yellow]PENDING[clear]:", test.title); append('->', '[yellow]PENDING[clear]:', test.title);
}); });
var $console = $("#console"); const $console = $('#console');
var level = 0; var level = 0;
var append = function(){ var append = function () {
var text = Array.prototype.join.apply(arguments, [" "]); const text = Array.prototype.join.apply(arguments, [' ']);
var oldText = $console.text(); const oldText = $console.text();
var space = ""; let space = '';
for(var i=0;i<level*2;i++){ for (let i = 0; i < level * 2; i++) {
space+=" "; space += ' ';
} }
var splitedText = ""; let splitedText = '';
_(text.split("\n")).each(function(line){ _(text.split('\n')).each((line) => {
while(line.length > 0){ while (line.length > 0) {
var split = line.substr(0,100); const split = line.substr(0, 100);
line = line.substr(100); line = line.substr(100);
if(splitedText.length > 0) splitedText+="\n"; if (splitedText.length > 0) splitedText += '\n';
splitedText += split; splitedText += split;
} }
}); });
//indent all lines with the given amount of space // indent all lines with the given amount of space
var newText = _(splitedText.split("\n")).map(function(line){ const newText = _(splitedText.split('\n')).map((line) => space + line).join('\\n');
return space + line;
}).join("\\n");
$console.text(oldText + newText + "\\n"); $console.text(`${oldText + newText}\\n`);
} };
var total = runner.total; const total = runner.total;
runner.on('end', function(){ runner.on('end', () => {
stats.end = new Date; stats.end = new Date();
stats.duration = stats.end - stats.start; stats.duration = stats.end - stats.start;
var minutes = Math.floor(stats.duration / 1000 / 60); const 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"); const seconds = Math.round((stats.duration / 1000) % 60); // chrome < 57 does not like this .toString().padStart("2","0");
if(stats.tests === total){ if (stats.tests === total) {
append("FINISHED -", stats.passes, "tests passed,", stats.failures, "tests failed,", stats.pending," pending, duration: " + minutes + ":" + seconds); append('FINISHED -', stats.passes, 'tests passed,', stats.failures, 'tests failed,', stats.pending, ` pending, duration: ${minutes}:${seconds}`);
} else if (stats.tests > total) { } 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('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"); 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 {
else { append('FINISHED - but not all tests returned', stats.passes, 'tests passed,', stats.failures, 'tests failed,', stats.pending, `tests pending, duration: ${minutes}:${seconds}`);
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');
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 // http://stackoverflow.com/questions/1403888/get-url-parameter-with-jquery
var getURLParameter = function (name) { const getURLParameter = function (name) {
return decodeURI( return decodeURI(
(RegExp(name + '=' + '(.+?)(&|$)').exec(location.search)||[,null])[1] (RegExp(`${name}=` + '(.+?)(&|$)').exec(location.search) || [, null])[1],
); );
} };
//get the list of specs and filter it if requested // get the list of specs and filter it if requested
var specs = specs_list.slice(); const specs = specs_list.slice();
//inject spec scripts into the dom // inject spec scripts into the dom
var $body = $('body'); const $body = $('body');
$.each(specs, function(i, spec){ $.each(specs, (i, spec) => {
if(spec[0] != "/"){ // if the spec isn't a plugin spec which means the spec file might be in a different subfolder if (spec[0] != '/') { // if the spec isn't a plugin spec which means the spec file might be in a different subfolder
$body.append('<script src="specs/' + spec + '"></script>') $body.append(`<script src="specs/${spec}"></script>`);
}else{ } else {
$body.append('<script src="' + spec + '"></script>') $body.append(`<script src="${spec}"></script>`);
} }
}); });
//initalize the test helper // initalize the test helper
helper.init(function(){ helper.init(() => {
//configure and start the test framework // configure and start the test framework
var grep = getURLParameter("grep"); const grep = getURLParameter('grep');
if(grep != "null"){ if (grep != 'null') {
mocha.grep(grep); mocha.grep(grep);
} }
var runner = mocha.run(); const runner = mocha.run();
CustomRunner(runner) CustomRunner(runner);
}); });
}); });

View file

@ -1,27 +1,24 @@
describe("All the alphabet works n stuff", function(){ describe('All the alphabet works n stuff', function () {
var expectedString = "abcdefghijklmnopqrstuvwxyz"; const expectedString = 'abcdefghijklmnopqrstuvwxyz';
//create a new pad before each test run // create a new pad before each test run
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); helper.newPad(cb);
this.timeout(60000); this.timeout(60000);
}); });
it("when you enter any char it appears right", function(done) { it('when you enter any char it appears right', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe // get the first text element out of the inner iframe
var firstTextElement = inner$("div").first(); const firstTextElement = inner$('div').first();
// simulate key presses to delete content // simulate key presses to delete content
firstTextElement.sendkeys('{selectall}'); // select all firstTextElement.sendkeys('{selectall}'); // select all
firstTextElement.sendkeys('{del}'); // clear the first line firstTextElement.sendkeys('{del}'); // clear the first line
firstTextElement.sendkeys(expectedString); // insert the string firstTextElement.sendkeys(expectedString); // insert the string
helper.waitFor(function(){ helper.waitFor(() => inner$('div').first().text() === expectedString, 2000).done(done);
return inner$("div").first().text() === expectedString;
}, 2000).done(done);
}); });
}); });

View file

@ -1,46 +1,46 @@
describe('author of pad edition', function() { describe('author of pad edition', function () {
var REGULAR_LINE = 0; const REGULAR_LINE = 0;
var LINE_WITH_ORDERED_LIST = 1; const LINE_WITH_ORDERED_LIST = 1;
var LINE_WITH_UNORDERED_LIST = 2; const LINE_WITH_UNORDERED_LIST = 2;
// author 1 creates a new pad with some content (regular lines and lists) // author 1 creates a new pad with some content (regular lines and lists)
before(function(done) { before(function (done) {
var padId = helper.newPad(function() { var padId = helper.newPad(() => {
// make sure pad has at least 3 lines // make sure pad has at least 3 lines
var $firstLine = helper.padInner$('div').first(); const $firstLine = helper.padInner$('div').first();
var threeLines = ['regular line', 'line with ordered list', 'line with unordered list'].join('<br>'); const threeLines = ['regular line', 'line with ordered list', 'line with unordered list'].join('<br>');
$firstLine.html(threeLines); $firstLine.html(threeLines);
// wait for lines to be processed by Etherpad // wait for lines to be processed by Etherpad
helper.waitFor(function() { helper.waitFor(() => {
var $lineWithUnorderedList = getLine(LINE_WITH_UNORDERED_LIST); const $lineWithUnorderedList = getLine(LINE_WITH_UNORDERED_LIST);
return $lineWithUnorderedList.text() === 'line with unordered list'; return $lineWithUnorderedList.text() === 'line with unordered list';
}).done(function() { }).done(() => {
// create the unordered list // create the unordered list
var $lineWithUnorderedList = getLine(LINE_WITH_UNORDERED_LIST); const $lineWithUnorderedList = getLine(LINE_WITH_UNORDERED_LIST);
$lineWithUnorderedList.sendkeys('{selectall}'); $lineWithUnorderedList.sendkeys('{selectall}');
var $insertUnorderedListButton = helper.padChrome$('.buttonicon-insertunorderedlist'); const $insertUnorderedListButton = helper.padChrome$('.buttonicon-insertunorderedlist');
$insertUnorderedListButton.click(); $insertUnorderedListButton.click();
helper.waitFor(function() { helper.waitFor(() => {
var $lineWithUnorderedList = getLine(LINE_WITH_UNORDERED_LIST); const $lineWithUnorderedList = getLine(LINE_WITH_UNORDERED_LIST);
return $lineWithUnorderedList.find('ul li').length === 1; return $lineWithUnorderedList.find('ul li').length === 1;
}).done(function() { }).done(() => {
// create the ordered list // create the ordered list
var $lineWithOrderedList = getLine(LINE_WITH_ORDERED_LIST); const $lineWithOrderedList = getLine(LINE_WITH_ORDERED_LIST);
$lineWithOrderedList.sendkeys('{selectall}'); $lineWithOrderedList.sendkeys('{selectall}');
var $insertOrderedListButton = helper.padChrome$('.buttonicon-insertorderedlist'); const $insertOrderedListButton = helper.padChrome$('.buttonicon-insertorderedlist');
$insertOrderedListButton.click(); $insertOrderedListButton.click();
helper.waitFor(function() { helper.waitFor(() => {
var $lineWithOrderedList = getLine(LINE_WITH_ORDERED_LIST); const $lineWithOrderedList = getLine(LINE_WITH_ORDERED_LIST);
return $lineWithOrderedList.find('ol li').length === 1; return $lineWithOrderedList.find('ol li').length === 1;
}).done(function() { }).done(() => {
// Reload pad, to make changes as a second user. Need a timeout here to make sure // Reload pad, to make changes as a second user. Need a timeout here to make sure
// all changes were saved before reloading // all changes were saved before reloading
setTimeout(function() { setTimeout(() => {
// Expire cookie, so author is changed after reloading the pad. // 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 // 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.padChrome$.document.cookie = 'token=foo;expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
@ -55,55 +55,51 @@ describe('author of pad edition', function() {
}); });
// author 2 makes some changes on the pad // author 2 makes some changes on the pad
it('marks only the new content as changes of the second user on a regular line', function(done) { it('marks only the new content as changes of the second user on a regular line', function (done) {
changeLineAndCheckOnlyThatChangeIsFromThisAuthor(REGULAR_LINE, 'x', done); changeLineAndCheckOnlyThatChangeIsFromThisAuthor(REGULAR_LINE, 'x', done);
}); });
it('marks only the new content as changes of the second user on a line with ordered list', function(done) { it('marks only the new content as changes of the second user on a line with ordered list', function (done) {
changeLineAndCheckOnlyThatChangeIsFromThisAuthor(LINE_WITH_ORDERED_LIST, 'y', done); changeLineAndCheckOnlyThatChangeIsFromThisAuthor(LINE_WITH_ORDERED_LIST, 'y', done);
}); });
it('marks only the new content as changes of the second user on a line with unordered list', function(done) { it('marks only the new content as changes of the second user on a line with unordered list', function (done) {
changeLineAndCheckOnlyThatChangeIsFromThisAuthor(LINE_WITH_UNORDERED_LIST, 'z', done); changeLineAndCheckOnlyThatChangeIsFromThisAuthor(LINE_WITH_UNORDERED_LIST, 'z', done);
}); });
/* ********************** Helper functions ************************ */ /* ********************** Helper functions ************************ */
var getLine = function(lineNumber) { var getLine = function (lineNumber) {
return helper.padInner$('div').eq(lineNumber); return helper.padInner$('div').eq(lineNumber);
} };
var getAuthorFromClassList = function(classes) { const getAuthorFromClassList = function (classes) {
return classes.find(function(cls) { return classes.find((cls) => cls.startsWith('author'));
return cls.startsWith('author'); };
});
}
var changeLineAndCheckOnlyThatChangeIsFromThisAuthor = function(lineNumber, textChange, done) { var changeLineAndCheckOnlyThatChangeIsFromThisAuthor = function (lineNumber, textChange, done) {
// get original author class // get original author class
var classes = getLine(lineNumber).find('span').first().attr('class').split(' '); const classes = getLine(lineNumber).find('span').first().attr('class').split(' ');
var originalAuthor = getAuthorFromClassList(classes); const originalAuthor = getAuthorFromClassList(classes);
// make change on target line // make change on target line
var $regularLine = getLine(lineNumber); const $regularLine = getLine(lineNumber);
helper.selectLines($regularLine, $regularLine, 2, 2); // place caret after 2nd char of line helper.selectLines($regularLine, $regularLine, 2, 2); // place caret after 2nd char of line
$regularLine.sendkeys(textChange); $regularLine.sendkeys(textChange);
// wait for change to be processed by Etherpad // wait for change to be processed by Etherpad
var otherAuthorsOfLine; let otherAuthorsOfLine;
helper.waitFor(function() { helper.waitFor(() => {
var authorsOfLine = getLine(lineNumber).find('span').map(function() { const authorsOfLine = getLine(lineNumber).find('span').map(function () {
return getAuthorFromClassList($(this).attr('class').split(' ')); return getAuthorFromClassList($(this).attr('class').split(' '));
}).get(); }).get();
otherAuthorsOfLine = authorsOfLine.filter(function(author) { otherAuthorsOfLine = authorsOfLine.filter((author) => author !== originalAuthor);
return author !== originalAuthor; const lineHasChangeOfThisAuthor = otherAuthorsOfLine.length > 0;
});
var lineHasChangeOfThisAuthor = otherAuthorsOfLine.length > 0;
return lineHasChangeOfThisAuthor; return lineHasChangeOfThisAuthor;
}).done(function() { }).done(() => {
var thisAuthor = otherAuthorsOfLine[0]; const thisAuthor = otherAuthorsOfLine[0];
var $changeOfThisAuthor = getLine(lineNumber).find('span.' + thisAuthor); const $changeOfThisAuthor = getLine(lineNumber).find(`span.${thisAuthor}`);
expect($changeOfThisAuthor.text()).to.be(textChange); expect($changeOfThisAuthor.text()).to.be(textChange);
done(); done();
}); });
} };
}); });

View file

@ -1,64 +1,64 @@
describe("bold button", function(){ describe('bold button', function () {
//create a new pad before each test run // create a new pad before each test run
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); helper.newPad(cb);
this.timeout(60000); this.timeout(60000);
}); });
it("makes text bold on click", function(done) { it('makes text bold on click', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe // get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first(); const $firstTextElement = inner$('div').first();
//select this text element // select this text element
$firstTextElement.sendkeys('{selectall}'); $firstTextElement.sendkeys('{selectall}');
//get the bold button and click it // get the bold button and click it
var $boldButton = chrome$(".buttonicon-bold"); const $boldButton = chrome$('.buttonicon-bold');
$boldButton.click(); $boldButton.click();
//ace creates a new dom element when you press a button, so just get the first text element again // ace creates a new dom element when you press a button, so just get the first text element again
var $newFirstTextElement = inner$("div").first(); const $newFirstTextElement = inner$('div').first();
// is there a <b> element now? // is there a <b> element now?
var isBold = $newFirstTextElement.find("b").length === 1; const isBold = $newFirstTextElement.find('b').length === 1;
//expect it to be bold // expect it to be bold
expect(isBold).to.be(true); expect(isBold).to.be(true);
//make sure the text hasn't changed // make sure the text hasn't changed
expect($newFirstTextElement.text()).to.eql($firstTextElement.text()); expect($newFirstTextElement.text()).to.eql($firstTextElement.text());
done(); done();
}); });
it("makes text bold on keypress", function(done) { it('makes text bold on keypress', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe // get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first(); const $firstTextElement = inner$('div').first();
//select this text element // select this text element
$firstTextElement.sendkeys('{selectall}'); $firstTextElement.sendkeys('{selectall}');
var e = inner$.Event(helper.evtType); const e = inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key e.ctrlKey = true; // Control key
e.which = 66; // b e.which = 66; // b
inner$("#innerdocbody").trigger(e); inner$('#innerdocbody').trigger(e);
//ace creates a new dom element when you press a button, so just get the first text element again // ace creates a new dom element when you press a button, so just get the first text element again
var $newFirstTextElement = inner$("div").first(); const $newFirstTextElement = inner$('div').first();
// is there a <b> element now? // is there a <b> element now?
var isBold = $newFirstTextElement.find("b").length === 1; const isBold = $newFirstTextElement.find('b').length === 1;
//expect it to be bold // expect it to be bold
expect(isBold).to.be(true); expect(isBold).to.be(true);
//make sure the text hasn't changed // make sure the text hasn't changed
expect($newFirstTextElement.text()).to.eql($firstTextElement.text()); expect($newFirstTextElement.text()).to.eql($firstTextElement.text());
done(); done();

View file

@ -1,7 +1,7 @@
describe("As the caret is moved is the UI properly updated?", function(){ describe('As the caret is moved is the UI properly updated?', function () {
var padName; let padName;
var numberOfRows = 50; const numberOfRows = 50;
/* /*
//create a new pad before each test run //create a new pad before each test run
beforeEach(function(cb){ beforeEach(function(cb){
@ -28,7 +28,7 @@ describe("As the caret is moved is the UI properly updated?", function(){
* How do we keep the authors focus on a line if the lines above the author are modified? We should only redraw the user to a location if they are typing and make sure shift and arrow keys aren't redrawing the UI else highlight - copy/paste would get broken * How do we keep the authors focus on a line if the lines above the author are modified? We should only redraw the user to a location if they are typing and make sure shift and arrow keys aren't redrawing the UI else highlight - copy/paste would get broken
* How can we simulate an edit event in the test framework? * How can we simulate an edit event in the test framework?
*/ */
/* /*
// THIS DOESNT WORK IN CHROME AS IT DOESNT MOVE THE CURSOR! // THIS DOESNT WORK IN CHROME AS IT DOESNT MOVE THE CURSOR!
it("down arrow", function(done){ it("down arrow", function(done){
var inner$ = helper.padInner$; var inner$ = helper.padInner$;
@ -194,7 +194,6 @@ console.log(inner$);
}); });
/* /*
it("Creates N rows, changes height of rows, updates UI by caret key events", function(done){ it("Creates N rows, changes height of rows, updates UI by caret key events", function(done){
var inner$ = helper.padInner$; var inner$ = helper.padInner$;
@ -264,7 +263,6 @@ console.log(inner$);
}).done(function(){ // Once the DOM has registered the items }).done(function(){ // Once the DOM has registered the items
}); });
}); });
@ -284,54 +282,51 @@ console.log(inner$);
done(); done();
}); });
*/ */
}); });
function prepareDocument(n, target){ // generates a random document with random content on n lines function prepareDocument(n, target) { // generates a random document with random content on n lines
var i = 0; let i = 0;
while(i < n){ // for each line while (i < n) { // for each line
target.sendkeys(makeStr()); // generate a random string and send that to the editor target.sendkeys(makeStr()); // generate a random string and send that to the editor
target.sendkeys('{enter}'); // generator an enter keypress target.sendkeys('{enter}'); // generator an enter keypress
i++; // rinse n times i++; // rinse n times
} }
} }
function keyEvent(target, charCode, ctrl, shift){ // sends a charCode to the window function keyEvent(target, charCode, ctrl, shift) { // sends a charCode to the window
const e = target.Event(helper.evtType);
var e = target.Event(helper.evtType); if (ctrl) {
if(ctrl){
e.ctrlKey = true; // Control key e.ctrlKey = true; // Control key
} }
if(shift){ if (shift) {
e.shiftKey = true; // Shift Key e.shiftKey = true; // Shift Key
} }
e.which = charCode; e.which = charCode;
e.keyCode = charCode; e.keyCode = charCode;
target("#innerdocbody").trigger(e); target('#innerdocbody').trigger(e);
} }
function makeStr(){ // from http://stackoverflow.com/questions/1349404/generate-a-string-of-5-random-characters-in-javascript function makeStr() { // from http://stackoverflow.com/questions/1349404/generate-a-string-of-5-random-characters-in-javascript
var text = ""; let text = '';
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for( var i=0; i < 5; i++ ) for (let i = 0; i < 5; i++) text += possible.charAt(Math.floor(Math.random() * possible.length));
text += possible.charAt(Math.floor(Math.random() * possible.length));
return text; return text;
} }
function isScrolledIntoView(elem, $){ // from http://stackoverflow.com/questions/487073/check-if-element-is-visible-after-scrolling function isScrolledIntoView(elem, $) { // from http://stackoverflow.com/questions/487073/check-if-element-is-visible-after-scrolling
var docViewTop = $(window).scrollTop(); const docViewTop = $(window).scrollTop();
var docViewBottom = docViewTop + $(window).height(); const docViewBottom = docViewTop + $(window).height();
var elemTop = $(elem).offset().top; // how far the element is from the top of it's container const elemTop = $(elem).offset().top; // how far the element is from the top of it's container
var elemBottom = elemTop + $(elem).height(); // how far plus the height of the elem.. IE is it all in? let elemBottom = elemTop + $(elem).height(); // how far plus the height of the elem.. IE is it all in?
elemBottom = elemBottom - 16; // don't ask, sorry but this is needed.. elemBottom -= 16; // don't ask, sorry but this is needed..
return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop)); return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
} }
function caretPosition($){ function caretPosition($) {
var doc = $.window.document; const doc = $.window.document;
var pos = doc.getSelection(); const pos = doc.getSelection();
pos.y = pos.anchorNode.parentElement.offsetTop; pos.y = pos.anchorNode.parentElement.offsetTop;
pos.x = pos.anchorNode.parentElement.offsetLeft; pos.x = pos.anchorNode.parentElement.offsetLeft;
return pos; return pos;

View file

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

View file

@ -1,70 +1,69 @@
describe("change username value", function(){ describe('change username value', function () {
//create a new pad before each test run // create a new pad before each test run
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); helper.newPad(cb);
this.timeout(60000); this.timeout(60000);
}); });
it("Remembers the user name after a refresh", function(done) { it('Remembers the user name after a refresh', function (done) {
this.timeout(60000); this.timeout(60000);
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//click on the settings button to make settings visible // click on the settings button to make settings visible
var $userButton = chrome$(".buttonicon-showusers"); const $userButton = chrome$('.buttonicon-showusers');
$userButton.click(); $userButton.click();
var $usernameInput = chrome$("#myusernameedit"); const $usernameInput = chrome$('#myusernameedit');
$usernameInput.click(); $usernameInput.click();
$usernameInput.val('John McLear'); $usernameInput.val('John McLear');
$usernameInput.blur(); $usernameInput.blur();
setTimeout(function(){ //give it a second to save the username on the server side setTimeout(() => { // give it a second to save the username on the server side
helper.newPad({ // get a new pad, but don't clear the cookies helper.newPad({ // get a new pad, but don't clear the cookies
clearCookies: false clearCookies: false,
, cb: function(){ cb() {
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//click on the settings button to make settings visible // click on the settings button to make settings visible
var $userButton = chrome$(".buttonicon-showusers"); const $userButton = chrome$('.buttonicon-showusers');
$userButton.click(); $userButton.click();
var $usernameInput = chrome$("#myusernameedit"); const $usernameInput = chrome$('#myusernameedit');
expect($usernameInput.val()).to.be('John McLear') expect($usernameInput.val()).to.be('John McLear');
done(); done();
} },
}); });
}, 1000); }, 1000);
}); });
it("Own user name is shown when you enter a chat", function(done) { it('Own user name is shown when you enter a chat', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//click on the settings button to make settings visible // click on the settings button to make settings visible
var $userButton = chrome$(".buttonicon-showusers"); const $userButton = chrome$('.buttonicon-showusers');
$userButton.click(); $userButton.click();
var $usernameInput = chrome$("#myusernameedit"); const $usernameInput = chrome$('#myusernameedit');
$usernameInput.click(); $usernameInput.click();
$usernameInput.val('John McLear'); $usernameInput.val('John McLear');
$usernameInput.blur(); $usernameInput.blur();
//click on the chat button to make chat visible // click on the chat button to make chat visible
var $chatButton = chrome$("#chaticon"); const $chatButton = chrome$('#chaticon');
$chatButton.click(); $chatButton.click();
var $chatInput = chrome$("#chatinput"); const $chatInput = chrome$('#chatinput');
$chatInput.sendkeys('O hi'); // simulate a keypress of typing JohnMcLear $chatInput.sendkeys('O hi'); // simulate a keypress of typing JohnMcLear
$chatInput.sendkeys('{enter}'); // simulate a keypress of enter actually does evt.which = 10 not 13 $chatInput.sendkeys('{enter}'); // simulate a keypress of enter actually does evt.which = 10 not 13
//check if chat shows up // check if chat shows up
helper.waitFor(function(){ helper.waitFor(() => chrome$('#chattext').children('p').length !== 0, // wait until the chat message shows up
return chrome$("#chattext").children("p").length !== 0; // wait until the chat message shows up ).done(() => {
}).done(function(){ const $firstChatMessage = chrome$('#chattext').children('p');
var $firstChatMessage = chrome$("#chattext").children("p"); const containsJohnMcLear = $firstChatMessage.text().indexOf('John McLear') !== -1; // does the string contain John McLear
var containsJohnMcLear = $firstChatMessage.text().indexOf("John McLear") !== -1; // does the string contain John McLear
expect(containsJohnMcLear).to.be(true); // expect the first chat message to contain JohnMcLear expect(containsJohnMcLear).to.be(true); // expect the first chat message to contain JohnMcLear
done(); done();
}); });

View file

@ -1,11 +1,11 @@
describe("Chat messages and UI", function(){ describe('Chat messages and UI', function () {
//create a new pad before each test run // create a new pad before each test run
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); helper.newPad(cb);
}); });
it("opens chat, sends a message, makes sure it exists on the page and hides chat", async function() { it('opens chat, sends a message, makes sure it exists on the page and hides chat', async function () {
var chatValue = "JohnMcLear"; const chatValue = 'JohnMcLear';
await helper.showChat(); await helper.showChat();
await helper.sendChatMessage(`${chatValue}{enter}`); await helper.sendChatMessage(`${chatValue}{enter}`);
@ -18,33 +18,33 @@ describe("Chat messages and UI", function(){
// <span class="time author-a-qjkwz78zs4z122z0pz80zz82zz79zphz83z">12:38 // <span class="time author-a-qjkwz78zs4z122z0pz80zz82zz79zphz83z">12:38
// </span> JohnMcLear // </span> JohnMcLear
// </p> // </p>
let username = helper.chatTextParagraphs().children("b").text(); const username = helper.chatTextParagraphs().children('b').text();
let time = helper.chatTextParagraphs().children(".time").text(); const time = helper.chatTextParagraphs().children('.time').text();
expect(helper.chatTextParagraphs().text()).to.be(`${username}${time} ${chatValue}`); expect(helper.chatTextParagraphs().text()).to.be(`${username}${time} ${chatValue}`);
await helper.hideChat(); await helper.hideChat();
}); });
it("makes sure that an empty message can't be sent", async function() { it("makes sure that an empty message can't be sent", async function () {
var chatValue = "mluto"; const chatValue = 'mluto';
await helper.showChat(); await helper.showChat();
await helper.sendChatMessage(`{enter}${chatValue}{enter}`); // simulate a keypress of typing enter, mluto and enter (to send 'mluto') await helper.sendChatMessage(`{enter}${chatValue}{enter}`); // simulate a keypress of typing enter, mluto and enter (to send 'mluto')
let chat = helper.chatTextParagraphs(); const chat = helper.chatTextParagraphs();
expect(chat.length).to.be(1); expect(chat.length).to.be(1);
// check that the received message is not the empty one // check that the received message is not the empty one
let username = chat.children("b").text(); const username = chat.children('b').text();
let time = chat.children(".time").text(); const time = chat.children('.time').text();
expect(chat.text()).to.be(`${username}${time} ${chatValue}`); expect(chat.text()).to.be(`${username}${time} ${chatValue}`);
}); });
it("makes chat stick to right side of the screen via settings, remove sticky via settings, close it", async function() { it('makes chat stick to right side of the screen via settings, remove sticky via settings, close it', async function () {
await helper.showSettings(); await helper.showSettings();
await helper.enableStickyChatviaSettings(); await helper.enableStickyChatviaSettings();
@ -60,7 +60,7 @@ describe("Chat messages and UI", function(){
expect(helper.isChatboxShown()).to.be(false); expect(helper.isChatboxShown()).to.be(false);
}); });
it("makes chat stick to right side of the screen via icon on the top right, remove sticky via icon, close it", async function() { it('makes chat stick to right side of the screen via icon on the top right, remove sticky via icon, close it', async function () {
await helper.showChat(); await helper.showChat();
await helper.enableStickyChatviaIcon(); await helper.enableStickyChatviaIcon();
@ -76,39 +76,36 @@ describe("Chat messages and UI", function(){
expect(helper.isChatboxShown()).to.be(false); expect(helper.isChatboxShown()).to.be(false);
}); });
xit("Checks showChat=false URL Parameter hides chat then when removed it shows chat", function(done) { xit('Checks showChat=false URL Parameter hides chat then when removed it shows chat', function (done) {
this.timeout(60000); this.timeout(60000);
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
setTimeout(function(){ //give it a second to save the username on the server side setTimeout(() => { // give it a second to save the username on the server side
helper.newPad({ // get a new pad, but don't clear the cookies helper.newPad({ // get a new pad, but don't clear the cookies
clearCookies: false, clearCookies: false,
params:{ params: {
showChat: "false" showChat: 'false',
}, cb: function(){ }, cb() {
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
var chaticon = chrome$("#chaticon"); const chaticon = chrome$('#chaticon');
// chat should be hidden. // chat should be hidden.
expect(chaticon.is(":visible")).to.be(false); expect(chaticon.is(':visible')).to.be(false);
setTimeout(function(){ //give it a second to save the username on the server side setTimeout(() => { // give it a second to save the username on the server side
helper.newPad({ // get a new pad, but don't clear the cookies helper.newPad({ // get a new pad, but don't clear the cookies
clearCookies: false clearCookies: false,
, cb: function(){ cb() {
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
var chaticon = chrome$("#chaticon"); const chaticon = chrome$('#chaticon');
// chat should be visible. // chat should be visible.
expect(chaticon.is(":visible")).to.be(true); expect(chaticon.is(':visible')).to.be(true);
done(); done();
} },
}); });
}, 1000); }, 1000);
},
}
}); });
}, 1000); }, 1000);
}); });
}); });

View file

@ -1,85 +1,77 @@
describe("chat-load-messages", function(){ describe('chat-load-messages', function () {
var padName; let padName;
it("creates a pad", function(done) { it('creates a pad', function (done) {
padName = helper.newPad(done); padName = helper.newPad(done);
this.timeout(60000); this.timeout(60000);
}); });
it("adds a lot of messages", function(done) { it('adds a lot of messages', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
var chatButton = chrome$("#chaticon"); const chatButton = chrome$('#chaticon');
chatButton.click(); chatButton.click();
var chatInput = chrome$("#chatinput"); const chatInput = chrome$('#chatinput');
var chatText = chrome$("#chattext"); const chatText = chrome$('#chattext');
this.timeout(60000); this.timeout(60000);
var messages = 140; const messages = 140;
for(var i=1; i <= messages; i++) { for (let i = 1; i <= messages; i++) {
var num = ''+i; let num = `${i}`;
if(num.length == 1) if (num.length == 1) num = `00${num}`;
num = '00'+num; if (num.length == 2) num = `0${num}`;
if(num.length == 2) chatInput.sendkeys(`msg${num}`);
num = '0'+num;
chatInput.sendkeys('msg' + num);
chatInput.sendkeys('{enter}'); chatInput.sendkeys('{enter}');
} }
helper.waitFor(function(){ helper.waitFor(() => chatText.children('p').length == messages, 60000).always(() => {
return chatText.children("p").length == messages; expect(chatText.children('p').length).to.be(messages);
}, 60000).always(function(){
expect(chatText.children("p").length).to.be(messages);
helper.newPad(done, padName); helper.newPad(done, padName);
}); });
}); });
it("checks initial message count", function(done) { it('checks initial message count', function (done) {
var chatText; let chatText;
var expectedCount = 101; const expectedCount = 101;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
helper.waitFor(function(){ helper.waitFor(() => {
var chatButton = chrome$("#chaticon"); const chatButton = chrome$('#chaticon');
chatButton.click(); chatButton.click();
chatText = chrome$("#chattext"); chatText = chrome$('#chattext');
return chatText.children("p").length == expectedCount; return chatText.children('p').length == expectedCount;
}).always(function(){ }).always(() => {
expect(chatText.children("p").length).to.be(expectedCount); expect(chatText.children('p').length).to.be(expectedCount);
done(); done();
}); });
}); });
it("loads more messages", function(done) { it('loads more messages', function (done) {
var expectedCount = 122; const expectedCount = 122;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
var chatButton = chrome$("#chaticon"); const chatButton = chrome$('#chaticon');
chatButton.click(); chatButton.click();
var chatText = chrome$("#chattext"); const chatText = chrome$('#chattext');
var loadMsgBtn = chrome$("#chatloadmessagesbutton"); const loadMsgBtn = chrome$('#chatloadmessagesbutton');
loadMsgBtn.click(); loadMsgBtn.click();
helper.waitFor(function(){ helper.waitFor(() => chatText.children('p').length == expectedCount).always(() => {
return chatText.children("p").length == expectedCount; expect(chatText.children('p').length).to.be(expectedCount);
}).always(function(){
expect(chatText.children("p").length).to.be(expectedCount);
done(); done();
}); });
}); });
it("checks for button vanishing", function(done) { it('checks for button vanishing', function (done) {
var expectedDisplay = 'none'; const expectedDisplay = 'none';
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
var chatButton = chrome$("#chaticon"); const chatButton = chrome$('#chaticon');
chatButton.click(); chatButton.click();
var chatText = chrome$("#chattext"); const chatText = chrome$('#chattext');
var loadMsgBtn = chrome$("#chatloadmessagesbutton"); const loadMsgBtn = chrome$('#chatloadmessagesbutton');
var loadMsgBall = chrome$("#chatloadmessagesball"); const loadMsgBall = chrome$('#chatloadmessagesball');
loadMsgBtn.click(); loadMsgBtn.click();
helper.waitFor(function(){ helper.waitFor(() => loadMsgBtn.css('display') == expectedDisplay &&
return loadMsgBtn.css('display') == expectedDisplay && loadMsgBall.css('display') == expectedDisplay).always(() => {
loadMsgBall.css('display') == expectedDisplay;
}).always(function(){
expect(loadMsgBtn.css('display')).to.be(expectedDisplay); expect(loadMsgBtn.css('display')).to.be(expectedDisplay);
expect(loadMsgBall.css('display')).to.be(expectedDisplay); expect(loadMsgBall.css('display')).to.be(expectedDisplay);
done(); done();

View file

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

View file

@ -1,36 +1,36 @@
describe("delete keystroke", function(){ describe('delete keystroke', function () {
//create a new pad before each test run // create a new pad before each test run
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); helper.newPad(cb);
this.timeout(60000); this.timeout(60000);
}); });
it("makes text delete", function(done) { it('makes text delete', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe // get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first(); const $firstTextElement = inner$('div').first();
// get the original length of this element // get the original length of this element
var elementLength = $firstTextElement.text().length; const elementLength = $firstTextElement.text().length;
// get the original string value minus the last char // get the original string value minus the last char
var originalTextValue = $firstTextElement.text(); const originalTextValue = $firstTextElement.text();
var originalTextValueMinusFirstChar = originalTextValue.substring(1, originalTextValue.length ); const originalTextValueMinusFirstChar = originalTextValue.substring(1, originalTextValue.length);
// simulate key presses to delete content // simulate key presses to delete content
$firstTextElement.sendkeys('{leftarrow}'); // simulate a keypress of the left arrow key $firstTextElement.sendkeys('{leftarrow}'); // simulate a keypress of the left arrow key
$firstTextElement.sendkeys('{del}'); // simulate a keypress of delete $firstTextElement.sendkeys('{del}'); // simulate a keypress of delete
//ace creates a new dom element when you press a keystroke, so just get the first text element again // ace creates a new dom element when you press a keystroke, so just get the first text element again
var $newFirstTextElement = inner$("div").first(); const $newFirstTextElement = inner$('div').first();
// get the new length of this element // get the new length of this element
var newElementLength = $newFirstTextElement.text().length; const newElementLength = $newFirstTextElement.text().length;
//expect it to be one char less in length // expect it to be one char less in length
expect(newElementLength).to.be((elementLength-1)); expect(newElementLength).to.be((elementLength - 1));
done(); done();
}); });

View file

@ -1,39 +1,39 @@
// WARNING: drag and drop is only simulated on these tests, so manual testing might also be necessary // WARNING: drag and drop is only simulated on these tests, so manual testing might also be necessary
describe('drag and drop', function() { describe('drag and drop', function () {
before(function(done) { before(function (done) {
helper.newPad(function() { helper.newPad(() => {
createScriptWithSeveralLines(done); createScriptWithSeveralLines(done);
}); });
this.timeout(60000); this.timeout(60000);
}); });
context('when user drags part of one line and drops it far form its original place', function() { context('when user drags part of one line and drops it far form its original place', function () {
before(function(done) { before(function (done) {
selectPartOfSourceLine(); selectPartOfSourceLine();
dragSelectedTextAndDropItIntoMiddleOfLine(TARGET_LINE); dragSelectedTextAndDropItIntoMiddleOfLine(TARGET_LINE);
// make sure DnD was correctly simulated // make sure DnD was correctly simulated
helper.waitFor(function() { helper.waitFor(() => {
var $targetLine = getLine(TARGET_LINE); const $targetLine = getLine(TARGET_LINE);
var sourceWasMovedToTarget = $targetLine.text() === 'Target line [line 1]'; const sourceWasMovedToTarget = $targetLine.text() === 'Target line [line 1]';
return sourceWasMovedToTarget; return sourceWasMovedToTarget;
}).done(done); }).done(done);
}); });
context('and user triggers UNDO', function() { context('and user triggers UNDO', function () {
before(function() { before(function () {
var $undoButton = helper.padChrome$(".buttonicon-undo"); const $undoButton = helper.padChrome$('.buttonicon-undo');
$undoButton.click(); $undoButton.click();
}); });
it('moves text back to its original place', function(done) { it('moves text back to its original place', function (done) {
// test text was removed from drop target // test text was removed from drop target
var $targetLine = getLine(TARGET_LINE); const $targetLine = getLine(TARGET_LINE);
expect($targetLine.text()).to.be('Target line []'); expect($targetLine.text()).to.be('Target line []');
// test text was added back to original place // test text was added back to original place
var $firstSourceLine = getLine(FIRST_SOURCE_LINE); const $firstSourceLine = getLine(FIRST_SOURCE_LINE);
var $lastSourceLine = getLine(FIRST_SOURCE_LINE + 1); const $lastSourceLine = getLine(FIRST_SOURCE_LINE + 1);
expect($firstSourceLine.text()).to.be('Source line 1.'); expect($firstSourceLine.text()).to.be('Source line 1.');
expect($lastSourceLine.text()).to.be('Source line 2.'); expect($lastSourceLine.text()).to.be('Source line 2.');
@ -42,33 +42,33 @@ describe('drag and drop', function() {
}); });
}); });
context('when user drags some lines far form its original place', function() { context('when user drags some lines far form its original place', function () {
before(function(done) { before(function (done) {
selectMultipleSourceLines(); selectMultipleSourceLines();
dragSelectedTextAndDropItIntoMiddleOfLine(TARGET_LINE); dragSelectedTextAndDropItIntoMiddleOfLine(TARGET_LINE);
// make sure DnD was correctly simulated // make sure DnD was correctly simulated
helper.waitFor(function() { helper.waitFor(() => {
var $lineAfterTarget = getLine(TARGET_LINE + 1); const $lineAfterTarget = getLine(TARGET_LINE + 1);
var sourceWasMovedToTarget = $lineAfterTarget.text() !== '...'; const sourceWasMovedToTarget = $lineAfterTarget.text() !== '...';
return sourceWasMovedToTarget; return sourceWasMovedToTarget;
}).done(done); }).done(done);
}); });
context('and user triggers UNDO', function() { context('and user triggers UNDO', function () {
before(function() { before(function () {
var $undoButton = helper.padChrome$(".buttonicon-undo"); const $undoButton = helper.padChrome$('.buttonicon-undo');
$undoButton.click(); $undoButton.click();
}); });
it('moves text back to its original place', function(done) { it('moves text back to its original place', function (done) {
// test text was removed from drop target // test text was removed from drop target
var $targetLine = getLine(TARGET_LINE); const $targetLine = getLine(TARGET_LINE);
expect($targetLine.text()).to.be('Target line []'); expect($targetLine.text()).to.be('Target line []');
// test text was added back to original place // test text was added back to original place
var $firstSourceLine = getLine(FIRST_SOURCE_LINE); const $firstSourceLine = getLine(FIRST_SOURCE_LINE);
var $lastSourceLine = getLine(FIRST_SOURCE_LINE + 1); const $lastSourceLine = getLine(FIRST_SOURCE_LINE + 1);
expect($firstSourceLine.text()).to.be('Source line 1.'); expect($firstSourceLine.text()).to.be('Source line 1.');
expect($lastSourceLine.text()).to.be('Source line 2.'); expect($lastSourceLine.text()).to.be('Source line 2.');
@ -81,88 +81,88 @@ describe('drag and drop', function() {
var TARGET_LINE = 2; var TARGET_LINE = 2;
var FIRST_SOURCE_LINE = 5; var FIRST_SOURCE_LINE = 5;
var getLine = function(lineNumber) { var getLine = function (lineNumber) {
var $lines = helper.padInner$('div'); const $lines = helper.padInner$('div');
return $lines.slice(lineNumber, lineNumber + 1); return $lines.slice(lineNumber, lineNumber + 1);
} };
var createScriptWithSeveralLines = function(done) { var createScriptWithSeveralLines = function (done) {
// create some lines to be used on the tests // create some lines to be used on the tests
var $firstLine = helper.padInner$('div').first(); const $firstLine = helper.padInner$('div').first();
$firstLine.html('...<br>...<br>Target line []<br>...<br>...<br>Source line 1.<br>Source line 2.<br>'); $firstLine.html('...<br>...<br>Target line []<br>...<br>...<br>Source line 1.<br>Source line 2.<br>');
// wait for lines to be split // wait for lines to be split
helper.waitFor(function(){ helper.waitFor(() => {
var $lastSourceLine = getLine(FIRST_SOURCE_LINE + 1); const $lastSourceLine = getLine(FIRST_SOURCE_LINE + 1);
return $lastSourceLine.text() === 'Source line 2.'; return $lastSourceLine.text() === 'Source line 2.';
}).done(done); }).done(done);
} };
var selectPartOfSourceLine = function() { var selectPartOfSourceLine = function () {
var $sourceLine = getLine(FIRST_SOURCE_LINE); const $sourceLine = getLine(FIRST_SOURCE_LINE);
// select 'line 1' from 'Source line 1.' // select 'line 1' from 'Source line 1.'
var start = 'Source '.length; const start = 'Source '.length;
var end = start + 'line 1'.length; const end = start + 'line 1'.length;
helper.selectLines($sourceLine, $sourceLine, start, end); helper.selectLines($sourceLine, $sourceLine, start, end);
} };
var selectMultipleSourceLines = function() { var selectMultipleSourceLines = function () {
var $firstSourceLine = getLine(FIRST_SOURCE_LINE); const $firstSourceLine = getLine(FIRST_SOURCE_LINE);
var $lastSourceLine = getLine(FIRST_SOURCE_LINE + 1); const $lastSourceLine = getLine(FIRST_SOURCE_LINE + 1);
helper.selectLines($firstSourceLine, $lastSourceLine); helper.selectLines($firstSourceLine, $lastSourceLine);
} };
var dragSelectedTextAndDropItIntoMiddleOfLine = function(targetLineNumber) { var dragSelectedTextAndDropItIntoMiddleOfLine = function (targetLineNumber) {
// dragstart: start dragging content // dragstart: start dragging content
triggerEvent('dragstart'); triggerEvent('dragstart');
// drop: get HTML data from selected text // drop: get HTML data from selected text
var draggedHtml = getHtmlFromSelectedText(); const draggedHtml = getHtmlFromSelectedText();
triggerEvent('drop'); triggerEvent('drop');
// dragend: remove original content + insert HTML data into target // dragend: remove original content + insert HTML data into target
moveSelectionIntoTarget(draggedHtml, targetLineNumber); moveSelectionIntoTarget(draggedHtml, targetLineNumber);
triggerEvent('dragend'); triggerEvent('dragend');
} };
var getHtmlFromSelectedText = function() { var getHtmlFromSelectedText = function () {
var innerDocument = helper.padInner$.document; const innerDocument = helper.padInner$.document;
var range = innerDocument.getSelection().getRangeAt(0); const range = innerDocument.getSelection().getRangeAt(0);
var clonedSelection = range.cloneContents(); const clonedSelection = range.cloneContents();
var span = innerDocument.createElement('span'); const span = innerDocument.createElement('span');
span.id = 'buffer'; span.id = 'buffer';
span.appendChild(clonedSelection); span.appendChild(clonedSelection);
var draggedHtml = span.outerHTML; const draggedHtml = span.outerHTML;
return draggedHtml; return draggedHtml;
} };
var triggerEvent = function(eventName) { var triggerEvent = function (eventName) {
var event = helper.padInner$.Event(eventName); const event = helper.padInner$.Event(eventName);
helper.padInner$('#innerdocbody').trigger(event); helper.padInner$('#innerdocbody').trigger(event);
} };
var moveSelectionIntoTarget = function(draggedHtml, targetLineNumber) { var moveSelectionIntoTarget = function (draggedHtml, targetLineNumber) {
var innerDocument = helper.padInner$.document; const innerDocument = helper.padInner$.document;
// delete original content // delete original content
innerDocument.execCommand('delete'); innerDocument.execCommand('delete');
// set position to insert content on target line // set position to insert content on target line
var $target = getLine(targetLineNumber); const $target = getLine(targetLineNumber);
$target.sendkeys('{selectall}{rightarrow}{leftarrow}'); $target.sendkeys('{selectall}{rightarrow}{leftarrow}');
// Insert content. // Insert content.
// Based on http://stackoverflow.com/a/6691294, to be IE-compatible // Based on http://stackoverflow.com/a/6691294, to be IE-compatible
var range = innerDocument.getSelection().getRangeAt(0); const range = innerDocument.getSelection().getRangeAt(0);
var frag = innerDocument.createDocumentFragment(); const frag = innerDocument.createDocumentFragment();
var el = innerDocument.createElement('div'); const el = innerDocument.createElement('div');
el.innerHTML = draggedHtml; el.innerHTML = draggedHtml;
while (el.firstChild) { while (el.firstChild) {
frag.appendChild(el.firstChild); frag.appendChild(el.firstChild);
} }
range.insertNode(frag); range.insertNode(frag);
} };
}); });

View file

@ -1,135 +1,133 @@
describe("embed links", function(){ describe('embed links', function () {
var objectify = function (str) { const objectify = function (str) {
var hash = {}; const hash = {};
var parts = str.split('&'); const parts = str.split('&');
for(var i = 0; i < parts.length; i++) for (let i = 0; i < parts.length; i++) {
{ const keyValue = parts[i].split('=');
var keyValue = parts[i].split('=');
hash[keyValue[0]] = keyValue[1]; hash[keyValue[0]] = keyValue[1];
} }
return hash; return hash;
} };
var checkiFrameCode = function(embedCode, readonly){ const checkiFrameCode = function (embedCode, readonly) {
//turn the code into an html element // turn the code into an html element
var $embediFrame = $(embedCode); const $embediFrame = $(embedCode);
//read and check the frame attributes // read and check the frame attributes
var width = $embediFrame.attr("width"); const width = $embediFrame.attr('width');
var height = $embediFrame.attr("height"); const height = $embediFrame.attr('height');
var name = $embediFrame.attr("name"); const name = $embediFrame.attr('name');
expect(width).to.be('100%'); expect(width).to.be('100%');
expect(height).to.be('600'); expect(height).to.be('600');
expect(name).to.be(readonly ? "embed_readonly" : "embed_readwrite"); expect(name).to.be(readonly ? 'embed_readonly' : 'embed_readwrite');
//parse the url // parse the url
var src = $embediFrame.attr("src"); const src = $embediFrame.attr('src');
var questionMark = src.indexOf("?"); const questionMark = src.indexOf('?');
var url = src.substr(0,questionMark); const url = src.substr(0, questionMark);
var paramsStr = src.substr(questionMark+1); const paramsStr = src.substr(questionMark + 1);
var params = objectify(paramsStr); const params = objectify(paramsStr);
var expectedParams = { const expectedParams = {
showControls: 'true' showControls: 'true',
, showChat: 'true' showChat: 'true',
, showLineNumbers: 'true' showLineNumbers: 'true',
, useMonospaceFont: 'false' useMonospaceFont: 'false',
} };
//check the url // check the url
if(readonly){ if (readonly) {
expect(url.indexOf("r.") > 0).to.be(true); expect(url.indexOf('r.') > 0).to.be(true);
} else { } else {
expect(url).to.be(helper.padChrome$.window.location.href); expect(url).to.be(helper.padChrome$.window.location.href);
} }
//check if all parts of the url are like expected // check if all parts of the url are like expected
expect(params).to.eql(expectedParams); expect(params).to.eql(expectedParams);
} };
describe("read and write", function(){ describe('read and write', function () {
//create a new pad before each test run // create a new pad before each test run
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); helper.newPad(cb);
this.timeout(60000); this.timeout(60000);
}); });
describe("the share link", function(){ describe('the share link', function () {
it("is the actual pad url", function(done){ it('is the actual pad url', function (done) {
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//open share dropdown // open share dropdown
chrome$(".buttonicon-embed").click(); chrome$('.buttonicon-embed').click();
//get the link of the share field + the actual pad url and compare them // get the link of the share field + the actual pad url and compare them
var shareLink = chrome$("#linkinput").val(); const shareLink = chrome$('#linkinput').val();
var padURL = chrome$.window.location.href; const padURL = chrome$.window.location.href;
expect(shareLink).to.be(padURL); expect(shareLink).to.be(padURL);
done(); done();
}); });
}); });
describe("the embed as iframe code", function(){ describe('the embed as iframe code', function () {
it("is an iframe with the the correct url parameters and correct size", function(done){ it('is an iframe with the the correct url parameters and correct size', function (done) {
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//open share dropdown // open share dropdown
chrome$(".buttonicon-embed").click(); chrome$('.buttonicon-embed').click();
//get the link of the share field + the actual pad url and compare them // get the link of the share field + the actual pad url and compare them
var embedCode = chrome$("#embedinput").val(); const embedCode = chrome$('#embedinput').val();
checkiFrameCode(embedCode, false) checkiFrameCode(embedCode, false);
done(); done();
}); });
}); });
}); });
describe("when read only option is set", function(){ describe('when read only option is set', function () {
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); helper.newPad(cb);
this.timeout(60000); this.timeout(60000);
}); });
describe("the share link", function(){ describe('the share link', function () {
it("shows a read only url", function(done){ it('shows a read only url', function (done) {
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//open share dropdown // open share dropdown
chrome$(".buttonicon-embed").click(); chrome$('.buttonicon-embed').click();
chrome$('#readonlyinput').click(); chrome$('#readonlyinput').click();
chrome$('#readonlyinput:checkbox:not(:checked)').attr('checked', 'checked'); chrome$('#readonlyinput:checkbox:not(:checked)').attr('checked', 'checked');
//get the link of the share field + the actual pad url and compare them // get the link of the share field + the actual pad url and compare them
var shareLink = chrome$("#linkinput").val(); const shareLink = chrome$('#linkinput').val();
var containsReadOnlyLink = shareLink.indexOf("r.") > 0 const containsReadOnlyLink = shareLink.indexOf('r.') > 0;
expect(containsReadOnlyLink).to.be(true); expect(containsReadOnlyLink).to.be(true);
done(); done();
}); });
}); });
describe("the embed as iframe code", function(){ describe('the embed as iframe code', function () {
it("is an iframe with the the correct url parameters and correct size", function(done){ it('is an iframe with the the correct url parameters and correct size', function (done) {
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//open share dropdown // open share dropdown
chrome$(".buttonicon-embed").click(); chrome$('.buttonicon-embed').click();
//check read only checkbox, a bit hacky // check read only checkbox, a bit hacky
chrome$('#readonlyinput').click(); chrome$('#readonlyinput').click();
chrome$('#readonlyinput:checkbox:not(:checked)').attr('checked', 'checked'); chrome$('#readonlyinput:checkbox:not(:checked)').attr('checked', 'checked');
//get the link of the share field + the actual pad url and compare them // get the link of the share field + the actual pad url and compare them
var embedCode = chrome$("#embedinput").val(); const embedCode = chrome$('#embedinput').val();
checkiFrameCode(embedCode, true); checkiFrameCode(embedCode, true);
done(); done();
}); });
}); });
}); });
}); });

View file

@ -1,32 +1,30 @@
describe("enter keystroke", function(){ describe('enter keystroke', function () {
//create a new pad before each test run // create a new pad before each test run
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); helper.newPad(cb);
this.timeout(60000); this.timeout(60000);
}); });
it("creates a new line & puts cursor onto a new line", function(done) { it('creates a new line & puts cursor onto a new line', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe // get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first(); const $firstTextElement = inner$('div').first();
// get the original string value minus the last char // get the original string value minus the last char
var originalTextValue = $firstTextElement.text(); const originalTextValue = $firstTextElement.text();
// simulate key presses to enter content // simulate key presses to enter content
$firstTextElement.sendkeys('{enter}'); $firstTextElement.sendkeys('{enter}');
//ace creates a new dom element when you press a keystroke, so just get the first text element again // ace creates a new dom element when you press a keystroke, so just get the first text element again
var $newFirstTextElement = inner$("div").first(); const $newFirstTextElement = inner$('div').first();
helper.waitFor(function(){ helper.waitFor(() => inner$('div').first().text() === '').done(() => {
return inner$("div").first().text() === ""; const $newSecondLine = inner$('div').first().next();
}).done(function(){ const newFirstTextElementValue = inner$('div').first().text();
var $newSecondLine = inner$("div").first().next(); expect(newFirstTextElementValue).to.be(''); // expect the first line to be blank
var newFirstTextElementValue = inner$("div").first().text();
expect(newFirstTextElementValue).to.be(""); // expect the first line to be blank
expect($newSecondLine.text()).to.be(originalTextValue); // expect the second line to be the same as the original first line. expect($newSecondLine.text()).to.be(originalTextValue); // expect the second line to be the same as the original first line.
done(); done();
}); });

View file

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

View file

@ -1,34 +1,34 @@
describe("the test helper", function(){ describe('the test helper', function () {
describe("the newPad method", function(){ describe('the newPad method', function () {
xit("doesn't leak memory if you creates iframes over and over again", function(done){ xit("doesn't leak memory if you creates iframes over and over again", function (done) {
this.timeout(100000); this.timeout(100000);
var times = 10; let times = 10;
var loadPad = function(){ var loadPad = function () {
helper.newPad(function(){ helper.newPad(() => {
times--; times--;
if(times > 0){ if (times > 0) {
loadPad(); loadPad();
} else { } else {
done(); done();
} }
}) });
} };
loadPad(); loadPad();
}); });
it("gives me 3 jquery instances of chrome, outer and inner", function(done){ it('gives me 3 jquery instances of chrome, outer and inner', function (done) {
this.timeout(10000); this.timeout(10000);
helper.newPad(function(){ helper.newPad(() => {
//check if the jquery selectors have the desired elements // check if the jquery selectors have the desired elements
expect(helper.padChrome$("#editbar").length).to.be(1); expect(helper.padChrome$('#editbar').length).to.be(1);
expect(helper.padOuter$("#outerdocbody").length).to.be(1); expect(helper.padOuter$('#outerdocbody').length).to.be(1);
expect(helper.padInner$("#innerdocbody").length).to.be(1); expect(helper.padInner$('#innerdocbody').length).to.be(1);
//check if the document object was set correctly // check if the document object was set correctly
expect(helper.padChrome$.window.document).to.be(helper.padChrome$.document); expect(helper.padChrome$.window.document).to.be(helper.padChrome$.document);
expect(helper.padOuter$.window.document).to.be(helper.padOuter$.document); expect(helper.padOuter$.window.document).to.be(helper.padOuter$.document);
expect(helper.padInner$.window.document).to.be(helper.padInner$.document); expect(helper.padInner$.window.document).to.be(helper.padInner$.document);
@ -43,7 +43,7 @@ describe("the test helper", function(){
// However this doesn't seem to always be easily replicated, so this // 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 // timeout may or may end up in the code. None the less, we test here
// to catch it if the bug comes up again. // to catch it if the bug comes up again.
it("clears cookies", function(done) { it('clears cookies', function (done) {
this.timeout(60000); this.timeout(60000);
// set cookies far into the future to make sure they're not expired yet // set cookies far into the future to make sure they're not expired yet
@ -53,20 +53,20 @@ describe("the test helper", function(){
expect(window.document.cookie).to.contain('token=foo'); expect(window.document.cookie).to.contain('token=foo');
expect(window.document.cookie).to.contain('language=bar'); expect(window.document.cookie).to.contain('language=bar');
helper.newPad(function(){ helper.newPad(() => {
// helper function seems to have cleared cookies // 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 // NOTE: this doesn't yet mean it's proven to have taken effect by this point in execution
var firstCookie = window.document.cookie const firstCookie = window.document.cookie;
expect(firstCookie).to.not.contain('token=foo'); expect(firstCookie).to.not.contain('token=foo');
expect(firstCookie).to.not.contain('language=bar'); expect(firstCookie).to.not.contain('language=bar');
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
// click on the settings button to make settings visible // click on the settings button to make settings visible
var $userButton = chrome$(".buttonicon-showusers"); const $userButton = chrome$('.buttonicon-showusers');
$userButton.click(); $userButton.click();
var $usernameInput = chrome$("#myusernameedit"); const $usernameInput = chrome$('#myusernameedit');
$usernameInput.click(); $usernameInput.click();
$usernameInput.val('John McLear'); $usernameInput.val('John McLear');
@ -84,9 +84,9 @@ describe("the test helper", function(){
// be sure. // be sure.
expect(window.document.cookie).to.not.contain('prefsHtml=baz'); expect(window.document.cookie).to.not.contain('prefsHtml=baz');
setTimeout(function(){ //give it a second to save the username on the server side setTimeout(() => { // give it a second to save the username on the server side
helper.newPad(function(){ // get a new pad, let it clear the cookies helper.newPad(() => { // get a new pad, let it clear the cookies
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
// helper function seems to have cleared cookies // helper function seems to have cleared cookies
// NOTE: this doesn't yet mean cookies were cleared effectively. // NOTE: this doesn't yet mean cookies were cleared effectively.
@ -99,11 +99,11 @@ describe("the test helper", function(){
expect(window.document.cookie).to.not.be(firstCookie); expect(window.document.cookie).to.not.be(firstCookie);
// click on the settings button to make settings visible // click on the settings button to make settings visible
var $userButton = chrome$(".buttonicon-showusers"); const $userButton = chrome$('.buttonicon-showusers');
$userButton.click(); $userButton.click();
// confirm that the session was actually cleared // confirm that the session was actually cleared
var $usernameInput = chrome$("#myusernameedit"); const $usernameInput = chrome$('#myusernameedit');
expect($usernameInput.val()).to.be(''); expect($usernameInput.val()).to.be('');
done(); done();
@ -112,43 +112,41 @@ describe("the test helper", function(){
}); });
}); });
it("sets pad prefs cookie", function(done) { it('sets pad prefs cookie', function (done) {
this.timeout(60000); this.timeout(60000);
helper.newPad({ helper.newPad({
padPrefs: {foo:"bar"}, padPrefs: {foo: 'bar'},
cb: function(){ cb() {
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
expect(chrome$.document.cookie).to.contain('prefsHttp=%7B%22'); expect(chrome$.document.cookie).to.contain('prefsHttp=%7B%22');
expect(chrome$.document.cookie).to.contain('foo%22%3A%22bar'); expect(chrome$.document.cookie).to.contain('foo%22%3A%22bar');
done(); done();
} },
}); });
}); });
}); });
describe("the waitFor method", function(){ describe('the waitFor method', function () {
it("takes a timeout and waits long enough", function(done){ it('takes a timeout and waits long enough', function (done) {
this.timeout(2000); this.timeout(2000);
var startTime = Date.now(); const startTime = Date.now();
helper.waitFor(function(){ helper.waitFor(() => false, 1500).fail(() => {
return false; const duration = Date.now() - startTime;
}, 1500).fail(function(){
var duration = Date.now() - startTime;
expect(duration).to.be.greaterThan(1490); expect(duration).to.be.greaterThan(1490);
done(); done();
}); });
}); });
it("takes an interval and checks on every interval", function(done){ it('takes an interval and checks on every interval', function (done) {
this.timeout(4000); this.timeout(4000);
var checks = 0; let checks = 0;
helper.waitFor(function(){ helper.waitFor(() => {
checks++; checks++;
return false; return false;
}, 2000, 100).fail(function(){ }, 2000, 100).fail(() => {
// One at the beginning, and 19-20 more depending on whether it's the timeout or the final // One at the beginning, and 19-20 more depending on whether it's the timeout or the final
// poll that wins at 2000ms. // poll that wins at 2000ms.
expect(checks).to.be.greaterThan(15); expect(checks).to.be.greaterThan(15);
@ -157,7 +155,7 @@ describe("the test helper", function(){
}); });
}); });
it('rejects if the predicate throws', async function() { it('rejects if the predicate throws', async function () {
let err; let err;
await helper.waitFor(() => { throw new Error('test exception'); }) await helper.waitFor(() => { throw new Error('test exception'); })
.fail(() => {}) // Suppress the redundant uncatchable exception. .fail(() => {}) // Suppress the redundant uncatchable exception.
@ -166,44 +164,38 @@ describe("the test helper", function(){
expect(err.message).to.be('test exception'); expect(err.message).to.be('test exception');
}); });
describe("returns a deferred object", function(){ describe('returns a deferred object', function () {
it("it calls done after success", function(done){ it('it calls done after success', function (done) {
helper.waitFor(function(){ helper.waitFor(() => true).done(() => {
return true;
}).done(function(){
done(); done();
}); });
}); });
it("calls fail after failure", function(done){ it('calls fail after failure', function (done) {
helper.waitFor(function(){ helper.waitFor(() => false, 0).fail(() => {
return false;
},0).fail(function(){
done(); done();
}); });
}); });
xit("throws if you don't listen for fails", function(done){ xit("throws if you don't listen for fails", function (done) {
var onerror = window.onerror; const onerror = window.onerror;
window.onerror = function(){ window.onerror = function () {
window.onerror = onerror; window.onerror = onerror;
done(); done();
} };
helper.waitFor(function(){ helper.waitFor(() => false, 100);
return false;
},100);
}); });
}); });
describe('checks first then sleeps', function() { describe('checks first then sleeps', function () {
it('resolves quickly if the predicate is immediately true', async function() { it('resolves quickly if the predicate is immediately true', async function () {
const before = Date.now(); const before = Date.now();
await helper.waitFor(() => true, 1000, 900); await helper.waitFor(() => true, 1000, 900);
expect(Date.now() - before).to.be.lessThan(800); expect(Date.now() - before).to.be.lessThan(800);
}); });
it('polls exactly once if timeout < interval', async function() { it('polls exactly once if timeout < interval', async function () {
let calls = 0; let calls = 0;
await helper.waitFor(() => { calls++; }, 1, 1000) await helper.waitFor(() => { calls++; }, 1, 1000)
.fail(() => {}) // Suppress the redundant uncatchable exception. .fail(() => {}) // Suppress the redundant uncatchable exception.
@ -211,18 +203,18 @@ describe("the test helper", function(){
expect(calls).to.be(1); expect(calls).to.be(1);
}); });
it('resolves if condition is immediately true even if timeout is 0', async function() { it('resolves if condition is immediately true even if timeout is 0', async function () {
await helper.waitFor(() => true, 0); await helper.waitFor(() => true, 0);
}); });
}); });
}); });
describe('the waitForPromise method', function() { describe('the waitForPromise method', function () {
it('returns a Promise', async function() { it('returns a Promise', async function () {
expect(helper.waitForPromise(() => true)).to.be.a(Promise); expect(helper.waitForPromise(() => true)).to.be.a(Promise);
}); });
it('takes a timeout and waits long enough', async function() { it('takes a timeout and waits long enough', async function () {
this.timeout(2000); this.timeout(2000);
const startTime = Date.now(); const startTime = Date.now();
let rejected; let rejected;
@ -233,7 +225,7 @@ describe("the test helper", function(){
expect(duration).to.be.greaterThan(1490); expect(duration).to.be.greaterThan(1490);
}); });
it('takes an interval and checks on every interval', async function() { it('takes an interval and checks on every interval', async function () {
this.timeout(4000); this.timeout(4000);
let checks = 0; let checks = 0;
let rejected; let rejected;
@ -248,45 +240,45 @@ describe("the test helper", function(){
}); });
}); });
describe("the selectLines method", function(){ describe('the selectLines method', function () {
// function to support tests, use a single way to represent whitespaces // function to support tests, use a single way to represent whitespaces
var cleanText = function(text){ const cleanText = function (text) {
return text return text
// IE replaces line breaks with a whitespace, so we need to unify its behavior // IE replaces line breaks with a whitespace, so we need to unify its behavior
// for other browsers, to have all tests running for all browsers // for other browsers, to have all tests running for all browsers
.replace(/\n/gi, "") .replace(/\n/gi, '')
.replace(/\s/gi, " "); .replace(/\s/gi, ' ');
} };
before(function(done){ before(function (done) {
helper.newPad(function() { helper.newPad(() => {
// create some lines to be used on the tests // create some lines to be used on the tests
var $firstLine = helper.padInner$("div").first(); const $firstLine = helper.padInner$('div').first();
$firstLine.sendkeys("{selectall}some{enter}short{enter}lines{enter}to test{enter}{enter}"); $firstLine.sendkeys('{selectall}some{enter}short{enter}lines{enter}to test{enter}{enter}');
// wait for lines to be split // wait for lines to be split
helper.waitFor(function(){ helper.waitFor(() => {
var $fourthLine = helper.padInner$("div").eq(3); const $fourthLine = helper.padInner$('div').eq(3);
return $fourthLine.text() === "to test"; return $fourthLine.text() === 'to test';
}).done(done); }).done(done);
}); });
this.timeout(60000); this.timeout(60000);
}); });
it("changes editor selection to be between startOffset of $startLine and endOffset of $endLine", function(done){ it('changes editor selection to be between startOffset of $startLine and endOffset of $endLine', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var startOffset = 2; const startOffset = 2;
var endOffset = 4; const endOffset = 4;
var $lines = inner$("div"); const $lines = inner$('div');
var $startLine = $lines.eq(1); const $startLine = $lines.eq(1);
var $endLine = $lines.eq(3); const $endLine = $lines.eq(3);
helper.selectLines($startLine, $endLine, startOffset, endOffset); helper.selectLines($startLine, $endLine, startOffset, endOffset);
var selection = inner$.document.getSelection(); const selection = inner$.document.getSelection();
/* /*
* replace() is required here because Firefox keeps the line breaks. * replace() is required here because Firefox keeps the line breaks.
@ -295,24 +287,24 @@ describe("the test helper", function(){
* is not consistent between browsers but that's the situation so that's * is not consistent between browsers but that's the situation so that's
* how I'm covering it in this test. * how I'm covering it in this test.
*/ */
expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm,""))).to.be("ort lines to t"); expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm, ''))).to.be('ort lines to t');
done(); done();
}); });
it("ends selection at beginning of $endLine when it is an empty line", function(done){ it('ends selection at beginning of $endLine when it is an empty line', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var startOffset = 2; const startOffset = 2;
var endOffset = 1; const endOffset = 1;
var $lines = inner$("div"); const $lines = inner$('div');
var $startLine = $lines.eq(1); const $startLine = $lines.eq(1);
var $endLine = $lines.eq(4); const $endLine = $lines.eq(4);
helper.selectLines($startLine, $endLine, startOffset, endOffset); helper.selectLines($startLine, $endLine, startOffset, endOffset);
var selection = inner$.document.getSelection(); const selection = inner$.document.getSelection();
/* /*
* replace() is required here because Firefox keeps the line breaks. * replace() is required here because Firefox keeps the line breaks.
@ -321,24 +313,24 @@ describe("the test helper", function(){
* is not consistent between browsers but that's the situation so that's * is not consistent between browsers but that's the situation so that's
* how I'm covering it in this test. * how I'm covering it in this test.
*/ */
expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm,""))).to.be("ort lines to test"); expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm, ''))).to.be('ort lines to test');
done(); done();
}); });
it("ends selection at beginning of $endLine when its offset is zero", function(done){ it('ends selection at beginning of $endLine when its offset is zero', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var startOffset = 2; const startOffset = 2;
var endOffset = 0; const endOffset = 0;
var $lines = inner$("div"); const $lines = inner$('div');
var $startLine = $lines.eq(1); const $startLine = $lines.eq(1);
var $endLine = $lines.eq(3); const $endLine = $lines.eq(3);
helper.selectLines($startLine, $endLine, startOffset, endOffset); helper.selectLines($startLine, $endLine, startOffset, endOffset);
var selection = inner$.document.getSelection(); const selection = inner$.document.getSelection();
/* /*
* replace() is required here because Firefox keeps the line breaks. * replace() is required here because Firefox keeps the line breaks.
@ -347,24 +339,24 @@ describe("the test helper", function(){
* is not consistent between browsers but that's the situation so that's * is not consistent between browsers but that's the situation so that's
* how I'm covering it in this test. * how I'm covering it in this test.
*/ */
expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm,""))).to.be("ort lines "); expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm, ''))).to.be('ort lines ');
done(); done();
}); });
it("selects full line when offset is longer than line content", function(done){ it('selects full line when offset is longer than line content', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var startOffset = 2; const startOffset = 2;
var endOffset = 50; const endOffset = 50;
var $lines = inner$("div"); const $lines = inner$('div');
var $startLine = $lines.eq(1); const $startLine = $lines.eq(1);
var $endLine = $lines.eq(3); const $endLine = $lines.eq(3);
helper.selectLines($startLine, $endLine, startOffset, endOffset); helper.selectLines($startLine, $endLine, startOffset, endOffset);
var selection = inner$.document.getSelection(); const selection = inner$.document.getSelection();
/* /*
* replace() is required here because Firefox keeps the line breaks. * replace() is required here because Firefox keeps the line breaks.
@ -373,21 +365,21 @@ describe("the test helper", function(){
* is not consistent between browsers but that's the situation so that's * is not consistent between browsers but that's the situation so that's
* how I'm covering it in this test. * how I'm covering it in this test.
*/ */
expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm,""))).to.be("ort lines to test"); expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm, ''))).to.be('ort lines to test');
done(); done();
}); });
it("selects all text between beginning of $startLine and end of $endLine when no offset is provided", function(done){ it('selects all text between beginning of $startLine and end of $endLine when no offset is provided', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var $lines = inner$("div"); const $lines = inner$('div');
var $startLine = $lines.eq(1); const $startLine = $lines.eq(1);
var $endLine = $lines.eq(3); const $endLine = $lines.eq(3);
helper.selectLines($startLine, $endLine); helper.selectLines($startLine, $endLine);
var selection = inner$.document.getSelection(); const selection = inner$.document.getSelection();
/* /*
* replace() is required here because Firefox keeps the line breaks. * replace() is required here because Firefox keeps the line breaks.
@ -396,73 +388,73 @@ describe("the test helper", function(){
* is not consistent between browsers but that's the situation so that's * is not consistent between browsers but that's the situation so that's
* how I'm covering it in this test. * how I'm covering it in this test.
*/ */
expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm,""))).to.be("short lines to test"); expect(cleanText(selection.toString().replace(/(\r\n|\n|\r)/gm, ''))).to.be('short lines to test');
done(); done();
}); });
}); });
describe('helper',function(){ describe('helper', function () {
before(function(cb){ before(function (cb) {
helper.newPad(function(){ helper.newPad(() => {
cb(); cb();
}) });
}) });
it(".textLines() returns the text of the pad as strings", async function(){ it('.textLines() returns the text of the pad as strings', async function () {
let lines = helper.textLines(); const lines = helper.textLines();
let defaultText = helper.defaultText(); const defaultText = helper.defaultText();
expect(Array.isArray(lines)).to.be(true); expect(Array.isArray(lines)).to.be(true);
expect(lines[0]).to.be.an('string'); expect(lines[0]).to.be.an('string');
// @todo // @todo
// final "\n" is added automatically, but my understanding is this should happen // final "\n" is added automatically, but my understanding is this should happen
// only when the default text does not end with "\n" already // only when the default text does not end with "\n" already
expect(lines.join("\n")+"\n").to.equal(defaultText); expect(`${lines.join('\n')}\n`).to.equal(defaultText);
}) });
it(".linesDiv() returns the text of the pad as div elements", async function(){ it('.linesDiv() returns the text of the pad as div elements', async function () {
let lines = helper.linesDiv(); const lines = helper.linesDiv();
let defaultText = helper.defaultText(); const defaultText = helper.defaultText();
expect(Array.isArray(lines)).to.be(true); expect(Array.isArray(lines)).to.be(true);
expect(lines[0]).to.be.an('object'); expect(lines[0]).to.be.an('object');
expect(lines[0].text()).to.be.an('string'); expect(lines[0].text()).to.be.an('string');
_.each(defaultText.split("\n"), function(line, index){ _.each(defaultText.split('\n'), (line, index) => {
//last line of default text // last line of default text
if(index === lines.length){ if (index === lines.length) {
expect(line).to.equal(''); expect(line).to.equal('');
} else { } else {
expect(lines[index].text()).to.equal(line); expect(lines[index].text()).to.equal(line);
} }
}) });
}) });
it(".edit() defaults to send an edit to the first line", async function(){ it('.edit() defaults to send an edit to the first line', async function () {
let firstLine = helper.textLines()[0]; const firstLine = helper.textLines()[0];
await helper.edit("line") await helper.edit('line');
expect(helper.textLines()[0]).to.be(`line${firstLine}`); expect(helper.textLines()[0]).to.be(`line${firstLine}`);
}) });
it(".edit() to the line specified with parameter lineNo", async function(){ it('.edit() to the line specified with parameter lineNo', async function () {
let firstLine = helper.textLines()[0]; const firstLine = helper.textLines()[0];
await helper.edit("second line", 2); await helper.edit('second line', 2);
let text = helper.textLines(); const text = helper.textLines();
expect(text[0]).to.equal(firstLine); expect(text[0]).to.equal(firstLine);
expect(text[1]).to.equal("second line"); expect(text[1]).to.equal('second line');
}) });
it(".edit() supports sendkeys syntax ({selectall},{del},{enter})", async function(){ it('.edit() supports sendkeys syntax ({selectall},{del},{enter})', async function () {
expect(helper.textLines()[0]).to.not.equal(''); expect(helper.textLines()[0]).to.not.equal('');
// select first line // select first line
helper.linesDiv()[0].sendkeys("{selectall}") helper.linesDiv()[0].sendkeys('{selectall}');
// delete first line // delete first line
await helper.edit("{del}") await helper.edit('{del}');
expect(helper.textLines()[0]).to.be(''); expect(helper.textLines()[0]).to.be('');
let noOfLines = helper.textLines().length; const noOfLines = helper.textLines().length;
await helper.edit("{enter}") await helper.edit('{enter}');
expect(helper.textLines().length).to.be(noOfLines+1); expect(helper.textLines().length).to.be(noOfLines + 1);
}) });
}) });
}); });

View file

@ -1,176 +1,162 @@
describe("import functionality", function(){ describe('import functionality', function () {
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); // creates a new pad helper.newPad(cb); // creates a new pad
this.timeout(60000); this.timeout(60000);
}); });
function getinnertext(){ function getinnertext() {
var inner = helper.padInner$ const inner = helper.padInner$;
if(!inner){ if (!inner) {
return "" return '';
} }
var newtext = "" let newtext = '';
inner("div").each(function(line,el){ inner('div').each((line, el) => {
newtext += el.innerHTML+"\n" newtext += `${el.innerHTML}\n`;
}) });
return newtext return newtext;
} }
function importrequest(data,importurl,type){ function importrequest(data, importurl, type) {
var success; let success;
var error; let error;
var result = $.ajax({ const result = $.ajax({
url: importurl, url: importurl,
type: "post", type: 'post',
processData: false, processData: false,
async: false, async: false,
contentType: 'multipart/form-data; boundary=boundary', contentType: 'multipart/form-data; boundary=boundary',
accepts: { accepts: {
text: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" text: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
}, },
data: 'Content-Type: multipart/form-data; boundary=--boundary\r\n\r\n--boundary\r\nContent-Disposition: form-data; name="file"; filename="import.'+type+'"\r\nContent-Type: text/plain\r\n\r\n' + data + '\r\n\r\n--boundary', data: `Content-Type: multipart/form-data; boundary=--boundary\r\n\r\n--boundary\r\nContent-Disposition: form-data; name="file"; filename="import.${type}"\r\nContent-Type: text/plain\r\n\r\n${data}\r\n\r\n--boundary`,
error: function(res){ error(res) {
error = res error = res;
},
});
expect(error).to.be(undefined);
return result;
} }
}) function exportfunc(link) {
expect(error).to.be(undefined) const exportresults = [];
return result
}
function exportfunc(link){
var exportresults = []
$.ajaxSetup({ $.ajaxSetup({
async:false async: false,
}) });
$.get(link+"/export/html",function(data){ $.get(`${link}/export/html`, (data) => {
var start = data.indexOf("<body>") const start = data.indexOf('<body>');
var end = data.indexOf("</body>") const end = data.indexOf('</body>');
var html = data.substr(start+6,end-start-6) const html = data.substr(start + 6, end - start - 6);
exportresults.push(["html",html]) exportresults.push(['html', html]);
}) });
$.get(link+"/export/txt",function(data){ $.get(`${link}/export/txt`, (data) => {
exportresults.push(["txt",data]) exportresults.push(['txt', data]);
}) });
return exportresults return exportresults;
} }
xit("import a pad with newlines from txt", function(done){ xit('import a pad with newlines from txt', function (done) {
var importurl = helper.padChrome$.window.location.href+'/import' const importurl = `${helper.padChrome$.window.location.href}/import`;
var textWithNewLines = 'imported text\nnewline' const textWithNewLines = 'imported text\nnewline';
importrequest(textWithNewLines,importurl,"txt") importrequest(textWithNewLines, importurl, 'txt');
helper.waitFor(function(){ helper.waitFor(() => expect(getinnertext()).to.be('<span class="">imported text</span>\n<span class="">newline</span>\n<br>\n'));
return expect(getinnertext()).to.be('<span class="">imported text</span>\n<span class="">newline</span>\n<br>\n') const results = exportfunc(helper.padChrome$.window.location.href);
}) expect(results[0][1]).to.be('imported text<br>newline<br><br>');
var results = exportfunc(helper.padChrome$.window.location.href) expect(results[1][1]).to.be('imported text\nnewline\n\n');
expect(results[0][1]).to.be("imported text<br>newline<br><br>") done();
expect(results[1][1]).to.be("imported text\nnewline\n\n") });
done() xit('import a pad with newlines from html', function (done) {
}) const importurl = `${helper.padChrome$.window.location.href}/import`;
xit("import a pad with newlines from html", function(done){ const htmlWithNewLines = '<html><body>htmltext<br/>newline</body></html>';
var importurl = helper.padChrome$.window.location.href+'/import' importrequest(htmlWithNewLines, importurl, 'html');
var htmlWithNewLines = '<html><body>htmltext<br/>newline</body></html>' helper.waitFor(() => expect(getinnertext()).to.be('<span class="">htmltext</span>\n<span class="">newline</span>\n<br>\n'));
importrequest(htmlWithNewLines,importurl,"html") const results = exportfunc(helper.padChrome$.window.location.href);
helper.waitFor(function(){ expect(results[0][1]).to.be('htmltext<br>newline<br><br>');
return expect(getinnertext()).to.be('<span class="">htmltext</span>\n<span class="">newline</span>\n<br>\n') expect(results[1][1]).to.be('htmltext\nnewline\n\n');
}) done();
var results = exportfunc(helper.padChrome$.window.location.href) });
expect(results[0][1]).to.be("htmltext<br>newline<br><br>") xit('import a pad with attributes from html', function (done) {
expect(results[1][1]).to.be("htmltext\nnewline\n\n") const importurl = `${helper.padChrome$.window.location.href}/import`;
done() const htmlWithNewLines = '<html><body>htmltext<br/><span class="b s i u"><b><i><s><u>newline</u></s></i></b></body></html>';
}) importrequest(htmlWithNewLines, importurl, 'html');
xit("import a pad with attributes from html", function(done){ helper.waitFor(() => expect(getinnertext()).to.be('<span class="">htmltext</span>\n<span class="b i s u"><b><i><s><u>newline</u></s></i></b></span>\n<br>\n'));
var importurl = helper.padChrome$.window.location.href+'/import' const results = exportfunc(helper.padChrome$.window.location.href);
var htmlWithNewLines = '<html><body>htmltext<br/><span class="b s i u"><b><i><s><u>newline</u></s></i></b></body></html>' expect(results[0][1]).to.be('htmltext<br><strong><em><s><u>newline</u></s></em></strong><br><br>');
importrequest(htmlWithNewLines,importurl,"html") expect(results[1][1]).to.be('htmltext\nnewline\n\n');
helper.waitFor(function(){ done();
return expect(getinnertext()).to.be('<span class="">htmltext</span>\n<span class="b i s u"><b><i><s><u>newline</u></s></i></b></span>\n<br>\n') });
}) xit('import a pad with bullets from html', function (done) {
var results = exportfunc(helper.padChrome$.window.location.href) const importurl = `${helper.padChrome$.window.location.href}/import`;
expect(results[0][1]).to.be('htmltext<br><strong><em><s><u>newline</u></s></em></strong><br><br>') const htmlWithBullets = '<html><body><ul class="list-bullet1"><li>bullet line 1</li><li>bullet line 2</li><ul class="list-bullet2"><li>bullet2 line 1</li><li>bullet2 line 2</li></ul></ul></body></html>';
expect(results[1][1]).to.be('htmltext\nnewline\n\n') importrequest(htmlWithBullets, importurl, 'html');
done() helper.waitFor(() => expect(getinnertext()).to.be('\
})
xit("import a pad with bullets from html", function(done){
var importurl = helper.padChrome$.window.location.href+'/import'
var htmlWithBullets = '<html><body><ul class="list-bullet1"><li>bullet line 1</li><li>bullet line 2</li><ul class="list-bullet2"><li>bullet2 line 1</li><li>bullet2 line 2</li></ul></ul></body></html>'
importrequest(htmlWithBullets,importurl,"html")
helper.waitFor(function(){
return expect(getinnertext()).to.be('\
<ul class="list-bullet1"><li><span class="">bullet line 1</span></li></ul>\n\ <ul class="list-bullet1"><li><span class="">bullet line 1</span></li></ul>\n\
<ul class="list-bullet1"><li><span class="">bullet line 2</span></li></ul>\n\ <ul class="list-bullet1"><li><span class="">bullet line 2</span></li></ul>\n\
<ul class="list-bullet2"><li><span class="">bullet2 line 1</span></li></ul>\n\ <ul class="list-bullet2"><li><span class="">bullet2 line 1</span></li></ul>\n\
<ul class="list-bullet2"><li><span class="">bullet2 line 2</span></li></ul>\n\ <ul class="list-bullet2"><li><span class="">bullet2 line 2</span></li></ul>\n\
<br>\n') <br>\n'));
}) const results = exportfunc(helper.padChrome$.window.location.href);
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><li>bullet2 line 2</li></ul></ul><br>');
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><li>bullet2 line 2</li></ul></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* bullet2 line 2\n\n');
expect(results[1][1]).to.be('\t* bullet line 1\n\t* bullet line 2\n\t\t* bullet2 line 1\n\t\t* bullet2 line 2\n\n') done();
done() });
}) xit('import a pad with bullets and newlines from html', function (done) {
xit("import a pad with bullets and newlines from html", function(done){ const importurl = `${helper.padChrome$.window.location.href}/import`;
var importurl = helper.padChrome$.window.location.href+'/import' const htmlWithBullets = '<html><body><ul class="list-bullet1"><li>bullet line 1</li></ul><br/><ul class="list-bullet1"><li>bullet line 2</li><ul class="list-bullet2"><li>bullet2 line 1</li></ul></ul><br/><ul class="list-bullet1"><ul class="list-bullet2"><li>bullet2 line 2</li></ul></ul></body></html>';
var htmlWithBullets = '<html><body><ul class="list-bullet1"><li>bullet line 1</li></ul><br/><ul class="list-bullet1"><li>bullet line 2</li><ul class="list-bullet2"><li>bullet2 line 1</li></ul></ul><br/><ul class="list-bullet1"><ul class="list-bullet2"><li>bullet2 line 2</li></ul></ul></body></html>' importrequest(htmlWithBullets, importurl, 'html');
importrequest(htmlWithBullets,importurl,"html") helper.waitFor(() => expect(getinnertext()).to.be('\
helper.waitFor(function(){
return expect(getinnertext()).to.be('\
<ul class="list-bullet1"><li><span class="">bullet line 1</span></li></ul>\n\ <ul class="list-bullet1"><li><span class="">bullet line 1</span></li></ul>\n\
<br>\n\ <br>\n\
<ul class="list-bullet1"><li><span class="">bullet line 2</span></li></ul>\n\ <ul class="list-bullet1"><li><span class="">bullet line 2</span></li></ul>\n\
<ul class="list-bullet2"><li><span class="">bullet2 line 1</span></li></ul>\n\ <ul class="list-bullet2"><li><span class="">bullet2 line 1</span></li></ul>\n\
<br>\n\ <br>\n\
<ul class="list-bullet2"><li><span class="">bullet2 line 2</span></li></ul>\n\ <ul class="list-bullet2"><li><span class="">bullet2 line 2</span></li></ul>\n\
<br>\n') <br>\n'));
}) const results = exportfunc(helper.padChrome$.window.location.href);
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 class="bullet"><li>bullet2 line 2</li></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 class="bullet"><li>bullet2 line 2</li></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* bullet2 line 2\n\n');
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* bullet2 line 2\n\n') done();
done() });
}) xit('import a pad with bullets and newlines and attributes from html', function (done) {
xit("import a pad with bullets and newlines and attributes from html", function(done){ const importurl = `${helper.padChrome$.window.location.href}/import`;
var importurl = helper.padChrome$.window.location.href+'/import' const htmlWithBullets = '<html><body><ul class="list-bullet1"><li>bullet line 1</li></ul><br/><ul class="list-bullet1"><li>bullet line 2</li><ul class="list-bullet2"><li>bullet2 line 1</li></ul></ul><br/><ul class="list-bullet1"><ul class="list-bullet2"><ul class="list-bullet3"><ul class="list-bullet4"><li><span class="b s i u"><b><i><s><u>bullet4 line 2 bisu</u></s></i></b></span></li><li><span class="b s "><b><s>bullet4 line 2 bs</s></b></span></li><li><span class="u"><u>bullet4 line 2 u</u></span><span class="u i s"><i><s><u>uis</u></s></i></span></li></ul></ul></ul></ul></body></html>';
var htmlWithBullets = '<html><body><ul class="list-bullet1"><li>bullet line 1</li></ul><br/><ul class="list-bullet1"><li>bullet line 2</li><ul class="list-bullet2"><li>bullet2 line 1</li></ul></ul><br/><ul class="list-bullet1"><ul class="list-bullet2"><ul class="list-bullet3"><ul class="list-bullet4"><li><span class="b s i u"><b><i><s><u>bullet4 line 2 bisu</u></s></i></b></span></li><li><span class="b s "><b><s>bullet4 line 2 bs</s></b></span></li><li><span class="u"><u>bullet4 line 2 u</u></span><span class="u i s"><i><s><u>uis</u></s></i></span></li></ul></ul></ul></ul></body></html>' importrequest(htmlWithBullets, importurl, 'html');
importrequest(htmlWithBullets,importurl,"html") helper.waitFor(() => expect(getinnertext()).to.be('\
helper.waitFor(function(){
return expect(getinnertext()).to.be('\
<ul class="list-bullet1"><li><span class="">bullet line 1</span></li></ul>\n\<br>\n\ <ul class="list-bullet1"><li><span class="">bullet line 1</span></li></ul>\n\<br>\n\
<ul class="list-bullet1"><li><span class="">bullet line 2</span></li></ul>\n\ <ul class="list-bullet1"><li><span class="">bullet line 2</span></li></ul>\n\
<ul class="list-bullet2"><li><span class="">bullet2 line 1</span></li></ul>\n<br>\n\ <ul class="list-bullet2"><li><span class="">bullet2 line 1</span></li></ul>\n<br>\n\
<ul class="list-bullet4"><li><span class="b i s u"><b><i><s><u>bullet4 line 2 bisu</u></s></i></b></span></li></ul>\n\ <ul class="list-bullet4"><li><span class="b i s u"><b><i><s><u>bullet4 line 2 bisu</u></s></i></b></span></li></ul>\n\
<ul class="list-bullet4"><li><span class="b s"><b><s>bullet4 line 2 bs</s></b></span></li></ul>\n\ <ul class="list-bullet4"><li><span class="b s"><b><s>bullet4 line 2 bs</s></b></span></li></ul>\n\
<ul class="list-bullet4"><li><span class="u"><u>bullet4 line 2 u</u></span><span class="i s u"><i><s><u>uis</u></s></i></span></li></ul>\n\ <ul class="list-bullet4"><li><span class="u"><u>bullet4 line 2 u</u></span><span class="i s u"><i><s><u>uis</u></s></i></span></li></ul>\n\
<br>\n') <br>\n'));
}) const results = exportfunc(helper.padChrome$.window.location.href);
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><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><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\n');
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\n') done();
done() });
}) xit('import a pad with nested bullets from html', function (done) {
xit("import a pad with nested bullets from html", function(done){ const importurl = `${helper.padChrome$.window.location.href}/import`;
var importurl = helper.padChrome$.window.location.href+'/import' const htmlWithBullets = '<html><body><ul class="list-bullet1"><li>bullet line 1</li></ul><ul class="list-bullet1"><li>bullet line 2</li><ul class="list-bullet2"><li>bullet2 line 1</li></ul></ul><ul class="list-bullet1"><ul class="list-bullet2"><ul class="list-bullet3"><ul class="list-bullet4"><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></body></html>';
var htmlWithBullets = '<html><body><ul class="list-bullet1"><li>bullet line 1</li></ul><ul class="list-bullet1"><li>bullet line 2</li><ul class="list-bullet2"><li>bullet2 line 1</li></ul></ul><ul class="list-bullet1"><ul class="list-bullet2"><ul class="list-bullet3"><ul class="list-bullet4"><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></body></html>' importrequest(htmlWithBullets, importurl, 'html');
importrequest(htmlWithBullets,importurl,"html") const oldtext = getinnertext();
var oldtext=getinnertext() helper.waitFor(() => oldtext != getinnertext(),
helper.waitFor(function(){ // return expect(getinnertext()).to.be('\
return oldtext != getinnertext() // <ul class="list-bullet1"><li><span class="">bullet line 1</span></li></ul>\n\
// return expect(getinnertext()).to.be('\ // <ul class="list-bullet1"><li><span class="">bullet line 2</span></li></ul>\n\
//<ul class="list-bullet1"><li><span class="">bullet line 1</span></li></ul>\n\ // <ul class="list-bullet2"><li><span class="">bullet2 line 1</span></li></ul>\n\
//<ul class="list-bullet1"><li><span class="">bullet line 2</span></li></ul>\n\ // <ul class="list-bullet4"><li><span class="">bullet4 line 2</span></li></ul>\n\
//<ul class="list-bullet2"><li><span class="">bullet2 line 1</span></li></ul>\n\ // <ul class="list-bullet4"><li><span class="">bullet4 line 2</span></li></ul>\n\
//<ul class="list-bullet4"><li><span class="">bullet4 line 2</span></li></ul>\n\ // <ul class="list-bullet4"><li><span class="">bullet4 line 2</span></li></ul>\n\
//<ul class="list-bullet4"><li><span class="">bullet4 line 2</span></li></ul>\n\ // <br>\n')
//<ul class="list-bullet4"><li><span class="">bullet4 line 2</span></li></ul>\n\ );
//<br>\n')
})
var results = exportfunc(helper.padChrome$.window.location.href) const 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[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') 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');
done() done();
}) });
xit("import a pad with 8 levels of bullets and newlines and attributes from html", function(done){ xit('import a pad with 8 levels of bullets and newlines and attributes from html', function (done) {
var importurl = helper.padChrome$.window.location.href+'/import' const importurl = `${helper.padChrome$.window.location.href}/import`;
var htmlWithBullets = '<html><body><ul class="list-bullet1"><li>bullet line 1</li></ul><br/><ul class="list-bullet1"><li>bullet line 2</li><ul class="list-bullet2"><li>bullet2 line 1</li></ul></ul><br/><ul class="list-bullet1"><ul class="list-bullet2"><ul class="list-bullet3"><ul class="list-bullet4"><li><span class="b s i u"><b><i><s><u>bullet4 line 2 bisu</u></s></i></b></span></li><li><span class="b s "><b><s>bullet4 line 2 bs</s></b></span></li><li><span class="u"><u>bullet4 line 2 u</u></span><span class="u i s"><i><s><u>uis</u></s></i></span></li><ul class="list-bullet5"><ul class="list-bullet6"><ul class="list-bullet7"><ul class="list-bullet8"><li><span class="">foo</span></li><li><span class="b s"><b><s>foobar bs</b></s></span></li></ul></ul></ul></ul><ul class="list-bullet5"><li>foobar</li></ul></ul></ul></ul></body></html>' const htmlWithBullets = '<html><body><ul class="list-bullet1"><li>bullet line 1</li></ul><br/><ul class="list-bullet1"><li>bullet line 2</li><ul class="list-bullet2"><li>bullet2 line 1</li></ul></ul><br/><ul class="list-bullet1"><ul class="list-bullet2"><ul class="list-bullet3"><ul class="list-bullet4"><li><span class="b s i u"><b><i><s><u>bullet4 line 2 bisu</u></s></i></b></span></li><li><span class="b s "><b><s>bullet4 line 2 bs</s></b></span></li><li><span class="u"><u>bullet4 line 2 u</u></span><span class="u i s"><i><s><u>uis</u></s></i></span></li><ul class="list-bullet5"><ul class="list-bullet6"><ul class="list-bullet7"><ul class="list-bullet8"><li><span class="">foo</span></li><li><span class="b s"><b><s>foobar bs</b></s></span></li></ul></ul></ul></ul><ul class="list-bullet5"><li>foobar</li></ul></ul></ul></ul></body></html>';
importrequest(htmlWithBullets,importurl,"html") importrequest(htmlWithBullets, importurl, 'html');
helper.waitFor(function(){ helper.waitFor(() => expect(getinnertext()).to.be('\
return expect(getinnertext()).to.be('\
<ul class="list-bullet1"><li><span class="">bullet line 1</span></li></ul>\n\<br>\n\ <ul class="list-bullet1"><li><span class="">bullet line 1</span></li></ul>\n\<br>\n\
<ul class="list-bullet1"><li><span class="">bullet line 2</span></li></ul>\n\ <ul class="list-bullet1"><li><span class="">bullet line 2</span></li></ul>\n\
<ul class="list-bullet2"><li><span class="">bullet2 line 1</span></li></ul>\n<br>\n\ <ul class="list-bullet2"><li><span class="">bullet2 line 1</span></li></ul>\n<br>\n\
@ -180,32 +166,31 @@ describe("import functionality", function(){
<ul class="list-bullet8"><li><span class="">foo</span></li></ul>\n\ <ul class="list-bullet8"><li><span class="">foo</span></li></ul>\n\
<ul class="list-bullet8"><li><span class="b s"><b><s>foobar bs</s></b></span></li></ul>\n\ <ul class="list-bullet8"><li><span class="b s"><b><s>foobar bs</s></b></span></li></ul>\n\
<ul class="list-bullet5"><li><span class="">foobar</span></li></ul>\n\ <ul class="list-bullet5"><li><span class="">foobar</span></li></ul>\n\
<br>\n') <br>\n'));
}) const results = exportfunc(helper.padChrome$.window.location.href);
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');
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();
done() });
})
xit("import a pad with ordered lists from html", function(done){ xit('import a pad with ordered lists from html', function (done) {
var importurl = helper.padChrome$.window.location.href+'/import' const 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>' const 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>';
importrequest(htmlWithBullets,importurl,"html") importrequest(htmlWithBullets, importurl, 'html') -
-console.error(getinnertext()) console.error(getinnertext());
expect(getinnertext()).to.be('\ expect(getinnertext()).to.be('\
<ol class="list-number1" start="1"><li><span class="">number 1 line 1</span></li></ol>\n\ <ol class="list-number1" start="1"><li><span class="">number 1 line 1</span></li></ol>\n\
<ol class="list-number1" start="2"><li><span class="">number 2 line 2</span></li></ol>\n\ <ol class="list-number1" start="2"><li><span class="">number 2 line 2</span></li></ol>\n\
<br>\n') <br>\n');
var results = exportfunc(helper.padChrome$.window.location.href) const results = exportfunc(helper.padChrome$.window.location.href);
expect(results[0][1]).to.be('<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>') expect(results[0][1]).to.be('<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>');
expect(results[1][1]).to.be('') expect(results[1][1]).to.be('');
done() done();
}) });
xit("import a pad with ordered lists and newlines from html", function(done){ xit('import a pad with ordered lists and newlines from html', function (done) {
var importurl = helper.padChrome$.window.location.href+'/import' const importurl = `${helper.padChrome$.window.location.href}/import`;
var htmlWithBullets = '<html><body><ol class="list-number1" start="1"><li>number 9 line 1</li></ol><br/><ol class="list-number1" start="2"><li>number 10 line 2</li><ol class="list-number2"><li>number 2 times line 1</li></ol></ol><br/><ol class="list-bullet1"><ol class="list-number2"><li>number 2 times line 2</li></ol></ol></body></html>' const htmlWithBullets = '<html><body><ol class="list-number1" start="1"><li>number 9 line 1</li></ol><br/><ol class="list-number1" start="2"><li>number 10 line 2</li><ol class="list-number2"><li>number 2 times line 1</li></ol></ol><br/><ol class="list-bullet1"><ol class="list-number2"><li>number 2 times line 2</li></ol></ol></body></html>';
importrequest(htmlWithBullets,importurl,"html") importrequest(htmlWithBullets, importurl, 'html');
expect(getinnertext()).to.be('\ expect(getinnertext()).to.be('\
<ol class="list-number1" start="1"><li><span class="">number 9 line 1</span></li></ol>\n\ <ol class="list-number1" start="1"><li><span class="">number 9 line 1</span></li></ol>\n\
<br>\n\ <br>\n\
@ -213,15 +198,15 @@ describe("import functionality", function(){
<ol class="list-number2"><li><span class="">number 2 times line 1</span></li></ol>\n\ <ol class="list-number2"><li><span class="">number 2 times line 1</span></li></ol>\n\
<br>\n\ <br>\n\
<ol class="list-number2"><li><span class="">number 2 times line 2</span></li></ol>\n\ <ol class="list-number2"><li><span class="">number 2 times line 2</span></li></ol>\n\
<br>\n') <br>\n');
var results = exportfunc(helper.padChrome$.window.location.href) const results = exportfunc(helper.padChrome$.window.location.href);
console.error(results) console.error(results);
done() done();
}) });
xit("import a pad with nested ordered lists and attributes and newlines from html", function(done){ xit('import a pad with nested ordered lists and attributes and newlines from html', function (done) {
var importurl = helper.padChrome$.window.location.href+'/import' const importurl = `${helper.padChrome$.window.location.href}/import`;
var htmlWithBullets = '<html><body><ol class="list-number1" start="1"><li><span class="b s i u"><b><i><s><u>bold strikethrough italics underline</u></s><i/></b></span> line <span class="b"><b>1bold</b></span></li></ol><br/><span class="i"><i><ol class="list-number1" start="2"><li>number 10 line 2</li><ol class="list-number2"><li>number 2 times line 1</li></ol></ol></i></span><br/><ol class="list-bullet1"><ol class="list-number2"><li>number 2 times line 2</li></ol></ol></body></html>' const htmlWithBullets = '<html><body><ol class="list-number1" start="1"><li><span class="b s i u"><b><i><s><u>bold strikethrough italics underline</u></s><i/></b></span> line <span class="b"><b>1bold</b></span></li></ol><br/><span class="i"><i><ol class="list-number1" start="2"><li>number 10 line 2</li><ol class="list-number2"><li>number 2 times line 1</li></ol></ol></i></span><br/><ol class="list-bullet1"><ol class="list-number2"><li>number 2 times line 2</li></ol></ol></body></html>';
importrequest(htmlWithBullets,importurl,"html") importrequest(htmlWithBullets, importurl, 'html');
expect(getinnertext()).to.be('\ expect(getinnertext()).to.be('\
<ol class="list-number1"><li><span class="b i s u"><b><i><s><u>bold strikethrough italics underline</u></s></i></b></span><span class=""> line </span><span class="b"><b>1bold</b></span></li></ol>\n\ <ol class="list-number1"><li><span class="b i s u"><b><i><s><u>bold strikethrough italics underline</u></s></i></b></span><span class=""> line </span><span class="b"><b>1bold</b></span></li></ol>\n\
<br>\n\ <br>\n\
@ -229,9 +214,9 @@ describe("import functionality", function(){
<ol class="list-number2"><li><span class="i"><i>number 2 times line 1</i></span></li></ol>\n\ <ol class="list-number2"><li><span class="i"><i>number 2 times line 1</i></span></li></ol>\n\
<br>\n\ <br>\n\
<ol class="list-number2"><li><span class="">number 2 times line 2</span></li></ol>\n\ <ol class="list-number2"><li><span class="">number 2 times line 2</span></li></ol>\n\
<br>\n') <br>\n');
var results = exportfunc(helper.padChrome$.window.location.href) const results = exportfunc(helper.padChrome$.window.location.href);
console.error(results) console.error(results);
done() done();
}) });
}) });

View file

@ -1,97 +1,92 @@
describe("import indents functionality", function(){ describe('import indents functionality', function () {
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); // creates a new pad helper.newPad(cb); // creates a new pad
this.timeout(60000); this.timeout(60000);
}); });
function getinnertext(){ function getinnertext() {
var inner = helper.padInner$ const inner = helper.padInner$;
var newtext = "" let newtext = '';
inner("div").each(function(line,el){ inner('div').each((line, el) => {
newtext += el.innerHTML+"\n" newtext += `${el.innerHTML}\n`;
}) });
return newtext return newtext;
} }
function importrequest(data,importurl,type){ function importrequest(data, importurl, type) {
var success; let success;
var error; let error;
var result = $.ajax({ const result = $.ajax({
url: importurl, url: importurl,
type: "post", type: 'post',
processData: false, processData: false,
async: false, async: false,
contentType: 'multipart/form-data; boundary=boundary', contentType: 'multipart/form-data; boundary=boundary',
accepts: { accepts: {
text: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" text: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
}, },
data: 'Content-Type: multipart/form-data; boundary=--boundary\r\n\r\n--boundary\r\nContent-Disposition: form-data; name="file"; filename="import.'+type+'"\r\nContent-Type: text/plain\r\n\r\n' + data + '\r\n\r\n--boundary', data: `Content-Type: multipart/form-data; boundary=--boundary\r\n\r\n--boundary\r\nContent-Disposition: form-data; name="file"; filename="import.${type}"\r\nContent-Type: text/plain\r\n\r\n${data}\r\n\r\n--boundary`,
error: function(res){ error(res) {
error = res error = res;
},
});
expect(error).to.be(undefined);
return result;
} }
}) function exportfunc(link) {
expect(error).to.be(undefined) const exportresults = [];
return result
}
function exportfunc(link){
var exportresults = []
$.ajaxSetup({ $.ajaxSetup({
async:false async: false,
}) });
$.get(link+"/export/html",function(data){ $.get(`${link}/export/html`, (data) => {
var start = data.indexOf("<body>") const start = data.indexOf('<body>');
var end = data.indexOf("</body>") const end = data.indexOf('</body>');
var html = data.substr(start+6,end-start-6) const html = data.substr(start + 6, end - start - 6);
exportresults.push(["html",html]) exportresults.push(['html', html]);
}) });
$.get(link+"/export/txt",function(data){ $.get(`${link}/export/txt`, (data) => {
exportresults.push(["txt",data]) exportresults.push(['txt', data]);
}) });
return exportresults return exportresults;
} }
xit("import a pad with indents from html", function(done){ xit('import a pad with indents from html', function (done) {
var importurl = helper.padChrome$.window.location.href+'/import' const importurl = `${helper.padChrome$.window.location.href}/import`;
var htmlWithIndents = '<html><body><ul class="list-indent1"><li>indent line 1</li><li>indent line 2</li><ul class="list-indent2"><li>indent2 line 1</li><li>indent2 line 2</li></ul></ul></body></html>' const htmlWithIndents = '<html><body><ul class="list-indent1"><li>indent line 1</li><li>indent line 2</li><ul class="list-indent2"><li>indent2 line 1</li><li>indent2 line 2</li></ul></ul></body></html>';
importrequest(htmlWithIndents,importurl,"html") importrequest(htmlWithIndents, importurl, 'html');
helper.waitFor(function(){ helper.waitFor(() => expect(getinnertext()).to.be('\
return expect(getinnertext()).to.be('\
<ul class="list-indent1"><li><span class="">indent line 1</span></li></ul>\n\ <ul class="list-indent1"><li><span class="">indent line 1</span></li></ul>\n\
<ul class="list-indent1"><li><span class="">indent line 2</span></li></ul>\n\ <ul class="list-indent1"><li><span class="">indent line 2</span></li></ul>\n\
<ul class="list-indent2"><li><span class="">indent2 line 1</span></li></ul>\n\ <ul class="list-indent2"><li><span class="">indent2 line 1</span></li></ul>\n\
<ul class="list-indent2"><li><span class="">indent2 line 2</span></li></ul>\n\ <ul class="list-indent2"><li><span class="">indent2 line 2</span></li></ul>\n\
<br>\n') <br>\n'));
}) const results = exportfunc(helper.padChrome$.window.location.href);
var results = exportfunc(helper.padChrome$.window.location.href) expect(results[0][1]).to.be('<ul class="indent"><li>indent line 1</li><li>indent line 2</li><ul class="indent"><li>indent2 line 1</li><li>indent2 line 2</li></ul></ul><br>');
expect(results[0][1]).to.be('<ul class="indent"><li>indent line 1</li><li>indent line 2</li><ul class="indent"><li>indent2 line 1</li><li>indent2 line 2</li></ul></ul><br>') 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');
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();
done() });
})
xit("import a pad with indented lists and newlines from html", function(done){ xit('import a pad with indented lists and newlines from html', function (done) {
var importurl = helper.padChrome$.window.location.href+'/import' const 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>' const 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>';
importrequest(htmlWithIndents,importurl,"html") importrequest(htmlWithIndents, importurl, 'html');
helper.waitFor(function(){ helper.waitFor(() => expect(getinnertext()).to.be('\
return expect(getinnertext()).to.be('\
<ul class="list-indent1"><li><span class="">indent line 1</span></li></ul>\n\ <ul class="list-indent1"><li><span class="">indent line 1</span></li></ul>\n\
<br>\n\ <br>\n\
<ul class="list-indent1"><li><span class="">indent 1 line 2</span></li></ul>\n\ <ul class="list-indent1"><li><span class="">indent 1 line 2</span></li></ul>\n\
<ul class="list-indent2"><li><span class="">indent 2 times line 1</span></li></ul>\n\ <ul class="list-indent2"><li><span class="">indent 2 times line 1</span></li></ul>\n\
<br>\n\ <br>\n\
<ul class="list-indent2"><li><span class="">indent 2 times line 2</span></li></ul>\n\ <ul class="list-indent2"><li><span class="">indent 2 times line 2</span></li></ul>\n\
<br>\n') <br>\n'));
}) const results = exportfunc(helper.padChrome$.window.location.href);
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 1 line 2</li><ul class="indent"><li>indent 2 times line 1</li></ul></ul><br><ul><ul class="indent"><li>indent 2 times line 2</li></ul></ul><br>');
expect(results[0][1]).to.be('<ul class="indent"><li>indent line 1</li></ul><br><ul class="indent"><li>indent 1 line 2</li><ul class="indent"><li>indent 2 times line 1</li></ul></ul><br><ul><ul class="indent"><li>indent 2 times line 2</li></ul></ul><br>') expect(results[1][1]).to.be('\tindent line 1\n\n\tindent 1 line 2\n\t\tindent 2 times line 1\n\n\t\tindent 2 times line 2\n\n');
expect(results[1][1]).to.be('\tindent line 1\n\n\tindent 1 line 2\n\t\tindent 2 times line 1\n\n\t\tindent 2 times line 2\n\n') done();
done() });
}) xit('import a pad with 8 levels of indents and newlines and attributes from html', function (done) {
xit("import a pad with 8 levels of indents and newlines and attributes from html", function(done){ const importurl = `${helper.padChrome$.window.location.href}/import`;
var importurl = helper.padChrome$.window.location.href+'/import' const htmlWithIndents = '<html><body><ul class="list-indent1"><li>indent line 1</li></ul><br/><ul class="list-indent1"><li>indent line 2</li><ul class="list-indent2"><li>indent2 line 1</li></ul></ul><br/><ul class="list-indent1"><ul class="list-indent2"><ul class="list-indent3"><ul class="list-indent4"><li><span class="b s i u"><b><i><s><u>indent4 line 2 bisu</u></s></i></b></span></li><li><span class="b s "><b><s>indent4 line 2 bs</s></b></span></li><li><span class="u"><u>indent4 line 2 u</u></span><span class="u i s"><i><s><u>uis</u></s></i></span></li><ul class="list-indent5"><ul class="list-indent6"><ul class="list-indent7"><ul class="list-indent8"><li><span class="">foo</span></li><li><span class="b s"><b><s>foobar bs</b></s></span></li></ul></ul></ul></ul><ul class="list-indent5"><li>foobar</li></ul></ul></ul></ul></body></html>';
var htmlWithIndents = '<html><body><ul class="list-indent1"><li>indent line 1</li></ul><br/><ul class="list-indent1"><li>indent line 2</li><ul class="list-indent2"><li>indent2 line 1</li></ul></ul><br/><ul class="list-indent1"><ul class="list-indent2"><ul class="list-indent3"><ul class="list-indent4"><li><span class="b s i u"><b><i><s><u>indent4 line 2 bisu</u></s></i></b></span></li><li><span class="b s "><b><s>indent4 line 2 bs</s></b></span></li><li><span class="u"><u>indent4 line 2 u</u></span><span class="u i s"><i><s><u>uis</u></s></i></span></li><ul class="list-indent5"><ul class="list-indent6"><ul class="list-indent7"><ul class="list-indent8"><li><span class="">foo</span></li><li><span class="b s"><b><s>foobar bs</b></s></span></li></ul></ul></ul></ul><ul class="list-indent5"><li>foobar</li></ul></ul></ul></ul></body></html>' importrequest(htmlWithIndents, importurl, 'html');
importrequest(htmlWithIndents,importurl,"html") helper.waitFor(() => expect(getinnertext()).to.be('\
helper.waitFor(function(){
return expect(getinnertext()).to.be('\
<ul class="list-indent1"><li><span class="">indent line 1</span></li></ul>\n\<br>\n\ <ul class="list-indent1"><li><span class="">indent line 1</span></li></ul>\n\<br>\n\
<ul class="list-indent1"><li><span class="">indent line 2</span></li></ul>\n\ <ul class="list-indent1"><li><span class="">indent line 2</span></li></ul>\n\
<ul class="list-indent2"><li><span class="">indent2 line 1</span></li></ul>\n<br>\n\ <ul class="list-indent2"><li><span class="">indent2 line 1</span></li></ul>\n<br>\n\
@ -101,11 +96,10 @@ describe("import indents functionality", function(){
<ul class="list-indent8"><li><span class="">foo</span></li></ul>\n\ <ul class="list-indent8"><li><span class="">foo</span></li></ul>\n\
<ul class="list-indent8"><li><span class="b s"><b><s>foobar bs</s></b></span></li></ul>\n\ <ul class="list-indent8"><li><span class="b s"><b><s>foobar bs</s></b></span></li></ul>\n\
<ul class="list-indent5"><li><span class="">foobar</span></li></ul>\n\ <ul class="list-indent5"><li><span class="">foobar</span></li></ul>\n\
<br>\n') <br>\n'));
}) const results = exportfunc(helper.padChrome$.window.location.href);
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');
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();
done() });
}) });
})

View file

@ -1,178 +1,164 @@
describe("indentation button", function(){ describe('indentation button', function () {
//create a new pad before each test run // create a new pad before each test run
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); helper.newPad(cb);
this.timeout(60000); this.timeout(60000);
}); });
it("indent text with keypress", function(done){ it('indent text with keypress', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe // get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first(); const $firstTextElement = inner$('div').first();
//select this text element // select this text element
$firstTextElement.sendkeys('{selectall}'); $firstTextElement.sendkeys('{selectall}');
var e = inner$.Event(helper.evtType); const e = inner$.Event(helper.evtType);
e.keyCode = 9; // tab :| e.keyCode = 9; // tab :|
inner$("#innerdocbody").trigger(e); inner$('#innerdocbody').trigger(e);
helper.waitFor(function(){ helper.waitFor(() => inner$('div').first().find('ul li').length === 1).done(done);
return inner$("div").first().find("ul li").length === 1;
}).done(done);
}); });
it("indent text with button", function(done){ it('indent text with button', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
var $indentButton = chrome$(".buttonicon-indent"); const $indentButton = chrome$('.buttonicon-indent');
$indentButton.click(); $indentButton.click();
helper.waitFor(function(){ helper.waitFor(() => inner$('div').first().find('ul li').length === 1).done(done);
return inner$("div").first().find("ul li").length === 1;
}).done(done);
}); });
it("keeps the indent on enter for the new line", function(done){ it('keeps the indent on enter for the new line', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
var $indentButton = chrome$(".buttonicon-indent"); const $indentButton = chrome$('.buttonicon-indent');
$indentButton.click(); $indentButton.click();
//type a bit, make a line break and type again // type a bit, make a line break and type again
var $firstTextElement = inner$("div span").first(); const $firstTextElement = inner$('div span').first();
$firstTextElement.sendkeys('line 1'); $firstTextElement.sendkeys('line 1');
$firstTextElement.sendkeys('{enter}'); $firstTextElement.sendkeys('{enter}');
$firstTextElement.sendkeys('line 2'); $firstTextElement.sendkeys('line 2');
$firstTextElement.sendkeys('{enter}'); $firstTextElement.sendkeys('{enter}');
helper.waitFor(function(){ helper.waitFor(() => inner$('div span').first().text().indexOf('line 2') === -1).done(() => {
return inner$("div span").first().text().indexOf("line 2") === -1; const $newSecondLine = inner$('div').first().next();
}).done(function(){ const hasULElement = $newSecondLine.find('ul li').length === 1;
var $newSecondLine = inner$("div").first().next();
var hasULElement = $newSecondLine.find("ul li").length === 1;
expect(hasULElement).to.be(true); expect(hasULElement).to.be(true);
expect($newSecondLine.text()).to.be("line 2"); expect($newSecondLine.text()).to.be('line 2');
done(); done();
}); });
}); });
it("indents text with spaces on enter if previous line ends with ':', '[', '(', or '{'", function(done){ it("indents text with spaces on enter if previous line ends with ':', '[', '(', or '{'", function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//type a bit, make a line break and type again // type a bit, make a line break and type again
var $firstTextElement = inner$("div").first(); const $firstTextElement = inner$('div').first();
$firstTextElement.sendkeys("line with ':'{enter}"); $firstTextElement.sendkeys("line with ':'{enter}");
$firstTextElement.sendkeys("line with '['{enter}"); $firstTextElement.sendkeys("line with '['{enter}");
$firstTextElement.sendkeys("line with '('{enter}"); $firstTextElement.sendkeys("line with '('{enter}");
$firstTextElement.sendkeys("line with '{{}'{enter}"); $firstTextElement.sendkeys("line with '{{}'{enter}");
helper.waitFor(function(){ helper.waitFor(() => {
// wait for Etherpad to split four lines into separated divs // wait for Etherpad to split four lines into separated divs
var $fourthLine = inner$("div").first().next().next().next(); const $fourthLine = inner$('div').first().next().next().next();
return $fourthLine.text().indexOf("line with '{'") === 0; return $fourthLine.text().indexOf("line with '{'") === 0;
}).done(function(){ }).done(() => {
// we validate bottom to top for easier implementation // we validate bottom to top for easier implementation
// curly braces // curly braces
var $lineWithCurlyBraces = inner$("div").first().next().next().next(); const $lineWithCurlyBraces = inner$('div').first().next().next().next();
$lineWithCurlyBraces.sendkeys('{{}'); $lineWithCurlyBraces.sendkeys('{{}');
pressEnter(); // cannot use sendkeys('{enter}') here, browser does not read the command properly pressEnter(); // cannot use sendkeys('{enter}') here, browser does not read the command properly
var $lineAfterCurlyBraces = inner$("div").first().next().next().next().next(); const $lineAfterCurlyBraces = inner$('div').first().next().next().next().next();
expect($lineAfterCurlyBraces.text()).to.match(/\s{4}/); // tab === 4 spaces expect($lineAfterCurlyBraces.text()).to.match(/\s{4}/); // tab === 4 spaces
// parenthesis // parenthesis
var $lineWithParenthesis = inner$("div").first().next().next(); const $lineWithParenthesis = inner$('div').first().next().next();
$lineWithParenthesis.sendkeys('('); $lineWithParenthesis.sendkeys('(');
pressEnter(); pressEnter();
var $lineAfterParenthesis = inner$("div").first().next().next().next(); const $lineAfterParenthesis = inner$('div').first().next().next().next();
expect($lineAfterParenthesis.text()).to.match(/\s{4}/); expect($lineAfterParenthesis.text()).to.match(/\s{4}/);
// bracket // bracket
var $lineWithBracket = inner$("div").first().next(); const $lineWithBracket = inner$('div').first().next();
$lineWithBracket.sendkeys('['); $lineWithBracket.sendkeys('[');
pressEnter(); pressEnter();
var $lineAfterBracket = inner$("div").first().next().next(); const $lineAfterBracket = inner$('div').first().next().next();
expect($lineAfterBracket.text()).to.match(/\s{4}/); expect($lineAfterBracket.text()).to.match(/\s{4}/);
// colon // colon
var $lineWithColon = inner$("div").first(); const $lineWithColon = inner$('div').first();
$lineWithColon.sendkeys(':'); $lineWithColon.sendkeys(':');
pressEnter(); pressEnter();
var $lineAfterColon = inner$("div").first().next(); const $lineAfterColon = inner$('div').first().next();
expect($lineAfterColon.text()).to.match(/\s{4}/); expect($lineAfterColon.text()).to.match(/\s{4}/);
done(); done();
}); });
}); });
it("appends indentation to the indent of previous line if previous line ends with ':', '[', '(', or '{'", function(done){ it("appends indentation to the indent of previous line if previous line ends with ':', '[', '(', or '{'", function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//type a bit, make a line break and type again // type a bit, make a line break and type again
var $firstTextElement = inner$("div").first(); const $firstTextElement = inner$('div').first();
$firstTextElement.sendkeys(" line with some indentation and ':'{enter}"); $firstTextElement.sendkeys(" line with some indentation and ':'{enter}");
$firstTextElement.sendkeys("line 2{enter}"); $firstTextElement.sendkeys('line 2{enter}');
helper.waitFor(function(){ helper.waitFor(() => {
// wait for Etherpad to split two lines into separated divs // wait for Etherpad to split two lines into separated divs
var $secondLine = inner$("div").first().next(); const $secondLine = inner$('div').first().next();
return $secondLine.text().indexOf("line 2") === 0; return $secondLine.text().indexOf('line 2') === 0;
}).done(function(){ }).done(() => {
var $lineWithColon = inner$("div").first(); const $lineWithColon = inner$('div').first();
$lineWithColon.sendkeys(':'); $lineWithColon.sendkeys(':');
pressEnter(); pressEnter();
var $lineAfterColon = inner$("div").first().next(); const $lineAfterColon = inner$('div').first().next();
expect($lineAfterColon.text()).to.match(/\s{6}/); // previous line indentation + regular tab (4 spaces) expect($lineAfterColon.text()).to.match(/\s{6}/); // previous line indentation + regular tab (4 spaces)
done(); done();
}); });
}); });
it("issue #2772 shows '*' when multiple indented lines receive a style and are outdented", function(done){ it("issue #2772 shows '*' when multiple indented lines receive a style and are outdented", function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
// make sure pad has more than one line // make sure pad has more than one line
inner$("div").first().sendkeys("First{enter}Second{enter}"); inner$('div').first().sendkeys('First{enter}Second{enter}');
helper.waitFor(function(){ helper.waitFor(() => inner$('div').first().text().trim() === 'First').done(() => {
return inner$("div").first().text().trim() === "First";
}).done(function(){
// indent first 2 lines // indent first 2 lines
var $lines = inner$("div"); const $lines = inner$('div');
var $firstLine = $lines.first(); const $firstLine = $lines.first();
var $secondLine = $lines.slice(1,2); const $secondLine = $lines.slice(1, 2);
helper.selectLines($firstLine, $secondLine); helper.selectLines($firstLine, $secondLine);
var $indentButton = chrome$(".buttonicon-indent"); const $indentButton = chrome$('.buttonicon-indent');
$indentButton.click(); $indentButton.click();
helper.waitFor(function(){ helper.waitFor(() => inner$('div').first().find('ul li').length === 1).done(() => {
return inner$("div").first().find("ul li").length === 1;
}).done(function(){
// apply bold // apply bold
var $boldButton = chrome$(".buttonicon-bold"); const $boldButton = chrome$('.buttonicon-bold');
$boldButton.click(); $boldButton.click();
helper.waitFor(function(){ helper.waitFor(() => inner$('div').first().find('b').length === 1).done(() => {
return inner$("div").first().find("b").length === 1;
}).done(function(){
// outdent first 2 lines // outdent first 2 lines
var $outdentButton = chrome$(".buttonicon-outdent"); const $outdentButton = chrome$('.buttonicon-outdent');
$outdentButton.click(); $outdentButton.click();
helper.waitFor(function(){ helper.waitFor(() => inner$('div').first().find('ul li').length === 0).done(() => {
return inner$("div").first().find("ul li").length === 0;
}).done(function(){
// check if '*' is displayed // check if '*' is displayed
var $secondLine = inner$("div").slice(1,2); const $secondLine = inner$('div').slice(1, 2);
expect($secondLine.text().trim()).to.be("Second"); expect($secondLine.text().trim()).to.be('Second');
done(); done();
}); });
@ -314,12 +300,11 @@ describe("indentation button", function(){
expect(isLI).to.be(true); expect(isLI).to.be(true);
},1000); },1000);
});*/ });*/
}); });
function pressEnter(){ function pressEnter() {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var e = inner$.Event(helper.evtType); const e = inner$.Event(helper.evtType);
e.keyCode = 13; // enter :| e.keyCode = 13; // enter :|
inner$("#innerdocbody").trigger(e); inner$('#innerdocbody').trigger(e);
} }

View file

@ -1,67 +1,66 @@
describe("italic some text", function(){ describe('italic some text', function () {
//create a new pad before each test run // create a new pad before each test run
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); helper.newPad(cb);
this.timeout(60000); this.timeout(60000);
}); });
it("makes text italic using button", function(done) { it('makes text italic using button', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe // get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first(); const $firstTextElement = inner$('div').first();
//select this text element // select this text element
$firstTextElement.sendkeys('{selectall}'); $firstTextElement.sendkeys('{selectall}');
//get the bold button and click it // get the bold button and click it
var $boldButton = chrome$(".buttonicon-italic"); const $boldButton = chrome$('.buttonicon-italic');
$boldButton.click(); $boldButton.click();
//ace creates a new dom element when you press a button, so just get the first text element again // ace creates a new dom element when you press a button, so just get the first text element again
var $newFirstTextElement = inner$("div").first(); const $newFirstTextElement = inner$('div').first();
// is there a <i> element now? // is there a <i> element now?
var isItalic = $newFirstTextElement.find("i").length === 1; const isItalic = $newFirstTextElement.find('i').length === 1;
//expect it to be bold // expect it to be bold
expect(isItalic).to.be(true); expect(isItalic).to.be(true);
//make sure the text hasn't changed // make sure the text hasn't changed
expect($newFirstTextElement.text()).to.eql($firstTextElement.text()); expect($newFirstTextElement.text()).to.eql($firstTextElement.text());
done(); done();
}); });
it("makes text italic using keypress", function(done) { it('makes text italic using keypress', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe // get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first(); const $firstTextElement = inner$('div').first();
//select this text element // select this text element
$firstTextElement.sendkeys('{selectall}'); $firstTextElement.sendkeys('{selectall}');
var e = inner$.Event(helper.evtType); const e = inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key e.ctrlKey = true; // Control key
e.which = 105; // i e.which = 105; // i
inner$("#innerdocbody").trigger(e); inner$('#innerdocbody').trigger(e);
//ace creates a new dom element when you press a button, so just get the first text element again // ace creates a new dom element when you press a button, so just get the first text element again
var $newFirstTextElement = inner$("div").first(); const $newFirstTextElement = inner$('div').first();
// is there a <i> element now? // is there a <i> element now?
var isItalic = $newFirstTextElement.find("i").length === 1; const isItalic = $newFirstTextElement.find('i').length === 1;
//expect it to be bold // expect it to be bold
expect(isItalic).to.be(true); expect(isItalic).to.be(true);
//make sure the text hasn't changed // make sure the text hasn't changed
expect($newFirstTextElement.text()).to.eql($firstTextElement.text()); expect($newFirstTextElement.text()).to.eql($firstTextElement.text());
done(); done();
}); });
}); });

View file

@ -1,134 +1,126 @@
function deletecookie(name) { function deletecookie(name) {
document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;'; document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
} }
describe("Language select and change", function(){ describe('Language select and change', function () {
// Destroy language cookies // Destroy language cookies
deletecookie("language", null); deletecookie('language', null);
//create a new pad before each test run // create a new pad before each test run
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); helper.newPad(cb);
this.timeout(60000); this.timeout(60000);
}); });
// Destroy language cookies // Destroy language cookies
it("makes text german", function(done) { it('makes text german', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//click on the settings button to make settings visible // click on the settings button to make settings visible
var $settingsButton = chrome$(".buttonicon-settings"); const $settingsButton = chrome$('.buttonicon-settings');
$settingsButton.click(); $settingsButton.click();
//click the language button // click the language button
var $language = chrome$("#languagemenu"); const $language = chrome$('#languagemenu');
var $languageoption = $language.find("[value=de]"); const $languageoption = $language.find('[value=de]');
//select german // select german
$languageoption.attr('selected','selected'); $languageoption.attr('selected', 'selected');
$language.change(); $language.change();
helper.waitFor(function() { helper.waitFor(() => chrome$('.buttonicon-bold').parent()[0].title == 'Fett (Strg-B)')
return chrome$(".buttonicon-bold").parent()[0]["title"] == "Fett (Strg-B)"; .done(() => {
}) // get the value of the bold button
.done(function(){ const $boldButton = chrome$('.buttonicon-bold').parent();
//get the value of the bold button
var $boldButton = chrome$(".buttonicon-bold").parent();
//get the title of the bold button // get the title of the bold button
var boldButtonTitle = $boldButton[0]["title"]; const boldButtonTitle = $boldButton[0].title;
//check if the language is now german // check if the language is now german
expect(boldButtonTitle).to.be("Fett (Strg-B)"); expect(boldButtonTitle).to.be('Fett (Strg-B)');
done(); done();
}); });
}); });
it("makes text English", function(done) { it('makes text English', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//click on the settings button to make settings visible // click on the settings button to make settings visible
var $settingsButton = chrome$(".buttonicon-settings"); const $settingsButton = chrome$('.buttonicon-settings');
$settingsButton.click(); $settingsButton.click();
//click the language button // click the language button
var $language = chrome$("#languagemenu"); const $language = chrome$('#languagemenu');
//select english // select english
$language.val("en"); $language.val('en');
$language.change(); $language.change();
//get the value of the bold button // get the value of the bold button
var $boldButton = chrome$(".buttonicon-bold").parent(); const $boldButton = chrome$('.buttonicon-bold').parent();
helper.waitFor(function() { return $boldButton[0]["title"] != "Fett (Strg+B)";}) helper.waitFor(() => $boldButton[0].title != 'Fett (Strg+B)')
.done(function(){ .done(() => {
// get the value of the bold button
const $boldButton = chrome$('.buttonicon-bold').parent();
//get the value of the bold button // get the title of the bold button
var $boldButton = chrome$(".buttonicon-bold").parent(); const boldButtonTitle = $boldButton[0].title;
//get the title of the bold button // check if the language is now English
var boldButtonTitle = $boldButton[0]["title"]; expect(boldButtonTitle).to.be('Bold (Ctrl+B)');
//check if the language is now English
expect(boldButtonTitle).to.be("Bold (Ctrl+B)");
done(); done();
}); });
}); });
it("changes direction when picking an rtl lang", function(done) { it('changes direction when picking an rtl lang', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//click on the settings button to make settings visible // click on the settings button to make settings visible
var $settingsButton = chrome$(".buttonicon-settings"); const $settingsButton = chrome$('.buttonicon-settings');
$settingsButton.click(); $settingsButton.click();
//click the language button // click the language button
var $language = chrome$("#languagemenu"); const $language = chrome$('#languagemenu');
var $languageoption = $language.find("[value=ar]"); const $languageoption = $language.find('[value=ar]');
//select arabic // select arabic
// $languageoption.attr('selected','selected'); // Breaks the test.. // $languageoption.attr('selected','selected'); // Breaks the test..
$language.val("ar"); $language.val('ar');
$languageoption.change(); $languageoption.change();
helper.waitFor(function() { helper.waitFor(() => chrome$('html')[0].dir != 'ltr')
return chrome$("html")[0]["dir"] != 'ltr'; .done(() => {
})
.done(function(){
// check if the document's direction was changed // check if the document's direction was changed
expect(chrome$("html")[0]["dir"]).to.be("rtl"); expect(chrome$('html')[0].dir).to.be('rtl');
done(); done();
}); });
}); });
it("changes direction when picking an ltr lang", function(done) { it('changes direction when picking an ltr lang', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//click on the settings button to make settings visible // click on the settings button to make settings visible
var $settingsButton = chrome$(".buttonicon-settings"); const $settingsButton = chrome$('.buttonicon-settings');
$settingsButton.click(); $settingsButton.click();
//click the language button // click the language button
var $language = chrome$("#languagemenu"); const $language = chrome$('#languagemenu');
var $languageoption = $language.find("[value=en]"); const $languageoption = $language.find('[value=en]');
//select english // select english
//select arabic // select arabic
$languageoption.attr('selected','selected'); $languageoption.attr('selected', 'selected');
$language.val("en"); $language.val('en');
$languageoption.change(); $languageoption.change();
helper.waitFor(function() { helper.waitFor(() => chrome$('html')[0].dir != 'rtl')
return chrome$("html")[0]["dir"] != 'rtl'; .done(() => {
})
.done(function(){
// check if the document's direction was changed // check if the document's direction was changed
expect(chrome$("html")[0]["dir"]).to.be("ltr"); expect(chrome$('html')[0].dir).to.be('ltr');
done(); done();
}); });
}); });

View file

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

View file

@ -1,204 +1,180 @@
describe("assign ordered list", function(){ describe('assign ordered list', function () {
//create a new pad before each test run // create a new pad before each test run
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); helper.newPad(cb);
this.timeout(60000); this.timeout(60000);
}); });
it("inserts ordered list text", function(done){ it('inserts ordered list text', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
var $insertorderedlistButton = chrome$(".buttonicon-insertorderedlist"); const $insertorderedlistButton = chrome$('.buttonicon-insertorderedlist');
$insertorderedlistButton.click(); $insertorderedlistButton.click();
helper.waitFor(function(){ helper.waitFor(() => inner$('div').first().find('ol li').length === 1).done(done);
return inner$("div").first().find("ol li").length === 1;
}).done(done);
}); });
context('when user presses Ctrl+Shift+N', function() { context('when user presses Ctrl+Shift+N', function () {
context('and pad shortcut is enabled', function() { context('and pad shortcut is enabled', function () {
beforeEach(function() { beforeEach(function () {
makeSureShortcutIsEnabled('cmdShiftN'); makeSureShortcutIsEnabled('cmdShiftN');
triggerCtrlShiftShortcut('N'); triggerCtrlShiftShortcut('N');
}); });
it('inserts unordered list', function(done) { it('inserts unordered list', function (done) {
helper.waitFor(function() { helper.waitFor(() => helper.padInner$('div').first().find('ol li').length === 1).done(done);
return helper.padInner$('div').first().find('ol li').length === 1;
}).done(done);
}); });
}); });
context('and pad shortcut is disabled', function() { context('and pad shortcut is disabled', function () {
beforeEach(function() { beforeEach(function () {
makeSureShortcutIsDisabled('cmdShiftN'); makeSureShortcutIsDisabled('cmdShiftN');
triggerCtrlShiftShortcut('N'); triggerCtrlShiftShortcut('N');
}); });
it('does not insert unordered list', function(done) { it('does not insert unordered list', function (done) {
helper.waitFor(function() { helper.waitFor(() => helper.padInner$('div').first().find('ol li').length === 1).done(() => {
return helper.padInner$('div').first().find('ol li').length === 1; expect().fail(() => 'Unordered list inserted, should ignore shortcut');
}).done(function() { }).fail(() => {
expect().fail(function() { return 'Unordered list inserted, should ignore shortcut' });
}).fail(function() {
done(); done();
}); });
}); });
}); });
}); });
context('when user presses Ctrl+Shift+1', function() { context('when user presses Ctrl+Shift+1', function () {
context('and pad shortcut is enabled', function() { context('and pad shortcut is enabled', function () {
beforeEach(function() { beforeEach(function () {
makeSureShortcutIsEnabled('cmdShift1'); makeSureShortcutIsEnabled('cmdShift1');
triggerCtrlShiftShortcut('1'); triggerCtrlShiftShortcut('1');
}); });
it('inserts unordered list', function(done) { it('inserts unordered list', function (done) {
helper.waitFor(function() { helper.waitFor(() => helper.padInner$('div').first().find('ol li').length === 1).done(done);
return helper.padInner$('div').first().find('ol li').length === 1;
}).done(done);
}); });
}); });
context('and pad shortcut is disabled', function() { context('and pad shortcut is disabled', function () {
beforeEach(function() { beforeEach(function () {
makeSureShortcutIsDisabled('cmdShift1'); makeSureShortcutIsDisabled('cmdShift1');
triggerCtrlShiftShortcut('1'); triggerCtrlShiftShortcut('1');
}); });
it('does not insert unordered list', function(done) { it('does not insert unordered list', function (done) {
helper.waitFor(function() { helper.waitFor(() => helper.padInner$('div').first().find('ol li').length === 1).done(() => {
return helper.padInner$('div').first().find('ol li').length === 1; expect().fail(() => 'Unordered list inserted, should ignore shortcut');
}).done(function() { }).fail(() => {
expect().fail(function() { return 'Unordered list inserted, should ignore shortcut' });
}).fail(function() {
done(); done();
}); });
}); });
}); });
}); });
xit("issue #1125 keeps the numbered list on enter for the new line - EMULATES PASTING INTO A PAD", function(done){ xit('issue #1125 keeps the numbered list on enter for the new line - EMULATES PASTING INTO A PAD', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
var $insertorderedlistButton = chrome$(".buttonicon-insertorderedlist"); const $insertorderedlistButton = chrome$('.buttonicon-insertorderedlist');
$insertorderedlistButton.click(); $insertorderedlistButton.click();
//type a bit, make a line break and type again // type a bit, make a line break and type again
var $firstTextElement = inner$("div span").first(); const $firstTextElement = inner$('div span').first();
$firstTextElement.sendkeys('line 1'); $firstTextElement.sendkeys('line 1');
$firstTextElement.sendkeys('{enter}'); $firstTextElement.sendkeys('{enter}');
$firstTextElement.sendkeys('line 2'); $firstTextElement.sendkeys('line 2');
$firstTextElement.sendkeys('{enter}'); $firstTextElement.sendkeys('{enter}');
helper.waitFor(function(){ helper.waitFor(() => inner$('div span').first().text().indexOf('line 2') === -1).done(() => {
return inner$("div span").first().text().indexOf("line 2") === -1; const $newSecondLine = inner$('div').first().next();
}).done(function(){ const hasOLElement = $newSecondLine.find('ol li').length === 1;
var $newSecondLine = inner$("div").first().next();
var hasOLElement = $newSecondLine.find("ol li").length === 1;
expect(hasOLElement).to.be(true); expect(hasOLElement).to.be(true);
expect($newSecondLine.text()).to.be("line 2"); expect($newSecondLine.text()).to.be('line 2');
var hasLineNumber = $newSecondLine.find("ol").attr("start") === 2; const hasLineNumber = $newSecondLine.find('ol').attr('start') === 2;
expect(hasLineNumber).to.be(true); // This doesn't work because pasting in content doesn't work expect(hasLineNumber).to.be(true); // This doesn't work because pasting in content doesn't work
done(); done();
}); });
}); });
var triggerCtrlShiftShortcut = function(shortcutChar) { var triggerCtrlShiftShortcut = function (shortcutChar) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var e = inner$.Event(helper.evtType); const e = inner$.Event(helper.evtType);
e.ctrlKey = true; e.ctrlKey = true;
e.shiftKey = true; e.shiftKey = true;
e.which = shortcutChar.toString().charCodeAt(0); e.which = shortcutChar.toString().charCodeAt(0);
inner$("#innerdocbody").trigger(e); inner$('#innerdocbody').trigger(e);
} };
var makeSureShortcutIsDisabled = function(shortcut) { var makeSureShortcutIsDisabled = function (shortcut) {
helper.padChrome$.window.clientVars.padShortcutEnabled[shortcut] = false; helper.padChrome$.window.clientVars.padShortcutEnabled[shortcut] = false;
} };
var makeSureShortcutIsEnabled = function(shortcut) { var makeSureShortcutIsEnabled = function (shortcut) {
helper.padChrome$.window.clientVars.padShortcutEnabled[shortcut] = true; helper.padChrome$.window.clientVars.padShortcutEnabled[shortcut] = true;
} };
}); });
describe("Pressing Tab in an OL increases and decreases indentation", function(){ describe('Pressing Tab in an OL increases and decreases indentation', function () {
//create a new pad before each test run // create a new pad before each test run
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); helper.newPad(cb);
this.timeout(60000); this.timeout(60000);
}); });
it("indent and de-indent list item with keypress", function(done){ it('indent and de-indent list item with keypress', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe // get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first(); const $firstTextElement = inner$('div').first();
//select this text element // select this text element
$firstTextElement.sendkeys('{selectall}'); $firstTextElement.sendkeys('{selectall}');
var $insertorderedlistButton = chrome$(".buttonicon-insertorderedlist"); const $insertorderedlistButton = chrome$('.buttonicon-insertorderedlist');
$insertorderedlistButton.click(); $insertorderedlistButton.click();
var e = inner$.Event(helper.evtType); const e = inner$.Event(helper.evtType);
e.keyCode = 9; // tab e.keyCode = 9; // tab
inner$("#innerdocbody").trigger(e); inner$('#innerdocbody').trigger(e);
expect(inner$("div").first().find(".list-number2").length === 1).to.be(true); expect(inner$('div').first().find('.list-number2').length === 1).to.be(true);
e.shiftKey = true; // shift e.shiftKey = true; // shift
e.keyCode = 9; // tab e.keyCode = 9; // tab
inner$("#innerdocbody").trigger(e); inner$('#innerdocbody').trigger(e);
helper.waitFor(function(){
return inner$("div").first().find(".list-number1").length === 1;
}).done(done);
helper.waitFor(() => 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(){ 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 // create a new pad before each test run
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); helper.newPad(cb);
this.timeout(60000); this.timeout(60000);
}); });
it("indent and de-indent list item with indent button", function(done){ it('indent and de-indent list item with indent button', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe // get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first(); const $firstTextElement = inner$('div').first();
//select this text element // select this text element
$firstTextElement.sendkeys('{selectall}'); $firstTextElement.sendkeys('{selectall}');
var $insertorderedlistButton = chrome$(".buttonicon-insertorderedlist"); const $insertorderedlistButton = chrome$('.buttonicon-insertorderedlist');
$insertorderedlistButton.click(); $insertorderedlistButton.click();
var $indentButton = chrome$(".buttonicon-indent"); const $indentButton = chrome$('.buttonicon-indent');
$indentButton.click(); // make it indented twice $indentButton.click(); // make it indented twice
expect(inner$("div").first().find(".list-number2").length === 1).to.be(true); expect(inner$('div').first().find('.list-number2').length === 1).to.be(true);
var $outdentButton = chrome$(".buttonicon-outdent"); const $outdentButton = chrome$('.buttonicon-outdent');
$outdentButton.click(); // make it deindented to 1 $outdentButton.click(); // make it deindented to 1
helper.waitFor(function(){ helper.waitFor(() => inner$('div').first().find('.list-number1').length === 1).done(done);
return inner$("div").first().find(".list-number1").length === 1;
}).done(done);
}); });
}); });

View file

@ -1,36 +1,34 @@
describe('Pad modal', function() { describe('Pad modal', function () {
context('when modal is a "force reconnect" message', function() { context('when modal is a "force reconnect" message', function () {
var MODAL_SELECTOR = '#connectivity'; const MODAL_SELECTOR = '#connectivity';
beforeEach(function(done) { beforeEach(function (done) {
helper.newPad(function() { helper.newPad(() => {
// force a "slowcommit" error // force a "slowcommit" error
helper.padChrome$.window.pad.handleChannelStateChange('DISCONNECTED', 'slowcommit'); helper.padChrome$.window.pad.handleChannelStateChange('DISCONNECTED', 'slowcommit');
// wait for modal to be displayed // wait for modal to be displayed
var $modal = helper.padChrome$(MODAL_SELECTOR); const $modal = helper.padChrome$(MODAL_SELECTOR);
helper.waitFor(function() { helper.waitFor(() => $modal.hasClass('popup-show'), 50000).done(done);
return $modal.hasClass('popup-show');
}, 50000).done(done);
}); });
this.timeout(60000); this.timeout(60000);
}); });
it('disables editor', function(done) { it('disables editor', function (done) {
expect(isEditorDisabled()).to.be(true); expect(isEditorDisabled()).to.be(true);
done(); done();
}); });
context('and user clicks on editor', function() { context('and user clicks on editor', function () {
beforeEach(function() { beforeEach(function () {
clickOnPadInner(); clickOnPadInner();
}); });
it('does not close the modal', function(done) { it('does not close the modal', function (done) {
var $modal = helper.padChrome$(MODAL_SELECTOR); const $modal = helper.padChrome$(MODAL_SELECTOR);
var modalIsVisible = $modal.hasClass('popup-show'); const modalIsVisible = $modal.hasClass('popup-show');
expect(modalIsVisible).to.be(true); expect(modalIsVisible).to.be(true);
@ -38,14 +36,14 @@ describe('Pad modal', function() {
}); });
}); });
context('and user clicks on pad outer', function() { context('and user clicks on pad outer', function () {
beforeEach(function() { beforeEach(function () {
clickOnPadOuter(); clickOnPadOuter();
}); });
it('does not close the modal', function(done) { it('does not close the modal', function (done) {
var $modal = helper.padChrome$(MODAL_SELECTOR); const $modal = helper.padChrome$(MODAL_SELECTOR);
var modalIsVisible = $modal.hasClass('popup-show'); const modalIsVisible = $modal.hasClass('popup-show');
expect(modalIsVisible).to.be(true); expect(modalIsVisible).to.be(true);
@ -55,79 +53,77 @@ describe('Pad modal', function() {
}); });
// we use "settings" here, but other modals have the same behaviour // we use "settings" here, but other modals have the same behaviour
context('when modal is not an error message', function() { context('when modal is not an error message', function () {
var MODAL_SELECTOR = '#settings'; const MODAL_SELECTOR = '#settings';
beforeEach(function(done) { beforeEach(function (done) {
helper.newPad(function() { helper.newPad(() => {
openSettingsAndWaitForModalToBeVisible(done); openSettingsAndWaitForModalToBeVisible(done);
}); });
this.timeout(60000); this.timeout(60000);
}); });
// This test breaks safari testing // This test breaks safari testing
/* /*
it('does not disable editor', function(done) { it('does not disable editor', function(done) {
expect(isEditorDisabled()).to.be(false); expect(isEditorDisabled()).to.be(false);
done(); done();
}); });
*/ */
context('and user clicks on editor', function() { context('and user clicks on editor', function () {
beforeEach(function() { beforeEach(function () {
clickOnPadInner(); clickOnPadInner();
}); });
it('closes the modal', function(done) { it('closes the modal', function (done) {
expect(isModalOpened(MODAL_SELECTOR)).to.be(false); expect(isModalOpened(MODAL_SELECTOR)).to.be(false);
done(); done();
}); });
}); });
context('and user clicks on pad outer', function() { context('and user clicks on pad outer', function () {
beforeEach(function() { beforeEach(function () {
clickOnPadOuter(); clickOnPadOuter();
}); });
it('closes the modal', function(done) { it('closes the modal', function (done) {
expect(isModalOpened(MODAL_SELECTOR)).to.be(false); expect(isModalOpened(MODAL_SELECTOR)).to.be(false);
done(); done();
}); });
}); });
}); });
var clickOnPadInner = function() { var clickOnPadInner = function () {
var $editor = helper.padInner$('#innerdocbody'); const $editor = helper.padInner$('#innerdocbody');
$editor.click(); $editor.click();
} };
var clickOnPadOuter = function() { var clickOnPadOuter = function () {
var $lineNumbersColumn = helper.padOuter$('#sidedivinner'); const $lineNumbersColumn = helper.padOuter$('#sidedivinner');
$lineNumbersColumn.click(); $lineNumbersColumn.click();
} };
var openSettingsAndWaitForModalToBeVisible = function(done) { var openSettingsAndWaitForModalToBeVisible = function (done) {
helper.padChrome$('.buttonicon-settings').click(); helper.padChrome$('.buttonicon-settings').click();
// wait for modal to be displayed // wait for modal to be displayed
var modalSelector = '#settings'; const modalSelector = '#settings';
helper.waitFor(function() { helper.waitFor(() => isModalOpened(modalSelector), 10000).done(done);
return isModalOpened(modalSelector); };
}, 10000).done(done);
}
var isEditorDisabled = function() { var isEditorDisabled = function () {
var editorDocument = helper.padOuter$("iframe[name='ace_inner']").get(0).contentDocument; const editorDocument = helper.padOuter$("iframe[name='ace_inner']").get(0).contentDocument;
var editorBody = editorDocument.getElementById('innerdocbody'); const editorBody = editorDocument.getElementById('innerdocbody');
var editorIsDisabled = editorBody.contentEditable === 'false' // IE/Safari const editorIsDisabled = editorBody.contentEditable === 'false' || // IE/Safari
|| editorDocument.designMode === 'off'; // other browsers editorDocument.designMode === 'off'; // other browsers
return editorIsDisabled; return editorIsDisabled;
} };
var isModalOpened = function(modalSelector) { var isModalOpened = function (modalSelector) {
var $modal = helper.padChrome$(modalSelector); const $modal = helper.padChrome$(modalSelector);
return $modal.hasClass('popup-show'); return $modal.hasClass('popup-show');
} };
}); });

View file

@ -1,68 +1,63 @@
describe("undo button then redo button", function(){ describe('undo button then redo button', function () {
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); // creates a new pad helper.newPad(cb); // creates a new pad
this.timeout(60000); this.timeout(60000);
}); });
it("redo some typing with button", function(done){ it('redo some typing with button', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
// get the first text element inside the editable space // get the first text element inside the editable space
var $firstTextElement = inner$("div span").first(); const $firstTextElement = inner$('div span').first();
var originalValue = $firstTextElement.text(); // get the original value const originalValue = $firstTextElement.text(); // get the original value
var newString = "Foo"; const newString = 'Foo';
$firstTextElement.sendkeys(newString); // send line 1 to the pad $firstTextElement.sendkeys(newString); // send line 1 to the pad
var modifiedValue = $firstTextElement.text(); // get the modified value const modifiedValue = $firstTextElement.text(); // get the modified value
expect(modifiedValue).not.to.be(originalValue); // expect the value to change expect(modifiedValue).not.to.be(originalValue); // expect the value to change
// get undo and redo buttons // get undo and redo buttons
var $undoButton = chrome$(".buttonicon-undo"); const $undoButton = chrome$('.buttonicon-undo');
var $redoButton = chrome$(".buttonicon-redo"); const $redoButton = chrome$('.buttonicon-redo');
// click the buttons // click the buttons
$undoButton.click(); // removes foo $undoButton.click(); // removes foo
$redoButton.click(); // resends foo $redoButton.click(); // resends foo
helper.waitFor(function(){ helper.waitFor(() => inner$('div span').first().text() === newString).done(() => {
return inner$("div span").first().text() === newString; const finalValue = inner$('div').first().text();
}).done(function(){
var finalValue = inner$("div").first().text();
expect(finalValue).to.be(modifiedValue); // expect the value to change expect(finalValue).to.be(modifiedValue); // expect the value to change
done(); done();
}); });
}); });
it("redo some typing with keypress", function(done){ it('redo some typing with keypress', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
// get the first text element inside the editable space // get the first text element inside the editable space
var $firstTextElement = inner$("div span").first(); const $firstTextElement = inner$('div span').first();
var originalValue = $firstTextElement.text(); // get the original value const originalValue = $firstTextElement.text(); // get the original value
var newString = "Foo"; const newString = 'Foo';
$firstTextElement.sendkeys(newString); // send line 1 to the pad $firstTextElement.sendkeys(newString); // send line 1 to the pad
var modifiedValue = $firstTextElement.text(); // get the modified value const modifiedValue = $firstTextElement.text(); // get the modified value
expect(modifiedValue).not.to.be(originalValue); // expect the value to change expect(modifiedValue).not.to.be(originalValue); // expect the value to change
var e = inner$.Event(helper.evtType); var e = inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key e.ctrlKey = true; // Control key
e.which = 90; // z e.which = 90; // z
inner$("#innerdocbody").trigger(e); inner$('#innerdocbody').trigger(e);
var e = inner$.Event(helper.evtType); var e = inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key e.ctrlKey = true; // Control key
e.which = 121; // y e.which = 121; // y
inner$("#innerdocbody").trigger(e); inner$('#innerdocbody').trigger(e);
helper.waitFor(function(){ helper.waitFor(() => inner$('div span').first().text() === newString).done(() => {
return inner$("div span").first().text() === newString; const finalValue = inner$('div').first().text();
}).done(function(){
var finalValue = inner$("div").first().text();
expect(finalValue).to.be(modifiedValue); // expect the value to change expect(finalValue).to.be(modifiedValue); // expect the value to change
done(); done();
}); });
}); });
}); });

View file

@ -13,9 +13,9 @@
// Adapted from John McLear's original test case. // Adapted from John McLear's original test case.
xdescribe('Responsiveness of Editor', function() { xdescribe('Responsiveness of Editor', function () {
// create a new pad before each test run // create a new pad before each test run
beforeEach(function(cb) { beforeEach(function (cb) {
helper.newPad(cb); helper.newPad(cb);
this.timeout(6000); this.timeout(6000);
}); });
@ -23,67 +23,62 @@ xdescribe('Responsiveness of Editor', function() {
// And the test needs to be fixed to work in Firefox 52 on Windows 7. I am not sure why it fails on this specific platform // And the test needs to be fixed to work in Firefox 52 on Windows 7. I am not sure why it fails on this specific platform
// The errors show this.timeout... then crash the browser but I am sure something is actually causing the stack trace and // The errors show this.timeout... then crash the browser but I am sure something is actually causing the stack trace and
// I just need to narrow down what, offers to help accepted. // I just need to narrow down what, offers to help accepted.
it('Fast response to keypress in pad with large amount of contents', function(done) { it('Fast response to keypress in pad with large amount of contents', function (done) {
// skip on Windows Firefox 52.0
//skip on Windows Firefox 52.0 if (window.bowser && window.bowser.windows && window.bowser.firefox && window.bowser.version == '52.0') {
if(window.bowser && window.bowser.windows && window.bowser.firefox && window.bowser.version == "52.0") {
this.skip(); this.skip();
} }
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
var chars = '0000000000'; // row of placeholder chars const chars = '0000000000'; // row of placeholder chars
var amount = 200000; //number of blocks of chars we will insert const amount = 200000; // number of blocks of chars we will insert
var length = (amount * (chars.length) +1); // include a counter for each space const length = (amount * (chars.length) + 1); // include a counter for each space
var text = ''; // the text we're gonna insert let text = ''; // the text we're gonna insert
this.timeout(amount * 150); // Changed from 100 to 150 to allow Mac OSX Safari to be slow. this.timeout(amount * 150); // Changed from 100 to 150 to allow Mac OSX Safari to be slow.
// get keys to send // get keys to send
var keyMultiplier = 10; // multiplier * 10 == total number of key events const keyMultiplier = 10; // multiplier * 10 == total number of key events
var keysToSend = ''; let keysToSend = '';
for(var i=0; i <= keyMultiplier; i++) { for (var i = 0; i <= keyMultiplier; i++) {
keysToSend += chars; keysToSend += chars;
} }
var textElement = inner$('div'); const textElement = inner$('div');
textElement.sendkeys('{selectall}'); // select all textElement.sendkeys('{selectall}'); // select all
textElement.sendkeys('{del}'); // clear the pad text textElement.sendkeys('{del}'); // clear the pad text
for(var i=0; i <= amount; i++) { for (var i = 0; i <= amount; i++) {
text = text + chars + ' '; // add the chars and space to the text contents text = `${text + chars} `; // add the chars and space to the text contents
} }
inner$('div').first().text(text); // Put the text contents into the pad inner$('div').first().text(text); // Put the text contents into the pad
helper.waitFor(function(){ // Wait for the new contents to be on the pad helper.waitFor(() => // Wait for the new contents to be on the pad
return inner$('div').text().length > length; inner$('div').text().length > length,
}).done(function(){ ).done(() => {
expect(inner$('div').text().length).to.be.greaterThan(length); // has the text changed?
expect( inner$('div').text().length ).to.be.greaterThan( length ); // has the text changed? const start = Date.now(); // 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) // send some new text to the screen (ensure all 3 key events are sent)
var el = inner$('div').first(); const el = inner$('div').first();
for(var i = 0; i < keysToSend.length; ++i) { for (let i = 0; i < keysToSend.length; ++i) {
var x = keysToSend.charCodeAt(i); var x = keysToSend.charCodeAt(i);
['keyup', 'keypress', 'keydown'].forEach(function(type) { ['keyup', 'keypress', 'keydown'].forEach((type) => {
var e = $.Event(type); const e = $.Event(type);
e.keyCode = x; e.keyCode = x;
el.trigger(e); el.trigger(e);
}); });
} }
helper.waitFor(function(){ // Wait for the ability to process helper.waitFor(() => { // Wait for the ability to process
var el = inner$('body'); const el = inner$('body');
if(el[0].textContent.length > amount) return true; if (el[0].textContent.length > amount) return true;
}).done(function(){ }).done(() => {
var end = Date.now(); // get the current time const end = Date.now(); // get the current time
var delay = end - start; // get the delay as the current time minus the start time const delay = end - start; // get the delay as the current time minus the start time
expect(delay).to.be.below(600); expect(delay).to.be.below(600);
done(); done();
}, 5000); }, 5000);
}, 10000); }, 10000);
}); });
}); });

View file

@ -1,105 +1,101 @@
describe("select formatting buttons when selection has style applied", function(){ describe('select formatting buttons when selection has style applied', function () {
var STYLES = ['italic', 'bold', 'underline', 'strikethrough']; const STYLES = ['italic', 'bold', 'underline', 'strikethrough'];
var SHORTCUT_KEYS = ['I', 'B', 'U', '5']; // italic, bold, underline, strikethrough const SHORTCUT_KEYS = ['I', 'B', 'U', '5']; // italic, bold, underline, strikethrough
var FIRST_LINE = 0; const FIRST_LINE = 0;
before(function(cb){ before(function (cb) {
helper.newPad(cb); helper.newPad(cb);
this.timeout(60000); this.timeout(60000);
}); });
var applyStyleOnLine = function(style, line) { const applyStyleOnLine = function (style, line) {
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
selectLine(line); selectLine(line);
var $formattingButton = chrome$('.buttonicon-' + style); const $formattingButton = chrome$(`.buttonicon-${style}`);
$formattingButton.click(); $formattingButton.click();
} };
var isButtonSelected = function(style) { const isButtonSelected = function (style) {
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
var $formattingButton = chrome$('.buttonicon-' + style); const $formattingButton = chrome$(`.buttonicon-${style}`);
return $formattingButton.parent().hasClass('selected'); return $formattingButton.parent().hasClass('selected');
} };
var selectLine = function(lineNumber, offsetStart, offsetEnd) { var selectLine = function (lineNumber, offsetStart, offsetEnd) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var $line = inner$("div").eq(lineNumber); const $line = inner$('div').eq(lineNumber);
helper.selectLines($line, $line, offsetStart, offsetEnd); helper.selectLines($line, $line, offsetStart, offsetEnd);
} };
var placeCaretOnLine = function(lineNumber) { const placeCaretOnLine = function (lineNumber) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var $line = inner$("div").eq(lineNumber); const $line = inner$('div').eq(lineNumber);
$line.sendkeys('{leftarrow}'); $line.sendkeys('{leftarrow}');
} };
var undo = function() { const undo = function () {
var $undoButton = helper.padChrome$(".buttonicon-undo"); const $undoButton = helper.padChrome$('.buttonicon-undo');
$undoButton.click(); $undoButton.click();
} };
var testIfFormattingButtonIsDeselected = function(style) { const testIfFormattingButtonIsDeselected = function (style) {
it('deselects the ' + style + ' button', function(done) { it(`deselects the ${style} button`, function (done) {
helper.waitFor(function(){ helper.waitFor(() => isButtonSelected(style) === false).done(done);
return isButtonSelected(style) === false;
}).done(done)
}); });
} };
var testIfFormattingButtonIsSelected = function(style) { const testIfFormattingButtonIsSelected = function (style) {
it('selects the ' + style + ' button', function(done) { it(`selects the ${style} button`, function (done) {
helper.waitFor(function(){ helper.waitFor(() => isButtonSelected(style)).done(done);
return isButtonSelected(style);
}).done(done)
}); });
} };
var applyStyleOnLineAndSelectIt = function(line, style, cb) { const applyStyleOnLineAndSelectIt = function (line, style, cb) {
applyStyleOnLineOnFullLineAndRemoveSelection(line, style, selectLine, cb); applyStyleOnLineOnFullLineAndRemoveSelection(line, style, selectLine, cb);
} };
var applyStyleOnLineAndPlaceCaretOnit = function(line, style, cb) { const applyStyleOnLineAndPlaceCaretOnit = function (line, style, cb) {
applyStyleOnLineOnFullLineAndRemoveSelection(line, style, placeCaretOnLine, cb); applyStyleOnLineOnFullLineAndRemoveSelection(line, style, placeCaretOnLine, cb);
} };
var applyStyleOnLineOnFullLineAndRemoveSelection = function(line, style, selectTarget, cb) { var applyStyleOnLineOnFullLineAndRemoveSelection = function (line, style, selectTarget, cb) {
// see if line html has changed // see if line html has changed
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var oldLineHTML = inner$.find("div")[line]; const oldLineHTML = inner$.find('div')[line];
applyStyleOnLine(style, line); applyStyleOnLine(style, line);
helper.waitFor(function(){ helper.waitFor(() => {
var lineHTML = inner$.find("div")[line]; const lineHTML = inner$.find('div')[line];
return lineHTML !== oldLineHTML; return lineHTML !== oldLineHTML;
}); });
// remove selection from previous line // remove selection from previous line
selectLine(line + 1); selectLine(line + 1);
//setTimeout(function() { // setTimeout(function() {
// select the text or place the caret on a position that // select the text or place the caret on a position that
// has the formatting text applied previously // has the formatting text applied previously
selectTarget(line); selectTarget(line);
cb(); cb();
//}, 1000); // }, 1000);
} };
var pressFormattingShortcutOnSelection = function(key) { const pressFormattingShortcutOnSelection = function (key) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe // get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first(); const $firstTextElement = inner$('div').first();
//select this text element // select this text element
$firstTextElement.sendkeys('{selectall}'); $firstTextElement.sendkeys('{selectall}');
var e = inner$.Event(helper.evtType); const e = inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key e.ctrlKey = true; // Control key
e.which = key.charCodeAt(0); // I, U, B, 5 e.which = key.charCodeAt(0); // I, U, B, 5
inner$("#innerdocbody").trigger(e); inner$('#innerdocbody').trigger(e);
} };
STYLES.forEach(function(style){ STYLES.forEach((style) => {
context('when selection is in a text with ' + style + ' applied', function(){ context(`when selection is in a text with ${style} applied`, function () {
before(function (done) { before(function (done) {
this.timeout(4000); this.timeout(4000);
applyStyleOnLineAndSelectIt(FIRST_LINE, style, done); applyStyleOnLineAndSelectIt(FIRST_LINE, style, done);
@ -112,7 +108,7 @@ describe("select formatting buttons when selection has style applied", function(
testIfFormattingButtonIsSelected(style); testIfFormattingButtonIsSelected(style);
}); });
context('when caret is in a position with ' + style + ' applied', function(){ context(`when caret is in a position with ${style} applied`, function () {
before(function (done) { before(function (done) {
this.timeout(4000); this.timeout(4000);
applyStyleOnLineAndPlaceCaretOnit(FIRST_LINE, style, done); applyStyleOnLineAndPlaceCaretOnit(FIRST_LINE, style, done);
@ -122,12 +118,12 @@ describe("select formatting buttons when selection has style applied", function(
undo(); undo();
}); });
testIfFormattingButtonIsSelected(style) testIfFormattingButtonIsSelected(style);
}); });
}); });
context('when user applies a style and the selection does not change', function() { context('when user applies a style and the selection does not change', function () {
var style = STYLES[0]; // italic const style = STYLES[0]; // italic
before(function () { before(function () {
applyStyleOnLine(style, FIRST_LINE); applyStyleOnLine(style, FIRST_LINE);
}); });
@ -143,16 +139,16 @@ describe("select formatting buttons when selection has style applied", function(
}); });
}); });
SHORTCUT_KEYS.forEach(function(key, index){ SHORTCUT_KEYS.forEach((key, index) => {
var styleOfTheShortcut = STYLES[index]; // italic, bold, ... const styleOfTheShortcut = STYLES[index]; // italic, bold, ...
context('when user presses CMD + ' + key, function() { context(`when user presses CMD + ${key}`, function () {
before(function () { before(function () {
pressFormattingShortcutOnSelection(key); pressFormattingShortcutOnSelection(key);
}); });
testIfFormattingButtonIsSelected(styleOfTheShortcut); testIfFormattingButtonIsSelected(styleOfTheShortcut);
context('and user presses CMD + ' + key + ' again', function() { context(`and user presses CMD + ${key} again`, function () {
before(function () { before(function () {
pressFormattingShortcutOnSelection(key); pressFormattingShortcutOnSelection(key);
}); });

View file

@ -1,34 +1,34 @@
describe("strikethrough button", function(){ describe('strikethrough button', function () {
//create a new pad before each test run // create a new pad before each test run
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); helper.newPad(cb);
this.timeout(60000); this.timeout(60000);
}); });
it("makes text strikethrough", function(done) { it('makes text strikethrough', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe // get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first(); const $firstTextElement = inner$('div').first();
//select this text element // select this text element
$firstTextElement.sendkeys('{selectall}'); $firstTextElement.sendkeys('{selectall}');
//get the strikethrough button and click it // get the strikethrough button and click it
var $strikethroughButton = chrome$(".buttonicon-strikethrough"); const $strikethroughButton = chrome$('.buttonicon-strikethrough');
$strikethroughButton.click(); $strikethroughButton.click();
//ace creates a new dom element when you press a button, so just get the first text element again // ace creates a new dom element when you press a button, so just get the first text element again
var $newFirstTextElement = inner$("div").first(); const $newFirstTextElement = inner$('div').first();
// is there a <i> element now? // is there a <i> element now?
var isstrikethrough = $newFirstTextElement.find("s").length === 1; const isstrikethrough = $newFirstTextElement.find('s').length === 1;
//expect it to be strikethrough // expect it to be strikethrough
expect(isstrikethrough).to.be(true); expect(isstrikethrough).to.be(true);
//make sure the text hasn't changed // make sure the text hasn't changed
expect($newFirstTextElement.text()).to.eql($firstTextElement.text()); expect($newFirstTextElement.text()).to.eql($firstTextElement.text());
done(); done();

View file

@ -1,47 +1,42 @@
//deactivated, we need a nice way to get the timeslider, this is ugly // deactivated, we need a nice way to get the timeslider, this is ugly
xdescribe("timeslider button takes you to the timeslider of a pad", function(){ xdescribe('timeslider button takes you to the timeslider of a pad', function () {
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); // creates a new pad helper.newPad(cb); // creates a new pad
this.timeout(60000); this.timeout(60000);
}); });
it("timeslider contained in URL", function(done){ it('timeslider contained in URL', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
// get the first text element inside the editable space // get the first text element inside the editable space
var $firstTextElement = inner$("div span").first(); const $firstTextElement = inner$('div span').first();
var originalValue = $firstTextElement.text(); // get the original value const originalValue = $firstTextElement.text(); // get the original value
var newValue = "Testing"+originalValue; const newValue = `Testing${originalValue}`;
$firstTextElement.sendkeys("Testing"); // send line 1 to the pad $firstTextElement.sendkeys('Testing'); // send line 1 to the pad
var modifiedValue = $firstTextElement.text(); // get the modified value const modifiedValue = $firstTextElement.text(); // get the modified value
expect(modifiedValue).not.to.be(originalValue); // expect the value to change expect(modifiedValue).not.to.be(originalValue); // expect the value to change
helper.waitFor(function(){ helper.waitFor(() => modifiedValue !== originalValue, // The value has changed so we can..
return modifiedValue !== originalValue; // The value has changed so we can.. ).done(() => {
}).done(function(){ const $timesliderButton = chrome$('#timesliderlink');
var $timesliderButton = chrome$("#timesliderlink");
$timesliderButton.click(); // So click the timeslider link $timesliderButton.click(); // So click the timeslider link
helper.waitFor(function(){ helper.waitFor(() => {
var iFrameURL = chrome$.window.location.href; const iFrameURL = chrome$.window.location.href;
if(iFrameURL){ if (iFrameURL) {
return iFrameURL.indexOf("timeslider") !== -1; return iFrameURL.indexOf('timeslider') !== -1;
}else{ } else {
return false; // the URL hasnt been set yet return false; // the URL hasnt been set yet
} }
}).done(function(){ }).done(() => {
// click the buttons // click the buttons
var iFrameURL = chrome$.window.location.href; // get the url const iFrameURL = chrome$.window.location.href; // get the url
var inTimeslider = iFrameURL.indexOf("timeslider") !== -1; const inTimeslider = iFrameURL.indexOf('timeslider') !== -1;
expect(inTimeslider).to.be(true); // expect the value to change expect(inTimeslider).to.be(true); // expect the value to change
done(); done();
}); });
}); });
}); });
}); });

View file

@ -1,32 +1,32 @@
describe("timeslider follow", function(){ describe('timeslider follow', function () {
//create a new pad before each test run // create a new pad before each test run
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); helper.newPad(cb);
}); });
it("content as it's added to timeslider", async function() { it("content as it's added to timeslider", async function () {
// send 6 revisions // send 6 revisions
let revs = 6; const revs = 6;
let message = 'a\n\n\n\n\n\n\n\n\n\n'; const message = 'a\n\n\n\n\n\n\n\n\n\n';
let newLines = message.split('\n').length const newLines = message.split('\n').length;
for (let i=0;i<revs;i++){ for (let i = 0; i < revs; i++) {
await helper.edit(message, newLines*i + 1); await helper.edit(message, newLines * i + 1);
} }
await helper.gotoTimeslider(0); await helper.gotoTimeslider(0);
await helper.waitForPromise(function(){return helper.contentWindow().location.hash === '#0'}) await helper.waitForPromise(() => helper.contentWindow().location.hash === '#0');
let originalTop = helper.contentWindow().$('#innerdocbody').offset(); const originalTop = helper.contentWindow().$('#innerdocbody').offset();
// set to follow contents as it arrives // set to follow contents as it arrives
helper.contentWindow().$('#options-followContents').prop("checked", true); helper.contentWindow().$('#options-followContents').prop('checked', true);
helper.contentWindow().$('#playpause_button_icon').click(); helper.contentWindow().$('#playpause_button_icon').click();
let newTop; let newTop;
return helper.waitForPromise(function(){ return helper.waitForPromise(() => {
newTop = helper.contentWindow().$('#innerdocbody').offset(); newTop = helper.contentWindow().$('#innerdocbody').offset();
return newTop.top < originalTop.top; return newTop.top < originalTop.top;
}) });
}); });
/** /**
@ -35,31 +35,28 @@ describe("timeslider follow", function(){
* the change is applied. * the change is applied.
* *
*/ */
it("only to lines that exist in the current pad view, see #4389", async function(){ it('only to lines that exist in the current pad view, see #4389', async function () {
// Select everything and clear via delete key // Select everything and clear via delete key
let e = helper.padInner$.Event(helper.evtType); const e = helper.padInner$.Event(helper.evtType);
e.keyCode = 8; //delete key e.keyCode = 8; // delete key
let lines = helper.linesDiv(); const lines = helper.linesDiv();
helper.selectLines(lines[0], lines[lines.length - 1]); // select all lines helper.selectLines(lines[0], lines[lines.length - 1]); // select all lines
// probably unnecessary, but wait for the selection to be Range not Caret // probably unnecessary, but wait for the selection to be Range not Caret
await helper.waitForPromise(function(){ await helper.waitForPromise(() => !helper.padInner$.document.getSelection().isCollapsed,
return !helper.padInner$.document.getSelection().isCollapsed; // only supported in FF57+
//only supported in FF57+ // return helper.padInner$.document.getSelection().type === 'Range';
//return helper.padInner$.document.getSelection().type === 'Range'; );
})
helper.padInner$('#innerdocbody').trigger(e); helper.padInner$('#innerdocbody').trigger(e);
await helper.waitForPromise(function(){ await helper.waitForPromise(() => helper.commits.length === 1);
return helper.commits.length === 1; await helper.edit('Test line\n\n');
}) await helper.edit('Another test line', 3);
await helper.edit("Test line\n\n")
await helper.edit("Another test line", 3)
await helper.gotoTimeslider(); await helper.gotoTimeslider();
// set to follow contents as it arrives // set to follow contents as it arrives
helper.contentWindow().$('#options-followContents').prop("checked", true); helper.contentWindow().$('#options-followContents').prop('checked', true);
let oldYPosition = helper.contentWindow().$("#editorcontainerbox")[0].scrollTop; const oldYPosition = helper.contentWindow().$('#editorcontainerbox')[0].scrollTop;
expect(oldYPosition).to.be(0); expect(oldYPosition).to.be(0);
/** /**
@ -71,40 +68,28 @@ describe("timeslider follow", function(){
// line 3 changed // line 3 changed
helper.contentWindow().$('#leftstep').click(); helper.contentWindow().$('#leftstep').click();
await helper.waitForPromise(function(){ await helper.waitForPromise(() => hasFollowedToLine(3));
return hasFollowedToLine(3);
})
// line 1 is the first line that changed // line 1 is the first line that changed
helper.contentWindow().$('#leftstep').click(); helper.contentWindow().$('#leftstep').click();
await helper.waitForPromise(function(){ await helper.waitForPromise(() => hasFollowedToLine(1));
return hasFollowedToLine(1);
})
// line 1 changed // line 1 changed
helper.contentWindow().$('#leftstep').click(); helper.contentWindow().$('#leftstep').click();
await helper.waitForPromise(function(){ await helper.waitForPromise(() => hasFollowedToLine(1));
return hasFollowedToLine(1);
})
// line 1 changed // line 1 changed
helper.contentWindow().$('#rightstep').click(); helper.contentWindow().$('#rightstep').click();
await helper.waitForPromise(function(){ await helper.waitForPromise(() => hasFollowedToLine(1));
return hasFollowedToLine(1);
})
// line 1 is the first line that changed // line 1 is the first line that changed
helper.contentWindow().$('#rightstep').click(); helper.contentWindow().$('#rightstep').click();
await helper.waitForPromise(function(){ await helper.waitForPromise(() => hasFollowedToLine(1));
return hasFollowedToLine(1);
})
// line 3 changed // line 3 changed
helper.contentWindow().$('#rightstep').click(); helper.contentWindow().$('#rightstep').click();
return helper.waitForPromise(function(){ return helper.waitForPromise(() => hasFollowedToLine(3));
return hasFollowedToLine(3); });
})
})
}); });
/** /**
@ -112,9 +97,8 @@ describe("timeslider follow", function(){
* @returns {boolean} scrolled to the lineOffset? * @returns {boolean} scrolled to the lineOffset?
*/ */
function hasFollowedToLine(lineNum) { function hasFollowedToLine(lineNum) {
let scrollPosition = helper.contentWindow().$("#editorcontainerbox")[0].scrollTop; const scrollPosition = helper.contentWindow().$('#editorcontainerbox')[0].scrollTop;
let lineOffset = helper.contentWindow().$('#innerdocbody').find(`div:nth-child(${lineNum})`)[0].offsetTop; const lineOffset = helper.contentWindow().$('#innerdocbody').find(`div:nth-child(${lineNum})`)[0].offsetTop;
return Math.abs(scrollPosition - lineOffset) < 1; return Math.abs(scrollPosition - lineOffset) < 1;
} }

View file

@ -1,37 +1,37 @@
describe("timeslider", function(){ describe('timeslider', function () {
//create a new pad before each test run // create a new pad before each test run
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); helper.newPad(cb);
}); });
/** /**
* @todo test authorsList * @todo test authorsList
*/ */
it("Shows a date and time in the timeslider and make sure it doesn't include NaN", async function() { it("Shows a date and time in the timeslider and make sure it doesn't include NaN", async function () {
// make some changes to produce 3 revisions // make some changes to produce 3 revisions
let revs = 3; const revs = 3;
for(let i=0; i < revs; i++) { for (let i = 0; i < revs; i++) {
await helper.edit('a\n'); await helper.edit('a\n');
} }
await helper.gotoTimeslider(revs); await helper.gotoTimeslider(revs);
await helper.waitForPromise(function(){return helper.contentWindow().location.hash === '#'+revs}) await helper.waitForPromise(() => helper.contentWindow().location.hash === `#${revs}`);
// the datetime of last edit // the datetime of last edit
let timerTimeLast = new Date(helper.timesliderTimerTime()).getTime(); const timerTimeLast = new Date(helper.timesliderTimerTime()).getTime();
// the day of this revision, e.g. August 12, 2020 (stripped the string "Saved") // the day of this revision, e.g. August 12, 2020 (stripped the string "Saved")
let dateLast = new Date(helper.revisionDateElem().substr(6)).getTime(); const dateLast = new Date(helper.revisionDateElem().substr(6)).getTime();
// the label/revision, ie Version 3 // the label/revision, ie Version 3
let labelLast = helper.revisionLabelElem().text(); const labelLast = helper.revisionLabelElem().text();
// the datetime should be a date // the datetime should be a date
expect( Number.isNaN(timerTimeLast)).to.eql(false); expect(Number.isNaN(timerTimeLast)).to.eql(false);
// the Date object of the day should not be NaN // the Date object of the day should not be NaN
expect( Number.isNaN(dateLast) ).to.eql(false) expect(Number.isNaN(dateLast)).to.eql(false);
// the label should be Version `Number` // the label should be Version `Number`
expect(labelLast).to.be(`Version ${revs}`); expect(labelLast).to.be(`Version ${revs}`);
@ -40,23 +40,23 @@ describe("timeslider", function(){
helper.sliderClick(1); helper.sliderClick(1);
// the datetime of last edit // the datetime of last edit
let timerTime = new Date(helper.timesliderTimerTime()).getTime(); const timerTime = new Date(helper.timesliderTimerTime()).getTime();
// the day of this revision, e.g. August 12, 2020 // the day of this revision, e.g. August 12, 2020
let date = new Date(helper.revisionDateElem().substr(6)).getTime(); const date = new Date(helper.revisionDateElem().substr(6)).getTime();
// the label/revision, e.g. Version 0 // the label/revision, e.g. Version 0
let label = helper.revisionLabelElem().text(); const label = helper.revisionLabelElem().text();
// the datetime should be a date // the datetime should be a date
expect( Number.isNaN(timerTime)).to.eql(false); expect(Number.isNaN(timerTime)).to.eql(false);
// the last revision should be newer or have the same time // the last revision should be newer or have the same time
expect(timerTimeLast).to.not.be.lessThan(timerTime); expect(timerTimeLast).to.not.be.lessThan(timerTime);
// the Date object of the day should not be NaN // the Date object of the day should not be NaN
expect( Number.isNaN(date) ).to.eql(false) expect(Number.isNaN(date)).to.eql(false);
// the label should be Version 0 // the label should be Version 0
expect( label ).to.be('Version 0'); expect(label).to.be('Version 0');
}); });
}); });

View file

@ -1,29 +1,29 @@
describe("timeslider", function(){ describe('timeslider', function () {
var padId = 735773577357+(Math.round(Math.random()*1000)); const padId = 735773577357 + (Math.round(Math.random() * 1000));
//create a new pad before each test run // create a new pad before each test run
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb, padId); helper.newPad(cb, padId);
}); });
it("Makes sure the export URIs are as expected when the padID is numeric", async function() { it('Makes sure the export URIs are as expected when the padID is numeric', async function () {
await helper.edit('a\n'); await helper.edit('a\n');
await helper.gotoTimeslider(1); await helper.gotoTimeslider(1);
// ensure we are on revision 1 // ensure we are on revision 1
await helper.waitForPromise(function(){return helper.contentWindow().location.hash === '#1'}) await helper.waitForPromise(() => helper.contentWindow().location.hash === '#1');
// expect URI to be similar to // expect URI to be similar to
// http://192.168.1.48:9001/p/2/1/export/html // http://192.168.1.48:9001/p/2/1/export/html
// http://192.168.1.48:9001/p/735773577399/1/export/html // http://192.168.1.48:9001/p/735773577399/1/export/html
let rev1ExportLink = helper.contentWindow().$('#exporthtmla').attr('href'); const rev1ExportLink = helper.contentWindow().$('#exporthtmla').attr('href');
expect(rev1ExportLink).to.contain('/1/export/html'); expect(rev1ExportLink).to.contain('/1/export/html');
// Click somewhere left on the timeslider to go to revision 0 // Click somewhere left on the timeslider to go to revision 0
helper.sliderClick(30); helper.sliderClick(30);
let rev0ExportLink = helper.contentWindow().$('#exporthtmla').attr('href'); const rev0ExportLink = helper.contentWindow().$('#exporthtmla').attr('href');
expect(rev0ExportLink).to.contain('/0/export/html'); expect(rev0ExportLink).to.contain('/0/export/html');
}); });
}); });

View file

@ -1,42 +1,42 @@
describe("timeslider", function(){ describe('timeslider', function () {
//create a new pad before each test run // create a new pad before each test run
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); helper.newPad(cb);
this.timeout(60000); this.timeout(60000);
}); });
it("loads adds a hundred revisions", function(done) { // passes it('loads adds a hundred revisions', function (done) { // passes
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
// make some changes to produce 100 revisions // make some changes to produce 100 revisions
var timePerRev = 900 const timePerRev = 900;
, revs = 99; const revs = 99;
this.timeout(revs*timePerRev+10000); this.timeout(revs * timePerRev + 10000);
for(var i=0; i < revs; i++) { for (let i = 0; i < revs; i++) {
setTimeout(function() { setTimeout(() => {
// enter 'a' in the first text element // enter 'a' in the first text element
inner$("div").first().sendkeys('a'); inner$('div').first().sendkeys('a');
}, timePerRev*i); }, timePerRev * i);
} }
chrome$('.buttonicon-savedRevision').click(); chrome$('.buttonicon-savedRevision').click();
setTimeout(function() { setTimeout(() => {
// go to timeslider // go to timeslider
$('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider'); $('#iframe-container iframe').attr('src', `${$('#iframe-container iframe').attr('src')}/timeslider`);
setTimeout(function() { setTimeout(() => {
var timeslider$ = $('#iframe-container iframe')[0].contentWindow.$; const timeslider$ = $('#iframe-container iframe')[0].contentWindow.$;
var $sliderBar = timeslider$('#ui-slider-bar'); const $sliderBar = timeslider$('#ui-slider-bar');
var latestContents = timeslider$('#innerdocbody').text(); const latestContents = timeslider$('#innerdocbody').text();
// Click somewhere on the timeslider // Click somewhere on the timeslider
var e = new jQuery.Event('mousedown'); let e = new jQuery.Event('mousedown');
// sets y co-ordinate of the pad slider modal. // sets y co-ordinate of the pad slider modal.
var base = (timeslider$('#ui-slider-bar').offset().top - 24) const base = (timeslider$('#ui-slider-bar').offset().top - 24);
e.clientX = e.pageX = 150; e.clientX = e.pageX = 150;
e.clientY = e.pageY = base+5; e.clientY = e.pageY = base + 5;
$sliderBar.trigger(e); $sliderBar.trigger(e);
e = new jQuery.Event('mousedown'); e = new jQuery.Event('mousedown');
@ -46,134 +46,127 @@ describe("timeslider", function(){
e = new jQuery.Event('mousedown'); e = new jQuery.Event('mousedown');
e.clientX = e.pageX = 150; e.clientX = e.pageX = 150;
e.clientY = e.pageY = base-5; e.clientY = e.pageY = base - 5;
$sliderBar.trigger(e); $sliderBar.trigger(e);
$sliderBar.trigger('mouseup') $sliderBar.trigger('mouseup');
setTimeout(function() { setTimeout(() => {
//make sure the text has changed // make sure the text has changed
expect( timeslider$('#innerdocbody').text() ).not.to.eql( latestContents ); expect(timeslider$('#innerdocbody').text()).not.to.eql(latestContents);
var starIsVisible = timeslider$('.star').is(":visible"); const starIsVisible = timeslider$('.star').is(':visible');
expect( starIsVisible ).to.eql( true ); expect(starIsVisible).to.eql(true);
done(); done();
}, 1000); }, 1000);
}, 6000); }, 6000);
}, revs*timePerRev); }, revs * timePerRev);
}); });
// Disabled as jquery trigger no longer works properly // Disabled as jquery trigger no longer works properly
xit("changes the url when clicking on the timeslider", function(done) { xit('changes the url when clicking on the timeslider', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
// make some changes to produce 7 revisions // make some changes to produce 7 revisions
var timePerRev = 1000 const timePerRev = 1000;
, revs = 20; const revs = 20;
this.timeout(revs*timePerRev+10000); this.timeout(revs * timePerRev + 10000);
for(var i=0; i < revs; i++) { for (let i = 0; i < revs; i++) {
setTimeout(function() { setTimeout(() => {
// enter 'a' in the first text element // enter 'a' in the first text element
inner$("div").first().sendkeys('a'); inner$('div').first().sendkeys('a');
}, timePerRev*i); }, timePerRev * i);
} }
setTimeout(function() { setTimeout(() => {
// go to timeslider // go to timeslider
$('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider'); $('#iframe-container iframe').attr('src', `${$('#iframe-container iframe').attr('src')}/timeslider`);
setTimeout(function() { setTimeout(() => {
var timeslider$ = $('#iframe-container iframe')[0].contentWindow.$; const timeslider$ = $('#iframe-container iframe')[0].contentWindow.$;
var $sliderBar = timeslider$('#ui-slider-bar'); const $sliderBar = timeslider$('#ui-slider-bar');
var latestContents = timeslider$('#innerdocbody').text(); const latestContents = timeslider$('#innerdocbody').text();
var oldUrl = $('#iframe-container iframe')[0].contentWindow.location.hash; const oldUrl = $('#iframe-container iframe')[0].contentWindow.location.hash;
// Click somewhere on the timeslider // Click somewhere on the timeslider
var e = new jQuery.Event('mousedown'); const e = new jQuery.Event('mousedown');
e.clientX = e.pageX = 150; e.clientX = e.pageX = 150;
e.clientY = e.pageY = 60; e.clientY = e.pageY = 60;
$sliderBar.trigger(e); $sliderBar.trigger(e);
helper.waitFor(function(){ helper.waitFor(() => $('#iframe-container iframe')[0].contentWindow.location.hash != oldUrl, 6000).always(() => {
return $('#iframe-container iframe')[0].contentWindow.location.hash != oldUrl; expect($('#iframe-container iframe')[0].contentWindow.location.hash).not.to.eql(oldUrl);
}, 6000).always(function(){
expect( $('#iframe-container iframe')[0].contentWindow.location.hash ).not.to.eql( oldUrl );
done(); done();
}); });
}, 6000); }, 6000);
}, revs*timePerRev); }, revs * timePerRev);
}); });
it("jumps to a revision given in the url", function(done) { it('jumps to a revision given in the url', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
this.timeout(40000); this.timeout(40000);
// wait for the text to be loaded // wait for the text to be loaded
helper.waitFor(function(){ helper.waitFor(() => inner$('body').text().length != 0, 10000).always(() => {
return inner$('body').text().length != 0; const newLines = inner$('body div').length;
}, 10000).always(function() { const oldLength = inner$('body').text().length + newLines / 2;
var newLines = inner$('body div').length; expect(oldLength).to.not.eql(0);
var oldLength = inner$('body').text().length + newLines / 2; inner$('div').first().sendkeys('a');
expect( oldLength ).to.not.eql( 0 ); let timeslider$;
inner$("div").first().sendkeys('a');
var timeslider$;
// wait for our additional revision to be added // wait for our additional revision to be added
helper.waitFor(function(){ helper.waitFor(() => {
// newLines takes the new lines into account which are strippen when using // newLines takes the new lines into account which are strippen when using
// inner$('body').text(), one <div> is used for one line in ACE. // inner$('body').text(), one <div> is used for one line in ACE.
var lenOkay = inner$('body').text().length + newLines / 2 != oldLength; const lenOkay = inner$('body').text().length + newLines / 2 != oldLength;
// this waits for the color to be added to our <span>, which means that the revision // this waits for the color to be added to our <span>, which means that the revision
// was accepted by the server. // was accepted by the server.
var colorOkay = inner$('span').first().attr('class').indexOf("author-") == 0; const colorOkay = inner$('span').first().attr('class').indexOf('author-') == 0;
return lenOkay && colorOkay; return lenOkay && colorOkay;
}, 10000).always(function() { }, 10000).always(() => {
// go to timeslider with a specific revision set // go to timeslider with a specific revision set
$('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider#0'); $('#iframe-container iframe').attr('src', `${$('#iframe-container iframe').attr('src')}/timeslider#0`);
// wait for the timeslider to be loaded // wait for the timeslider to be loaded
helper.waitFor(function(){ helper.waitFor(() => {
try { try {
timeslider$ = $('#iframe-container iframe')[0].contentWindow.$; timeslider$ = $('#iframe-container iframe')[0].contentWindow.$;
} catch(e){} } catch (e) {}
if(timeslider$){ if (timeslider$) {
return timeslider$('#innerdocbody').text().length == oldLength; return timeslider$('#innerdocbody').text().length == oldLength;
} }
}, 10000).always(function(){ }, 10000).always(() => {
expect( timeslider$('#innerdocbody').text().length ).to.eql( oldLength ); expect(timeslider$('#innerdocbody').text().length).to.eql(oldLength);
done(); done();
}); });
}); });
}); });
}); });
it("checks the export url", function(done) { it('checks the export url', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
this.timeout(11000); this.timeout(11000);
inner$("div").first().sendkeys('a'); inner$('div').first().sendkeys('a');
setTimeout(function() { setTimeout(() => {
// go to timeslider // go to timeslider
$('#iframe-container iframe').attr('src', $('#iframe-container iframe').attr('src')+'/timeslider#0'); $('#iframe-container iframe').attr('src', `${$('#iframe-container iframe').attr('src')}/timeslider#0`);
var timeslider$; let timeslider$;
var exportLink; let exportLink;
helper.waitFor(function(){ helper.waitFor(() => {
try{ try {
timeslider$ = $('#iframe-container iframe')[0].contentWindow.$; timeslider$ = $('#iframe-container iframe')[0].contentWindow.$;
}catch(e){} } catch (e) {}
if(!timeslider$) if (!timeslider$) return false;
return false;
exportLink = timeslider$('#exportplaina').attr('href'); exportLink = timeslider$('#exportplaina').attr('href');
if(!exportLink) if (!exportLink) return false;
return false; return exportLink.substr(exportLink.length - 12) == '0/export/txt';
return exportLink.substr(exportLink.length - 12) == "0/export/txt"; }, 6000).always(() => {
}, 6000).always(function(){ expect(exportLink.substr(exportLink.length - 12)).to.eql('0/export/txt');
expect( exportLink.substr(exportLink.length - 12) ).to.eql( "0/export/txt" );
done(); done();
}); });
}, 2500); }, 2500);

View file

@ -1,61 +1,54 @@
describe("undo button", function(){ describe('undo button', function () {
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); // creates a new pad helper.newPad(cb); // creates a new pad
this.timeout(60000); this.timeout(60000);
}); });
it("undo some typing by clicking undo button", function(done){ it('undo some typing by clicking undo button', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
// get the first text element inside the editable space // get the first text element inside the editable space
var $firstTextElement = inner$("div span").first(); const $firstTextElement = inner$('div span').first();
var originalValue = $firstTextElement.text(); // get the original value const originalValue = $firstTextElement.text(); // get the original value
$firstTextElement.sendkeys("foo"); // send line 1 to the pad $firstTextElement.sendkeys('foo'); // send line 1 to the pad
var modifiedValue = $firstTextElement.text(); // get the modified value const modifiedValue = $firstTextElement.text(); // get the modified value
expect(modifiedValue).not.to.be(originalValue); // expect the value to change expect(modifiedValue).not.to.be(originalValue); // expect the value to change
// get clear authorship button as a variable // get clear authorship button as a variable
var $undoButton = chrome$(".buttonicon-undo"); const $undoButton = chrome$('.buttonicon-undo');
// click the button // click the button
$undoButton.click(); $undoButton.click();
helper.waitFor(function(){ helper.waitFor(() => inner$('div span').first().text() === originalValue).done(() => {
return inner$("div span").first().text() === originalValue; const finalValue = inner$('div span').first().text();
}).done(function(){
var finalValue = inner$("div span").first().text();
expect(finalValue).to.be(originalValue); // expect the value to change expect(finalValue).to.be(originalValue); // expect the value to change
done(); done();
}); });
}); });
it("undo some typing using a keypress", function(done){ it('undo some typing using a keypress', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
// get the first text element inside the editable space // get the first text element inside the editable space
var $firstTextElement = inner$("div span").first(); const $firstTextElement = inner$('div span').first();
var originalValue = $firstTextElement.text(); // get the original value const originalValue = $firstTextElement.text(); // get the original value
$firstTextElement.sendkeys("foo"); // send line 1 to the pad $firstTextElement.sendkeys('foo'); // send line 1 to the pad
var modifiedValue = $firstTextElement.text(); // get the modified value const modifiedValue = $firstTextElement.text(); // get the modified value
expect(modifiedValue).not.to.be(originalValue); // expect the value to change expect(modifiedValue).not.to.be(originalValue); // expect the value to change
var e = inner$.Event(helper.evtType); const e = inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key e.ctrlKey = true; // Control key
e.which = 90; // z e.which = 90; // z
inner$("#innerdocbody").trigger(e); inner$('#innerdocbody').trigger(e);
helper.waitFor(function(){ helper.waitFor(() => inner$('div span').first().text() === originalValue).done(() => {
return inner$("div span").first().text() === originalValue; const finalValue = inner$('div span').first().text();
}).done(function(){
var finalValue = inner$("div span").first().text();
expect(finalValue).to.be(originalValue); // expect the value to change expect(finalValue).to.be(originalValue); // expect the value to change
done(); done();
}); });
}); });
}); });

View file

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

View file

@ -1,56 +1,54 @@
describe("urls", function(){ describe('urls', function () {
//create a new pad before each test run // create a new pad before each test run
beforeEach(function(cb){ beforeEach(function (cb) {
helper.newPad(cb); helper.newPad(cb);
this.timeout(60000); this.timeout(60000);
}); });
it("when you enter an url, it becomes clickable", function(done) { it('when you enter an url, it becomes clickable', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe // get the first text element out of the inner iframe
var firstTextElement = inner$("div").first(); const firstTextElement = inner$('div').first();
// simulate key presses to delete content // simulate key presses to delete content
firstTextElement.sendkeys('{selectall}'); // select all firstTextElement.sendkeys('{selectall}'); // select all
firstTextElement.sendkeys('{del}'); // clear the first line firstTextElement.sendkeys('{del}'); // clear the first line
firstTextElement.sendkeys('https://etherpad.org'); // insert a URL firstTextElement.sendkeys('https://etherpad.org'); // insert a URL
helper.waitFor(function(){ helper.waitFor(() => inner$('div').first().find('a').length === 1, 2000).done(done);
return inner$("div").first().find("a").length === 1;
}, 2000).done(done);
}); });
it("when you enter a url containing a !, it becomes clickable and contains the whole URL", function(done) { it('when you enter a url containing a !, it becomes clickable and contains the whole URL', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe // get the first text element out of the inner iframe
var firstTextElement = inner$("div").first(); const firstTextElement = inner$('div').first();
var url = "https://etherpad.org/!foo"; const url = 'https://etherpad.org/!foo';
// simulate key presses to delete content // simulate key presses to delete content
firstTextElement.sendkeys('{selectall}'); // select all firstTextElement.sendkeys('{selectall}'); // select all
firstTextElement.sendkeys('{del}'); // clear the first line firstTextElement.sendkeys('{del}'); // clear the first line
firstTextElement.sendkeys(url); // insert a URL firstTextElement.sendkeys(url); // insert a URL
helper.waitFor(function(){ helper.waitFor(() => {
if(inner$("div").first().find("a").length === 1){ // if it contains an A link if (inner$('div').first().find('a').length === 1) { // if it contains an A link
if(inner$("div").first().find("a")[0].href === url){ if (inner$('div').first().find('a')[0].href === url) {
return true; return true;
} }
}; }
}, 2000).done(done); }, 2000).done(done);
}); });
it("when you enter a url followed by a ], the ] is not included in the URL", function(done) { it('when you enter a url followed by a ], the ] is not included in the URL', function (done) {
var inner$ = helper.padInner$; const inner$ = helper.padInner$;
var chrome$ = helper.padChrome$; const chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe // get the first text element out of the inner iframe
var firstTextElement = inner$("div").first(); const firstTextElement = inner$('div').first();
var url = "https://etherpad.org/"; const url = 'https://etherpad.org/';
// simulate key presses to delete content // simulate key presses to delete content
firstTextElement.sendkeys('{selectall}'); // select all firstTextElement.sendkeys('{selectall}'); // select all
@ -58,13 +56,12 @@ describe("urls", function(){
firstTextElement.sendkeys(url); // insert a URL firstTextElement.sendkeys(url); // insert a URL
firstTextElement.sendkeys(']'); // put a ] after it firstTextElement.sendkeys(']'); // put a ] after it
helper.waitFor(function(){ helper.waitFor(() => {
if(inner$("div").first().find("a").length === 1){ // if it contains an A link if (inner$('div').first().find('a').length === 1) { // if it contains an A link
if(inner$("div").first().find("a")[0].href === url){ if (inner$('div').first().find('a')[0].href === url) {
return true; return true;
} }
}; }
}, 2000).done(done); }, 2000).done(done);
}); });
}); });

View file

@ -1,48 +1,46 @@
describe('Automatic pad reload on Force Reconnect message', function() { describe('Automatic pad reload on Force Reconnect message', function () {
var padId, $originalPadFrame; let padId, $originalPadFrame;
beforeEach(function(done) { beforeEach(function (done) {
padId = helper.newPad(function() { padId = helper.newPad(() => {
// enable userdup error to have timer to force reconnect // enable userdup error to have timer to force reconnect
var $errorMessageModal = helper.padChrome$('#connectivity .userdup'); const $errorMessageModal = helper.padChrome$('#connectivity .userdup');
$errorMessageModal.addClass('with_reconnect_timer'); $errorMessageModal.addClass('with_reconnect_timer');
// make sure there's a timeout set, otherwise automatic reconnect won't be enabled // make sure there's a timeout set, otherwise automatic reconnect won't be enabled
helper.padChrome$.window.clientVars.automaticReconnectionTimeout = 2; helper.padChrome$.window.clientVars.automaticReconnectionTimeout = 2;
// open same pad on another iframe, to force userdup error // open same pad on another iframe, to force userdup error
var $otherIframeWithSamePad = $('<iframe src="/p/' + padId + '" style="height: 1px;"></iframe>'); const $otherIframeWithSamePad = $(`<iframe src="/p/${padId}" style="height: 1px;"></iframe>`);
$originalPadFrame = $('#iframe-container iframe'); $originalPadFrame = $('#iframe-container iframe');
$otherIframeWithSamePad.insertAfter($originalPadFrame); $otherIframeWithSamePad.insertAfter($originalPadFrame);
// wait for modal to be displayed // wait for modal to be displayed
helper.waitFor(function() { helper.waitFor(() => $errorMessageModal.is(':visible'), 50000).done(done);
return $errorMessageModal.is(':visible');
}, 50000).done(done);
}); });
this.timeout(60000); this.timeout(60000);
}); });
it('displays a count down timer to automatically reconnect', function(done) { it('displays a count down timer to automatically reconnect', function (done) {
var $errorMessageModal = helper.padChrome$('#connectivity .userdup'); const $errorMessageModal = helper.padChrome$('#connectivity .userdup');
var $countDownTimer = $errorMessageModal.find('.reconnecttimer'); const $countDownTimer = $errorMessageModal.find('.reconnecttimer');
expect($countDownTimer.is(':visible')).to.be(true); expect($countDownTimer.is(':visible')).to.be(true);
done(); done();
}); });
context('and user clicks on Cancel', function() { context('and user clicks on Cancel', function () {
beforeEach(function() { beforeEach(function () {
var $errorMessageModal = helper.padChrome$('#connectivity .userdup'); const $errorMessageModal = helper.padChrome$('#connectivity .userdup');
$errorMessageModal.find('#cancelreconnect').click(); $errorMessageModal.find('#cancelreconnect').click();
}); });
it('does not show Cancel button nor timer anymore', function(done) { it('does not show Cancel button nor timer anymore', function (done) {
var $errorMessageModal = helper.padChrome$('#connectivity .userdup'); const $errorMessageModal = helper.padChrome$('#connectivity .userdup');
var $countDownTimer = $errorMessageModal.find('.reconnecttimer'); const $countDownTimer = $errorMessageModal.find('.reconnecttimer');
var $cancelButton = $errorMessageModal.find('#cancelreconnect'); const $cancelButton = $errorMessageModal.find('#cancelreconnect');
expect($countDownTimer.is(':visible')).to.be(false); expect($countDownTimer.is(':visible')).to.be(false);
expect($cancelButton.is(':visible')).to.be(false); expect($cancelButton.is(':visible')).to.be(false);
@ -51,19 +49,17 @@ describe('Automatic pad reload on Force Reconnect message', function() {
}); });
}); });
context('and user does not click on Cancel until timer expires', function() { context('and user does not click on Cancel until timer expires', function () {
var padWasReloaded = false; let padWasReloaded = false;
beforeEach(function() { beforeEach(function () {
$originalPadFrame.one('load', function() { $originalPadFrame.one('load', () => {
padWasReloaded = true; padWasReloaded = true;
}); });
}); });
it('reloads the pad', function(done) { it('reloads the pad', function (done) {
helper.waitFor(function() { helper.waitFor(() => padWasReloaded, 5000).done(done);
return padWasReloaded;
}, 5000).done(done);
this.timeout(5000); this.timeout(5000);
}); });

View file

@ -1,13 +1,13 @@
var srcFolder = "../../../src/node_modules/"; var srcFolder = '../../../src/node_modules/';
var wd = require(srcFolder + "wd"); var wd = require(`${srcFolder}wd`);
var async = require(srcFolder + "async"); var async = require(`${srcFolder}async`);
var config = { var config = {
host: "ondemand.saucelabs.com" host: 'ondemand.saucelabs.com',
, port: 80 port: 80,
, username: process.env.SAUCE_USER username: process.env.SAUCE_USER,
, accessKey: process.env.SAUCE_ACCESS_KEY accessKey: process.env.SAUCE_ACCESS_KEY,
} };
var allTestsPassed = true; var allTestsPassed = true;
// overwrite the default exit code // overwrite the default exit code
@ -20,26 +20,26 @@ process.on('exit', (code) => {
} }
}) })
var sauceTestWorker = async.queue(function (testSettings, callback) { var sauceTestWorker = async.queue((testSettings, callback) => {
var browser = wd.promiseChainRemote(config.host, config.port, config.username, config.accessKey); const browser = wd.promiseChainRemote(config.host, config.port, config.username, config.accessKey);
var name = process.env.GIT_HASH + " - " + testSettings.browserName + " " + testSettings.version + ", " + testSettings.platform; const name = `${process.env.GIT_HASH} - ${testSettings.browserName} ${testSettings.version}, ${testSettings.platform}`;
testSettings.name = name; testSettings.name = name;
testSettings["public"] = true; testSettings.public = true;
testSettings["build"] = process.env.GIT_HASH; 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.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; testSettings.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
browser.init(testSettings).get("http://localhost:9001/tests/frontend/", function(){ browser.init(testSettings).get('http://localhost:9001/tests/frontend/', () => {
var url = "https://saucelabs.com/jobs/" + browser.sessionID; const url = `https://saucelabs.com/jobs/${browser.sessionID}`;
console.log("Remote sauce test '" + name + "' started! " + url); console.log(`Remote sauce test '${name}' started! ${url}`);
//tear down the test excecution // tear down the test excecution
var stopSauce = function(success,timesup){ const stopSauce = function (success, timesup) {
clearInterval(getStatusInterval); clearInterval(getStatusInterval);
clearTimeout(timeout); clearTimeout(timeout);
browser.quit(function(){ browser.quit(() => {
if(!success){ if (!success) {
allTestsPassed = false; allTestsPassed = false;
} }
@ -48,37 +48,37 @@ var sauceTestWorker = async.queue(function (testSettings, callback) {
printLog(logIndex); printLog(logIndex);
if (timesup) { if (timesup) {
console.log("[" + testSettings.browserName + " " + testSettings.platform + (testSettings.version === "" ? '' : (" " + testSettings.version)) + "] \x1B[31mFAILED\x1B[39m allowed test duration exceeded"); console.log(`[${testSettings.browserName} ${testSettings.platform}${testSettings.version === '' ? '' : (` ${testSettings.version}`)}] \x1B[31mFAILED\x1B[39m allowed test duration exceeded`);
} }
console.log("Remote sauce test '" + name + "' finished! " + url); console.log(`Remote sauce test '${name}' finished! ${url}`);
callback(); callback();
}); });
} };
/** /**
* timeout if a test hangs or the job exceeds 14.5 minutes * timeout if a test hangs or the job exceeds 14.5 minutes
* It's necessary because if travis kills the saucelabs session due to inactivity, we don't get any output * 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 * @todo this should be configured in testSettings, see https://wiki.saucelabs.com/display/DOCS/Test+Configuration+Options#TestConfigurationOptions-Timeouts
*/ */
var timeout = setTimeout(function(){ var timeout = setTimeout(() => {
stopSauce(false,true); stopSauce(false, true);
}, 870000); // travis timeout is 15 minutes, set this to a slightly lower value }, 870000); // travis timeout is 15 minutes, set this to a slightly lower value
var knownConsoleText = ""; let knownConsoleText = '';
// how many characters of the log have been sent to travis // how many characters of the log have been sent to travis
let logIndex = 0; let logIndex = 0;
var getStatusInterval = setInterval(function(){ var getStatusInterval = setInterval(() => {
browser.eval("$('#console').text()", function(err, consoleText){ browser.eval("$('#console').text()", (err, consoleText) => {
if(!consoleText || err){ if (!consoleText || err) {
return; return;
} }
knownConsoleText = consoleText; knownConsoleText = consoleText;
if(knownConsoleText.indexOf("FINISHED") > 0){ if (knownConsoleText.indexOf('FINISHED') > 0) {
let match = knownConsoleText.match(/FINISHED.*([0-9]+) tests passed, ([0-9]+) tests failed/); const match = knownConsoleText.match(/FINISHED.*([0-9]+) tests passed, ([0-9]+) tests failed/);
// finished without failures // finished without failures
if (match[2] && match[2] == '0'){ if (match[2] && match[2] == '0') {
stopSauce(true); stopSauce(true);
// finished but some tests did not return or some tests failed // finished but some tests did not return or some tests failed
@ -99,32 +99,29 @@ var sauceTestWorker = async.queue(function (testSettings, callback) {
* *
* @param {number} index offset from where to start * @param {number} index offset from where to start
*/ */
function printLog(index){ function printLog(index) {
let testResult = knownConsoleText.substring(index).replace(/\[red\]/g,'\x1B[31m').replace(/\[yellow\]/g,'\x1B[33m') let testResult = knownConsoleText.substring(index).replace(/\[red\]/g, '\x1B[31m').replace(/\[yellow\]/g, '\x1B[33m')
.replace(/\[green\]/g,'\x1B[32m').replace(/\[clear\]/g, '\x1B[39m'); .replace(/\[green\]/g, '\x1B[32m').replace(/\[clear\]/g, '\x1B[39m');
testResult = testResult.split("\\n").map(function(line){ testResult = testResult.split('\\n').map((line) => `[${testSettings.browserName} ${testSettings.platform}${testSettings.version === '' ? '' : (` ${testSettings.version}`)}] ${line}`).join('\n');
return "[" + testSettings.browserName + " " + testSettings.platform + (testSettings.version === "" ? '' : (" " + testSettings.version)) + "] " + line;
}).join("\n");
console.log(testResult); console.log(testResult);
} }
}); });
}, 6); // run 6 tests in parrallel
}, 6); //run 6 tests in parrallel
// 1) Firefox on Linux // 1) Firefox on Linux
sauceTestWorker.push({ sauceTestWorker.push({
'platform' : 'Windows 7' platform: 'Windows 7',
, 'browserName' : 'firefox' browserName: 'firefox',
, 'version' : '52.0' version: '52.0',
}); });
// 2) Chrome on Linux // 2) Chrome on Linux
sauceTestWorker.push({ sauceTestWorker.push({
'platform' : 'Windows 7' platform: 'Windows 7',
, 'browserName' : 'chrome' browserName: 'chrome',
, 'version' : '55.0' version: '55.0',
, 'args' : ['--use-fake-device-for-media-stream'] args: ['--use-fake-device-for-media-stream'],
}); });
/* /*
@ -138,9 +135,9 @@ sauceTestWorker.push({
// 4) Safari on OSX 10.14 // 4) Safari on OSX 10.14
sauceTestWorker.push({ sauceTestWorker.push({
'platform' : 'OS X 10.15' platform: 'OS X 10.15',
, 'browserName' : 'safari' browserName: 'safari',
, 'version' : '13.1' version: '13.1',
}); });
// IE 10 doesn't appear to be working anyway // IE 10 doesn't appear to be working anyway
/* /*
@ -153,17 +150,17 @@ sauceTestWorker.push({
*/ */
// 5) Edge on Win 10 // 5) Edge on Win 10
sauceTestWorker.push({ sauceTestWorker.push({
'platform' : 'Windows 10' platform: 'Windows 10',
, 'browserName' : 'microsoftedge' browserName: 'microsoftedge',
, 'version' : '83.0' version: '83.0',
}); });
// 6) Firefox on Win 7 // 6) Firefox on Win 7
sauceTestWorker.push({ sauceTestWorker.push({
'platform' : 'Windows 7' platform: 'Windows 7',
, 'browserName' : 'firefox' browserName: 'firefox',
, 'version' : '78.0' version: '78.0',
}); });
sauceTestWorker.drain(function() { sauceTestWorker.drain(() => {
process.exit(allTestsPassed ? 0 : 1); process.exit(allTestsPassed ? 0 : 1);
}); });

View file

@ -1,25 +1,24 @@
try{ try {
var etherpad = require("../../src/node_modules/etherpad-cli-client"); var etherpad = require('../../src/node_modules/etherpad-cli-client');
//ugly // ugly
} catch { } catch {
var etherpad = require("etherpad-cli-client") var etherpad = require('etherpad-cli-client');
} }
var pad = etherpad.connect(process.argv[2]); const pad = etherpad.connect(process.argv[2]);
pad.on("connected", function(){ pad.on('connected', () => {
setTimeout(() => {
setTimeout(function(){ setInterval(() => {
setInterval(function(){ pad.append('1');
pad.append("1");
}, process.argv[3]); }, process.argv[3]);
},500); // wait because CLIENT_READY message is included in ratelimit }, 500); // wait because CLIENT_READY message is included in ratelimit
setTimeout(function(){ setTimeout(() => {
process.exit(0); process.exit(0);
},11000) }, 11000);
}); });
// in case of disconnect exit code 1 // in case of disconnect exit code 1
pad.on("message", function(message){ pad.on('message', (message) => {
if(message.disconnect == 'rateLimited'){ if (message.disconnect == 'rateLimited') {
process.exit(1); process.exit(1);
} }
}) });