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

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

View file

@ -5,86 +5,86 @@
*/
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 settings = require(__dirname + '/../../../../src/node/utils/Settings');
const api = supertest('http://'+settings.ip+":"+settings.port);
const settings = require(`${__dirname}/../../../../src/node/utils/Settings`);
const api = supertest(`http://${settings.ip}:${settings.port}`);
const apiKey = common.apiKey;
var apiVersion = 1;
var testPadId = makeid();
let apiVersion = 1;
const testPadId = makeid();
describe(__filename, function() {
describe('Connectivity For Character Encoding', function() {
it('can connect', function(done) {
describe(__filename, function () {
describe('Connectivity For Character Encoding', function () {
it('can connect', function (done) {
api.get('/api/')
.expect('Content-Type', /json/)
.expect(200, done)
.expect(200, done);
});
})
});
describe('API Versioning', function() {
it('finds the version tag', function(done) {
describe('API Versioning', function () {
it('finds the version tag', function (done) {
api.get('/api/')
.expect(function(res){
.expect((res) => {
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;
})
.expect(200, done)
.expect(200, done);
});
})
});
describe('Permission', function() {
it('errors with invalid APIKey', function(done) {
describe('Permission', function () {
it('errors with invalid APIKey', function (done) {
// 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
var permErrorURL = '/api/'+apiVersion+'/createPad?apikey=password&padID=test';
const permErrorURL = `/api/${apiVersion}/createPad?apikey=password&padID=test`;
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");
describe('createPad', function () {
it('creates a new Pad', function (done) {
api.get(`${endPoint('createPad')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Unable to create new Pad');
})
.expect('Content-Type', /json/)
.expect(200, done)
.expect(200, done);
});
})
});
describe('setHTML', function() {
it('Sets the HTML of a Pad attempting to weird utf8 encoded content', function(done) {
fs.readFile('../tests/backend/specs/api/emojis.html', 'utf8', function(err, html) {
describe('setHTML', function () {
it('Sets the HTML of a Pad attempting to weird utf8 encoded content', function (done) {
fs.readFile('../tests/backend/specs/api/emojis.html', 'utf8', (err, html) => {
api.post(endPoint('setHTML'))
.send({
"padID": testPadId,
"html": html,
padID: testPadId,
html,
})
.expect(function(res){
if(res.body.code !== 0) throw new Error("Can't set HTML properly");
.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(function(res){
if (res.body.data.html.indexOf("&#127484") === -1) {
throw new Error("Unable to get the HTML");
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(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;
return '/api/'+version+'/'+point+'?apikey='+apiKey;
}
return `/api/${version}/${point}?apikey=${apiKey}`;
};
function makeid() {
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let text = '';
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));
}
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 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 apiKey = common.apiKey;
var apiVersion = 1;
var authorID = "";
var padID = makeid();
var timestamp = Date.now();
let apiVersion = 1;
let authorID = '';
const padID = makeid();
const timestamp = Date.now();
describe(__filename, function() {
describe('API Versioning', function(){
it('errors if can not connect', function(done) {
describe(__filename, function () {
describe('API Versioning', function () {
it('errors if can not connect', function (done) {
api.get('/api/')
.expect(function(res){
.expect((res) => {
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;
})
.expect(200, done)
.expect(200, done);
});
})
});
// BEGIN GROUP AND AUTHOR TESTS
/////////////////////////////////////
/////////////////////////////////////
// ///////////////////////////////////
// ///////////////////////////////////
/* Tests performed
-> createPad(padID)
@ -36,76 +36,76 @@ describe(__filename, function() {
-> getChatHistory(padID)
*/
describe('createPad', function(){
it('creates a new Pad', function(done) {
api.get(endPoint('createPad')+"&padID="+padID)
.expect(function(res){
if(res.body.code !== 0) throw new Error("Unable to create new Pad");
describe('createPad', function () {
it('creates a new Pad', function (done) {
api.get(`${endPoint('createPad')}&padID=${padID}`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Unable to create new Pad');
})
.expect('Content-Type', /json/)
.expect(200, done)
.expect(200, done);
});
})
});
describe('createAuthor', function(){
it('Creates an author with a name set', function(done) {
describe('createAuthor', function () {
it('Creates an author with a name set', function (done) {
api.get(endPoint('createAuthor'))
.expect(function(res){
if(res.body.code !== 0 || !res.body.data.authorID) throw new Error("Unable to create author");
.expect((res) => {
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
})
.expect('Content-Type', /json/)
.expect(200, done)
.expect(200, done);
});
})
});
describe('appendChatMessage', function(){
it('Adds a chat message to the pad', function(done) {
api.get(endPoint('appendChatMessage')+"&padID="+padID+"&text=blalblalbha&authorID="+authorID+"&time="+timestamp)
.expect(function(res){
if(res.body.code !== 0) throw new Error("Unable to create chat message");
describe('appendChatMessage', function () {
it('Adds a chat message to the pad', function (done) {
api.get(`${endPoint('appendChatMessage')}&padID=${padID}&text=blalblalbha&authorID=${authorID}&time=${timestamp}`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Unable to create chat message');
})
.expect('Content-Type', /json/)
.expect(200, done)
.expect(200, done);
});
})
});
describe('getChatHead', function(){
it('Gets the head of chat', function(done) {
api.get(endPoint('getChatHead')+"&padID="+padID)
.expect(function(res){
if(res.body.data.chatHead !== 0) throw new Error("Chat Head Length is wrong");
describe('getChatHead', function () {
it('Gets the head of chat', function (done) {
api.get(`${endPoint('getChatHead')}&padID=${padID}`)
.expect((res) => {
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(200, done)
.expect(200, done);
});
})
});
describe('getChatHistory', function(){
it('Gets Chat History of a Pad', function(done) {
api.get(endPoint('getChatHistory')+"&padID="+padID)
.expect(function(res){
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");
describe('getChatHistory', function () {
it('Gets Chat History of a Pad', function (done) {
api.get(`${endPoint('getChatHistory')}&padID=${padID}`)
.expect((res) => {
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');
})
.expect('Content-Type', /json/)
.expect(200, done)
.expect(200, done);
});
})
});
});
var endPoint = function(point){
return '/api/'+apiVersion+'/'+point+'?apikey='+apiKey;
}
var endPoint = function (point) {
return `/api/${apiVersion}/${point}?apikey=${apiKey}`;
};
function makeid() {
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let text = '';
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));
}
return text;

View file

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

View file

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

View file

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

View file

@ -1,21 +1,21 @@
var assert = require('assert')
os = require('os'),
fs = require('fs'),
path = require('path'),
TidyHtml = null,
Settings = null;
const assert = require('assert');
os = require('os'),
fs = require('fs'),
path = require('path'),
TidyHtml = null,
Settings = null;
var npm = require("../../../../src/node_modules/npm/lib/npm.js");
var nodeify = require('../../../../src/node_modules/nodeify');
const npm = require('../../../../src/node_modules/npm/lib/npm.js');
const nodeify = require('../../../../src/node_modules/nodeify');
describe(__filename, function() {
describe('tidyHtml', function() {
before(function(done) {
npm.load({}, function(err) {
describe(__filename, function () {
describe('tidyHtml', function () {
before(function (done) {
npm.load({}, (err) => {
assert.ok(!err);
TidyHtml = require('../../../../src/node/utils/TidyHtml');
Settings = require('../../../../src/node/utils/Settings');
return done()
return done();
});
});
@ -23,7 +23,7 @@ describe(__filename, function() {
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 (!Settings.tidyHtml) {
this.skip();
@ -32,15 +32,15 @@ describe(__filename, function() {
// Try to tidy up a bad HTML file
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>');
tidy(tmpFile, function(err){
tidy(tmpFile, (err) => {
assert.ok(!err);
// Read the file again
var cleanedHtml = fs.readFileSync(tmpFile).toString();
const cleanedHtml = fs.readFileSync(tmpFile).toString();
var expectedHtml = [
const expectedHtml = [
'<title></title>',
'</head>',
'<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 (!Settings.tidyHtml) {
this.skip();
}
tidy('/some/none/existing/file.html', function(err) {
tidy('/some/none/existing/file.html', (err) => {
assert.ok(err);
return done();
});

View file

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

View file

@ -1,24 +1,24 @@
const Changeset = require("../../../src/static/js/Changeset");
const contentcollector = require("../../../src/static/js/contentcollector");
const AttributePool = require("../../../src/static/js/AttributePool");
const cheerio = require("../../../src/node_modules/cheerio");
const util = require('util');
const Changeset = require('../../../src/static/js/Changeset');
const contentcollector = require('../../../src/static/js/contentcollector');
const AttributePool = require('../../../src/static/js/AttributePool');
const cheerio = require('../../../src/node_modules/cheerio');
const util = require('util');
const tests = {
nestedLi:{
description: "Complex nested Li",
nestedLi: {
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>',
expectedLineAttribs : [
'*0*1*2*3+1+3', '*0*4*2*5+1+3', '*0*1*2*5+1+3'
expectedLineAttribs: [
'*0*1*2*3+1+3', '*0*4*2*5+1+3', '*0*1*2*5+1+3',
],
expectedText: [
'*one', '*1.1', '*two'
]
'*one', '*1.1', '*two',
],
},
complexNest:{
description: "Complex list of different types",
complexNest: {
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>',
expectedLineAttribs : [
expectedLineAttribs: [
'*0*1*2+1+3',
'*0*1*2+1+3',
'*0*1*2+1+1',
@ -28,113 +28,116 @@ const tests = {
'*0*3*2+1+1',
'*0*4*2*5+1+4',
'*0*6*2*7+1+5',
'*0*6*2*7+1+5'
'*0*6*2*7+1+5',
],
expectedText: [
'*one', '*two',
'*0', '*1',
'*2', '*3',
'*4', '*item',
'*item1', '*item2'
]
'*one',
'*two',
'*0',
'*1',
'*2',
'*3',
'*4',
'*item',
'*item1',
'*item2',
],
},
ul: {
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>",
expectedLineAttribs : [ '*0*1*2+1+1', '*0*1*2+1+1', '+3' , '+3'],
expectedText: ["*a","*b", "div", "foo"]
}
,
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>',
expectedLineAttribs: ['*0*1*2+1+1', '*0*1*2+1+1', '+3', '+3'],
expectedText: ['*a', '*b', 'div', 'foo'],
},
ulIndented: {
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>",
expectedLineAttribs : [ '*0*1*2+1+1', '*0*3*2+1+1', '*0*1*2+1+1', '+3' ],
expectedText: ["*a","*b", "*a", "foo"]
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>',
expectedLineAttribs: ['*0*1*2+1+1', '*0*3*2+1+1', '*0*1*2+1+1', '+3'],
expectedText: ['*a', '*b', '*a', 'foo'],
},
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>",
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"],
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"
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>',
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'],
noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?',
},
bulletListInOL:{
description : "A bullet within an OL should not change numbering..",
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 : [ '*0*1*2*3+1+b', '*0*4*2*3+1+i', '*0*1*2*5+1+b', '' ],
expectedText: ["*should be 1","*should be a bullet","*should be 2", ""]
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",
},
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"
bulletListInOL: {
description: 'A bullet within an OL should not change numbering..',
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: ['*0*1*2*3+1+b', '*0*4*2*3+1+i', '*0*1*2*5+1+b', ''],
expectedText: ['*should be 1', '*should be a bullet', '*should be 2', ''],
},
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: {
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>",
expectedLineAttribs : [ '+1', '*0*1*2*3+1+1', '*0*4*2*5+1+1', '+7', '+3' ],
expectedText: ["a","*b","*c", "notlist", "foo"],
noteToSelf: "Ensure empty P does not induce line attribute marker, wont this break the editor?"
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>',
expectedLineAttribs: ['+1', '*0*1*2*3+1+1', '*0*4*2*5+1+1', '+7', '+3'],
expectedText: ['a', '*b', '*c', 'notlist', 'foo'],
noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?',
},
nestedOl: {
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>",
expectedLineAttribs : [ '+1', '*0*1*2*3+1+1', '*0*4*2*5+1+1' ],
expectedText: ["a","*b","*c"],
noteToSelf: "Ensure empty P does not induce line attribute marker, wont this break the editor?",
disabled: true
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>',
expectedLineAttribs: ['+1', '*0*1*2*3+1+1', '*0*4*2*5+1+1'],
expectedText: ['a', '*b', '*c'],
noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?',
disabled: true,
},
lineDontBreakOL:{
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>",
expectedLineAttribs : [ ],
expectedText: ["*should be 1","*should be 2","*should be 3"],
lineDontBreakOL: {
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>',
expectedLineAttribs: [],
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!",
disabled: true
}
disabled: true,
},
}
};
describe(__filename, function() {
for (let test in tests) {
let testObj = tests[test];
describe(test, function() {
describe(__filename, function () {
for (const test in tests) {
const testObj = tests[test];
describe(test, function () {
if (testObj.disabled) {
return xit("DISABLED:", test, function(done){
return xit('DISABLED:', test, function (done) {
done();
})
});
}
it(testObj.description, function(done) {
var $ = cheerio.load(testObj.html); // Load HTML into Cheerio
var doc = $('html')[0]; // Creates a dom-like representation of HTML
it(testObj.description, function (done) {
const $ = cheerio.load(testObj.html); // Load HTML into Cheerio
const doc = $('html')[0]; // Creates a dom-like representation of HTML
// 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
// using the content collector object
var cc = contentcollector.makeContentCollector(true, null, apool);
const cc = contentcollector.makeContentCollector(true, null, apool);
cc.collectContent(doc);
var result = cc.finish();
var recievedAttributes = result.lineAttribs;
var expectedAttributes = testObj.expectedLineAttribs;
var recievedText = new Array(result.lines)
var expectedText = testObj.expectedText;
const result = cc.finish();
const recievedAttributes = result.lineAttribs;
const expectedAttributes = testObj.expectedLineAttribs;
const recievedText = new Array(result.lines);
const expectedText = testObj.expectedText;
// Check recieved text matches the expected text
if (arraysEqual(recievedText[0], expectedText)) {
// console.log("PASS: Recieved Text did match Expected Text\nRecieved:", recievedText[0], "\nExpected:", testObj.expectedText)
} 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();
}
@ -143,9 +146,9 @@ describe(__filename, function() {
// console.log("PASS: Recieved Attributes matched Expected Attributes");
done();
} else {
console.error("FAIL", test, testObj.description);
console.error("FAIL: Recieved Attributes did not match Expected Attributes\nRecieved: ", recievedAttributes, "\nExpected: ", expectedAttributes)
console.error("FAILING HTML", testObj.html);
console.error('FAIL', test, testObj.description);
console.error('FAIL: Recieved Attributes did not match Expected Attributes\nRecieved: ', recievedAttributes, '\nExpected: ', expectedAttributes);
console.error('FAILING HTML', testObj.html);
throw new Error();
}
});
@ -154,8 +157,6 @@ describe(__filename, function() {
});
function arraysEqual(a, b) {
if (a === b) return true;
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.
// 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;
}
return true;

View file

@ -1,6 +1,6 @@
/* 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 common = require('../common');
@ -10,13 +10,13 @@ const sinon = require(m('node_modules/sinon'));
const logger = common.logger;
describe(__filename, function() {
describe(__filename, function () {
const hookName = 'testHook';
const hookFnName = 'testPluginFileName:testHookFunctionName';
let testHooks; // Convenience shorthand for plugins.hooks[hookName].
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
// toes:
assert(plugins.hooks[hookName] == null);
@ -30,24 +30,22 @@ describe(__filename, function() {
testHooks = plugins.hooks[hookName];
});
afterEach(async function() {
afterEach(async function () {
sinon.restore();
delete plugins.hooks[hookName];
delete hooks.deprecationNotices[hookName];
delete hooks.exportedForTestingOnly.deprecationWarned[hookFnName];
});
const makeHook = (ret) => {
return {
hook_name: hookName,
// 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
// change behavior depending on the number of parameters.
hook_fn: (hn, ctx, cb) => cb(ret),
hook_fn_name: hookFnName,
part: {plugin: 'testPluginName'},
};
};
const makeHook = (ret) => ({
hook_name: hookName,
// 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
// change behavior depending on the number of parameters.
hook_fn: (hn, ctx, cb) => cb(ret),
hook_fn_name: hookFnName,
part: {plugin: 'testPluginName'},
});
// Hook functions that should work for both synchronous and asynchronous hooks.
const supportedSyncHookFunctions = [
@ -95,30 +93,30 @@ describe(__filename, function() {
},
];
describe('callHookFnSync', function() {
describe('callHookFnSync', function () {
const callHookFnSync = hooks.exportedForTestingOnly.callHookFnSync; // Convenience shorthand.
describe('basic behavior', function() {
it('passes hook name', async function() {
describe('basic behavior', function () {
it('passes hook name', async function () {
hook.hook_fn = (hn) => { assert.equal(hn, hookName); };
callHookFnSync(hook);
});
it('passes context', async function() {
it('passes context', async function () {
for (const val of ['value', null, undefined]) {
hook.hook_fn = (hn, ctx) => { assert.equal(ctx, 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]) {
hook.hook_fn = (hn, ctx, cb) => { cb(ctx); };
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]) {
// Must not have the cb parameter otherwise returning undefined will error.
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'); };
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); };
callHookFnSync(hook);
});
it('checks for deprecation', async function() {
it('checks for deprecation', async function () {
sinon.stub(console, 'warn');
hooks.deprecationNotices[hookName] = 'test deprecation';
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) {
it(tc.name, async function() {
it(tc.name, async function () {
sinon.stub(console, 'warn');
sinon.stub(console, 'error');
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 promise2 = Promise.resolve('val2');
@ -192,7 +190,7 @@ describe(__filename, function() {
];
for (const tc of testCases) {
it(tc.name, async function() {
it(tc.name, async function () {
sinon.stub(console, 'error');
hook.hook_fn = tc.fn;
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
// time, or call the callback and then return a value.)
describe('bad hook function behavior (double settle)', function() {
beforeEach(function() {
describe('bad hook function behavior (double settle)', function () {
beforeEach(function () {
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.
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) => {
step1.fn(cb, new Error(ctx.ret1), ctx.ret1);
return step2.fn(cb, new Error(ctx.ret2), ctx.ret2);
@ -306,7 +304,7 @@ describe(__filename, function() {
// doesn't, so skip those cases.
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');
hook.hook_fn = (hn, ctx, cb) => {
step1.fn(cb, err, 'val');
@ -329,66 +327,66 @@ describe(__filename, function() {
});
});
describe('hooks.callAll', function() {
describe('basic behavior', function() {
it('calls all in order', async function() {
describe('hooks.callAll', function () {
describe('basic behavior', function () {
it('calls all in order', async function () {
testHooks.length = 0;
testHooks.push(makeHook(1), makeHook(2), makeHook(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); };
hooks.callAll(hookName);
});
it('undefined context -> {}', async function() {
it('undefined context -> {}', async function () {
hook.hook_fn = (hn, ctx) => { assert.deepEqual(ctx, {}); };
hooks.callAll(hookName);
});
it('null context -> {}', async function() {
it('null context -> {}', async function () {
hook.hook_fn = (hn, ctx) => { assert.deepEqual(ctx, {}); };
hooks.callAll(hookName, null);
});
it('context unmodified', async function() {
it('context unmodified', async function () {
const wantContext = {};
hook.hook_fn = (hn, ctx) => { assert.equal(ctx, wantContext); };
hooks.callAll(hookName, wantContext);
});
});
describe('result processing', function() {
it('no registered hooks (undefined) -> []', async function() {
describe('result processing', function () {
it('no registered hooks (undefined) -> []', async function () {
delete plugins.hooks.testHook;
assert.deepEqual(hooks.callAll(hookName), []);
});
it('no registered hooks (empty list) -> []', async function() {
it('no registered hooks (empty list) -> []', async function () {
testHooks.length = 0;
assert.deepEqual(hooks.callAll(hookName), []);
});
it('flattens one level', async function() {
it('flattens one level', async function () {
testHooks.length = 0;
testHooks.push(makeHook(1), makeHook([2]), makeHook([[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.push(makeHook(), makeHook([2]), makeHook([[3]]));
assert.deepEqual(hooks.callAll(hookName), [2, [3]]);
});
it('preserves null', async function() {
it('preserves null', async function () {
testHooks.length = 0;
testHooks.push(makeHook(null), makeHook([2]), makeHook([[3]]));
assert.deepEqual(hooks.callAll(hookName), [null, 2, [3]]);
});
it('all undefined -> []', async function() {
it('all undefined -> []', async function () {
testHooks.length = 0;
testHooks.push(makeHook(), makeHook());
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.
describe('basic behavior', function() {
it('passes hook name', async function() {
describe('basic behavior', function () {
it('passes hook name', async function () {
hook.hook_fn = (hn) => { assert.equal(hn, hookName); };
await callHookFnAsync(hook);
});
it('passes context', async function() {
it('passes context', async function () {
for (const val of ['value', null, undefined]) {
hook.hook_fn = (hn, ctx) => { assert.equal(ctx, 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]) {
hook.hook_fn = (hn, ctx, cb) => { cb(ctx); };
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]) {
// Must not have the cb parameter otherwise returning undefined will never resolve.
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'); };
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')));
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'));
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); };
await callHookFnAsync(hook);
});
it('checks for deprecation', async function() {
it('checks for deprecation', async function () {
sinon.stub(console, 'warn');
hooks.deprecationNotices[hookName] = 'test deprecation';
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([
{
name: 'legacy async cb',
@ -541,7 +539,7 @@ describe(__filename, function() {
]);
for (const tc of supportedSyncHookFunctions.concat(supportedHookFunctions)) {
it(tc.name, async function() {
it(tc.name, async function () {
sinon.stub(console, 'warn');
sinon.stub(console, 'error');
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
// time, or call the callback and then return a value.)
describe('bad hook function behavior (double settle)', function() {
beforeEach(function() {
describe('bad hook function behavior (double settle)', function () {
beforeEach(function () {
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.
if (step1.name.startsWith('return ') || step1.name === 'throw') continue;
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) => {
step1.fn(cb, new Error(ctx.ret1), ctx.ret1);
return step2.fn(cb, new Error(ctx.ret2), ctx.ret2);
@ -729,8 +727,8 @@ describe(__filename, function() {
}
}
assert.equal(console.error.callCount, 1,
'Got errors:\n' +
console.error.getCalls().map((call) => call.args[0]).join('\n'));
`Got errors:\n${
console.error.getCalls().map((call) => call.args[0]).join('\n')}`);
assert.match(console.error.getCall(0).args[0], /DOUBLE SETTLE BUG/);
assert(asyncErr instanceof Error);
assert.match(asyncErr.message, /DOUBLE SETTLE BUG/);
@ -741,7 +739,7 @@ describe(__filename, function() {
// doesn't, so skip those cases.
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');
hook.hook_fn = (hn, ctx, cb) => {
step1.fn(cb, err, 'val');
@ -764,9 +762,9 @@ describe(__filename, function() {
});
});
describe('hooks.aCallAll', function() {
describe('basic behavior', function() {
it('calls all asynchronously, returns values in order', async function() {
describe('hooks.aCallAll', function () {
describe('basic behavior', function () {
it('calls all asynchronously, returns values in order', async function () {
testHooks.length = 0; // Delete the boilerplate hook -- this test doesn't use it.
let nextIndex = 0;
const hookPromises = [];
@ -800,35 +798,35 @@ describe(__filename, function() {
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); };
await hooks.aCallAll(hookName);
});
it('undefined context -> {}', async function() {
it('undefined context -> {}', async function () {
hook.hook_fn = async (hn, ctx) => { assert.deepEqual(ctx, {}); };
await hooks.aCallAll(hookName);
});
it('null context -> {}', async function() {
it('null context -> {}', async function () {
hook.hook_fn = async (hn, ctx) => { assert.deepEqual(ctx, {}); };
await hooks.aCallAll(hookName, null);
});
it('context unmodified', async function() {
it('context unmodified', async function () {
const wantContext = {};
hook.hook_fn = async (hn, ctx) => { assert.equal(ctx, wantContext); };
await hooks.aCallAll(hookName, wantContext);
});
});
describe('aCallAll callback', function() {
it('exception in callback rejects', async function() {
describe('aCallAll callback', function () {
it('exception in callback rejects', async function () {
const p = hooks.aCallAll(hookName, {}, () => { throw new Error('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'); };
await hooks.aCallAll(hookName, {}, (err) => {
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) => {
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';
await hooks.aCallAll(hookName, {}, (err, results) => {
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');
});
});
describe('result processing', function() {
it('no registered hooks (undefined) -> []', async function() {
describe('result processing', function () {
it('no registered hooks (undefined) -> []', async function () {
delete plugins.hooks[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;
assert.deepEqual(await hooks.aCallAll(hookName), []);
});
it('flattens one level', async function() {
it('flattens one level', async function () {
testHooks.length = 0;
testHooks.push(makeHook(1), makeHook([2]), makeHook([[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.push(makeHook(), makeHook([2]), makeHook([[3]]), makeHook(Promise.resolve()));
assert.deepEqual(await hooks.aCallAll(hookName), [2, [3]]);
});
it('preserves null', async function() {
it('preserves null', async function () {
testHooks.length = 0;
testHooks.push(makeHook(null), makeHook([2]), makeHook(Promise.resolve(null)));
assert.deepEqual(await hooks.aCallAll(hookName), [null, 2, null]);
});
it('all undefined -> []', async function() {
it('all undefined -> []', async function () {
testHooks.length = 0;
testHooks.push(makeHook(), makeHook(Promise.resolve()));
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 promises = require(m('node/utils/promises'));
describe(__filename, function() {
describe('promises.timesLimit', function() {
describe(__filename, function () {
describe('promises.timesLimit', function () {
let wantIndex = 0;
const testPromises = [];
const makePromise = (index) => {
@ -25,18 +25,18 @@ describe(__filename, function() {
const concurrency = 7;
const timesLimitPromise = promises.timesLimit(total, concurrency, makePromise);
it('honors concurrency', async function() {
it('honors concurrency', async function () {
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();
resolve();
await promise;
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) {
// Resolve them in random order to ensure that the resolution order doesn't matter.
const i = Math.floor(Math.random() * Math.floor(testPromises.length));
@ -47,11 +47,11 @@ describe(__filename, function() {
assert.equal(wantIndex, total);
});
it('resolves', async function() {
it('resolves', async function () {
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;
assert.equal(testPromises.length, 0);
const total = 7;
@ -66,21 +66,21 @@ describe(__filename, function() {
assert.equal(wantIndex, total);
});
it('accepts total === 0, concurrency > 0', async function() {
it('accepts total === 0, concurrency > 0', async function () {
wantIndex = 0;
assert.equal(testPromises.length, 0);
await promises.timesLimit(0, concurrency, makePromise);
assert.equal(wantIndex, 0);
});
it('accepts total === 0, concurrency === 0', async function() {
it('accepts total === 0, concurrency === 0', async function () {
wantIndex = 0;
assert.equal(testPromises.length, 0);
await promises.timesLimit(0, 0, makePromise);
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);
});
});

View file

@ -1,6 +1,6 @@
/* 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 common = require('../common');
@ -50,9 +50,7 @@ const getSocketEvent = async (socket, event) => {
const connect = async (res) => {
// Convert the `set-cookie` header(s) into a `cookie` header.
const resCookies = (res == null) ? {} : setCookieParser.parse(res, {map: true});
const reqCookieHdr = Object.entries(resCookies).map(([name, cookie]) => {
return `${name}=${encodeURIComponent(cookie.value)}`;
}).join('; ');
const reqCookieHdr = Object.entries(resCookies).map(([name, cookie]) => `${name}=${encodeURIComponent(cookie.value)}`).join('; ');
logger.debug('socket.io connecting...');
const socket = io(`${common.baseUrl}/`, {
@ -91,7 +89,7 @@ const handshake = async (socket, padID) => {
return msg;
};
describe(__filename, function() {
describe(__filename, function () {
let agent;
let authorize;
const backups = {};
@ -106,8 +104,8 @@ describe(__filename, function() {
};
let socket;
before(async function() { agent = await common.init(); });
beforeEach(async function() {
before(async function () { agent = await common.init(); });
beforeEach(async function () {
backups.hooks = {};
for (const hookName of ['preAuthorize', 'authenticate', 'authorize']) {
backups.hooks[hookName] = plugins.hooks[hookName];
@ -126,12 +124,10 @@ describe(__filename, function() {
};
assert(socket == null);
authorize = () => true;
plugins.hooks.authorize = [{hook_fn: (hookName, {req}, cb) => {
return cb([authorize(req)]);
}}];
plugins.hooks.authorize = [{hook_fn: (hookName, {req}, cb) => cb([authorize(req)])}];
await cleanUpPads();
});
afterEach(async function() {
afterEach(async function () {
if (socket) socket.close();
socket = null;
await cleanUpPads();
@ -139,32 +135,32 @@ describe(__filename, function() {
Object.assign(settings, backups.settings);
});
describe('Normal accesses', function() {
it('!authn anonymous cookie /p/pad -> 200, ok', async function() {
describe('Normal accesses', function () {
it('!authn anonymous cookie /p/pad -> 200, ok', async function () {
const res = await agent.get('/p/pad').expect(200);
socket = await connect(res);
const clientVars = await handshake(socket, 'pad');
assert.equal(clientVars.type, 'CLIENT_VARS');
});
it('!authn !cookie -> ok', async function() {
it('!authn !cookie -> ok', async function () {
socket = await connect(null);
const clientVars = await handshake(socket, 'pad');
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);
socket = await connect(res);
const clientVars = await handshake(socket, 'pad');
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;
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
socket = await connect(res);
const clientVars = await handshake(socket, 'pad');
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.requireAuthorization = true;
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');
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;
// 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
@ -187,8 +183,8 @@ describe(__filename, function() {
});
});
describe('Abnormal access attempts', function() {
it('authn anonymous /p/pad -> 401, error', async function() {
describe('Abnormal access attempts', function () {
it('authn anonymous /p/pad -> 401, error', async function () {
settings.requireAuthentication = true;
const res = await agent.get('/p/pad').expect(401);
// 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');
assert.equal(message.accessStatus, 'deny');
});
it('authn !cookie -> error', async function() {
it('authn !cookie -> error', async function () {
settings.requireAuthentication = true;
socket = await connect(null);
const message = await handshake(socket, 'pad');
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.
authorize = (req) => req.path === '/p/pad';
settings.requireAuthentication = true;
@ -216,13 +212,13 @@ describe(__filename, function() {
});
});
describe('Authorization levels via authorize hook', function() {
beforeEach(async function() {
describe('Authorization levels via authorize hook', function () {
beforeEach(async function () {
settings.requireAuthentication = true;
settings.requireAuthorization = true;
});
it("level='create' -> can create", async function() {
it("level='create' -> can create", async function () {
authorize = () => 'create';
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
socket = await connect(res);
@ -230,7 +226,7 @@ describe(__filename, function() {
assert.equal(clientVars.type, 'CLIENT_VARS');
assert.equal(clientVars.data.readonly, false);
});
it('level=true -> can create', async function() {
it('level=true -> can create', async function () {
authorize = () => true;
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
socket = await connect(res);
@ -238,7 +234,7 @@ describe(__filename, function() {
assert.equal(clientVars.type, 'CLIENT_VARS');
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.
authorize = () => 'modify';
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.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';
settings.editOnly = true;
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');
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';
settings.editOnly = false;
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');
assert.equal(message.accessStatus, 'deny');
});
it("level='readOnly' -> unable to create", async function() {
it("level='readOnly' -> unable to create", async function () {
authorize = () => 'readOnly';
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
socket = await connect(res);
const message = await handshake(socket, 'pad');
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.
authorize = () => 'readOnly';
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() {
beforeEach(async function() {
describe('Authorization levels via user settings', function () {
beforeEach(async function () {
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;
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
socket = await connect(res);
@ -294,21 +290,21 @@ describe(__filename, function() {
assert.equal(clientVars.type, 'CLIENT_VARS');
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;
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
socket = await connect(res);
const message = await handshake(socket, 'pad');
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;
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
socket = await connect(res);
const message = await handshake(socket, 'pad');
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.
settings.users.user.readOnly = true;
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.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;
const res = await agent.get('/p/pad').auth('user', 'user-password').expect(200);
socket = await connect(res);
@ -325,7 +321,7 @@ describe(__filename, function() {
assert.equal(clientVars.type, 'CLIENT_VARS');
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.readOnly = true;
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() {
beforeEach(async function() {
describe('Authorization level interaction between authorize hook and user settings', function () {
beforeEach(async function () {
settings.requireAuthentication = 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;
authorize = () => 'create';
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');
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.canCreate = true;
authorize = () => 'readOnly';

View file

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

View file

@ -1,19 +1,19 @@
/* 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 common = require('../common');
const plugins = require(m('static/js/pluginfw/plugin_defs'));
const settings = require(m('node/utils/Settings'));
describe(__filename, function() {
describe(__filename, function () {
let agent;
const backups = {};
const authHookNames = ['preAuthorize', 'authenticate', 'authorize'];
const failHookNames = ['preAuthzFailure', 'authnFailure', 'authzFailure', 'authFailure'];
before(async function() { agent = await common.init(); });
beforeEach(async function() {
before(async function () { agent = await common.init(); });
beforeEach(async function () {
backups.hooks = {};
for (const hookName of authHookNames.concat(failHookNames)) {
backups.hooks[hookName] = plugins.hooks[hookName];
@ -30,76 +30,76 @@ describe(__filename, function() {
user: {password: 'user-password'},
};
});
afterEach(async function() {
afterEach(async function () {
Object.assign(plugins.hooks, backups.hooks);
Object.assign(settings, backups.settings);
});
describe('webaccess: without plugins', function() {
it('!authn !authz anonymous / -> 200', async function() {
describe('webaccess: without plugins', function () {
it('!authn !authz anonymous / -> 200', async function () {
settings.requireAuthentication = false;
settings.requireAuthorization = false;
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.requireAuthorization = false;
await agent.get('/admin/').expect(401);
});
it('authn !authz anonymous / -> 401', async function() {
it('authn !authz anonymous / -> 401', async function () {
settings.requireAuthentication = true;
settings.requireAuthorization = false;
await agent.get('/').expect(401);
});
it('authn !authz user / -> 200', async function() {
it('authn !authz user / -> 200', async function () {
settings.requireAuthentication = true;
settings.requireAuthorization = false;
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.requireAuthorization = false;
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.requireAuthorization = false;
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.requireAuthorization = false;
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.requireAuthorization = true;
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.requireAuthorization = true;
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.requireAuthorization = true;
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.requireAuthorization = true;
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]) {
// 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
// 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.
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;
const encCreds = Buffer.from(creds).toString('base64');
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;
const Handler = class {
constructor(hookName, suffix) {
@ -134,7 +134,7 @@ describe(__filename, function() {
};
const handlers = {};
beforeEach(async function() {
beforeEach(async function () {
callOrder = [];
for (const hookName of authHookNames) {
// Create two handlers for each hook to test deferral to the next function.
@ -145,38 +145,38 @@ describe(__filename, function() {
}
});
describe('preAuthorize', function() {
beforeEach(async function() {
describe('preAuthorize', function () {
beforeEach(async function () {
settings.requireAuthentication = false;
settings.requireAuthorization = false;
});
it('defers if it returns []', async function() {
it('defers if it returns []', async function () {
await agent.get('/').expect(200);
// Note: The preAuthorize hook always runs even if requireAuthorization is false.
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.requireAuthorization = true;
handlers.preAuthorize[0].innerHandle = () => [true];
await agent.get('/').expect(200);
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.requireAuthorization = true;
handlers.preAuthorize[0].innerHandle = () => [false];
await agent.get('/').expect(403);
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.requireAuthorization = true;
await agent.get('/static/robots.txt').expect(200);
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];
await agent.get('/admin/').expect(401);
// Notes:
@ -184,15 +184,17 @@ describe(__filename, function() {
// 'true' entries are ignored for /admin/* requests.
// * The authenticate hook always runs for /admin/* requests even if
// settings.requireAuthentication is false.
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1',
'authenticate_0', 'authenticate_1']);
assert.deepEqual(callOrder, ['preAuthorize_0',
'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];
await agent.get('/admin/').auth('admin', 'admin-password').expect(403);
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];
let called = false;
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');
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'); };
await agent.get('/').expect(500);
});
});
describe('authenticate', function() {
beforeEach(async function() {
describe('authenticate', function () {
beforeEach(async function () {
settings.requireAuthentication = true;
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;
await agent.get('/').expect(200);
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;
await agent.get('/admin/').expect(401);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1',
'authenticate_0', 'authenticate_1']);
assert.deepEqual(callOrder, ['preAuthorize_0',
'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);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1',
'authenticate_0', 'authenticate_1']);
assert.deepEqual(callOrder, ['preAuthorize_0',
'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]; };
await agent.get('/').expect(200);
// Note: authenticate_1 was not called because authenticate_0 handled it.
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];
await agent.get('/').expect(401);
// Note: authenticate_1 was not called because authenticate_0 handled it.
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);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1',
'authenticate_0', 'authenticate_1']);
assert.deepEqual(callOrder, ['preAuthorize_0',
'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}) => {
assert.equal(users, settings.users);
};
await agent.get('/').expect(401);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1',
'authenticate_0', 'authenticate_1']);
assert.deepEqual(callOrder, ['preAuthorize_0',
'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}) => {
assert.equal(username, 'user');
assert.equal(password, 'user-password');
};
await agent.get('/').auth('user', 'user-password').expect(200);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1',
'authenticate_0', 'authenticate_1']);
assert.deepEqual(callOrder, ['preAuthorize_0',
'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}) => {
assert(username == null);
assert(password == null);
};
await agent.get('/').expect(401);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1',
'authenticate_0', 'authenticate_1']);
assert.deepEqual(callOrder, ['preAuthorize_0',
'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];
await agent.get('/').expect(500);
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'); };
await agent.get('/').expect(500);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1', 'authenticate_0']);
});
});
describe('authorize', function() {
beforeEach(async function() {
describe('authorize', function () {
beforeEach(async function () {
settings.requireAuthentication = 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;
await agent.get('/').auth('user', 'user-password').expect(200);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1',
'authenticate_0', 'authenticate_1']);
assert.deepEqual(callOrder, ['preAuthorize_0',
'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;
await agent.get('/admin/').auth('admin', 'admin-password').expect(200);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1',
'authenticate_0', 'authenticate_1']);
assert.deepEqual(callOrder, ['preAuthorize_0',
'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);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1',
'authenticate_0', 'authenticate_1',
'authorize_0', 'authorize_1']);
assert.deepEqual(callOrder, ['preAuthorize_0',
'preAuthorize_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];
await agent.get('/').auth('user', 'user-password').expect(200);
// Note: authorize_1 was not called because authorize_0 handled it.
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1',
'authenticate_0', 'authenticate_1',
'authorize_0']);
assert.deepEqual(callOrder, ['preAuthorize_0',
'preAuthorize_1',
'authenticate_0',
'authenticate_1',
'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];
await agent.get('/').auth('user', 'user-password').expect(403);
// Note: authorize_1 was not called because authorize_0 handled it.
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1',
'authenticate_0', 'authenticate_1',
'authorize_0']);
assert.deepEqual(callOrder, ['preAuthorize_0',
'preAuthorize_1',
'authenticate_0',
'authenticate_1',
'authorize_0']);
});
it('passes req.path in context', async function() {
it('passes req.path in context', async function () {
handlers.authorize[0].checkContext = ({resource}) => {
assert.equal(resource, '/');
};
await agent.get('/').auth('user', 'user-password').expect(403);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1',
'authenticate_0', 'authenticate_1',
'authorize_0', 'authorize_1']);
assert.deepEqual(callOrder, ['preAuthorize_0',
'preAuthorize_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'); };
await agent.get('/').auth('user', 'user-password').expect(500);
assert.deepEqual(callOrder, ['preAuthorize_0', 'preAuthorize_1',
'authenticate_0', 'authenticate_1',
'authorize_0']);
assert.deepEqual(callOrder, ['preAuthorize_0',
'preAuthorize_1',
'authenticate_0',
'authenticate_1',
'authorize_0']);
});
});
});
describe('webaccess: authnFailure, authzFailure, authFailure hooks', function() {
describe('webaccess: authnFailure, authzFailure, authFailure hooks', function () {
const Handler = class {
constructor(hookName) {
this.hookName = hookName;
@ -372,7 +402,7 @@ describe(__filename, function() {
};
const handlers = {};
beforeEach(function() {
beforeEach(function () {
failHookNames.forEach((hookName) => {
const handler = new Handler(hookName);
handlers[hookName] = handler;
@ -383,61 +413,61 @@ describe(__filename, function() {
});
// 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);
assert(handlers['authnFailure'].called);
assert(!handlers['authzFailure'].called);
assert(handlers['authFailure'].called);
assert(handlers.authnFailure.called);
assert(!handlers.authzFailure.called);
assert(handlers.authFailure.called);
});
it('authn fail, authnFailure handles', async function() {
handlers['authnFailure'].shouldHandle = true;
it('authn fail, authnFailure handles', async function () {
handlers.authnFailure.shouldHandle = true;
await agent.get('/').expect(200, 'authnFailure');
assert(handlers['authnFailure'].called);
assert(!handlers['authzFailure'].called);
assert(!handlers['authFailure'].called);
assert(handlers.authnFailure.called);
assert(!handlers.authzFailure.called);
assert(!handlers.authFailure.called);
});
it('authn fail, authFailure handles', async function() {
handlers['authFailure'].shouldHandle = true;
it('authn fail, authFailure handles', async function () {
handlers.authFailure.shouldHandle = true;
await agent.get('/').expect(200, 'authFailure');
assert(handlers['authnFailure'].called);
assert(!handlers['authzFailure'].called);
assert(handlers['authFailure'].called);
assert(handlers.authnFailure.called);
assert(!handlers.authzFailure.called);
assert(handlers.authFailure.called);
});
it('authnFailure trumps authFailure', async function() {
handlers['authnFailure'].shouldHandle = true;
handlers['authFailure'].shouldHandle = true;
it('authnFailure trumps authFailure', async function () {
handlers.authnFailure.shouldHandle = true;
handlers.authFailure.shouldHandle = true;
await agent.get('/').expect(200, 'authnFailure');
assert(handlers['authnFailure'].called);
assert(!handlers['authFailure'].called);
assert(handlers.authnFailure.called);
assert(!handlers.authFailure.called);
});
// 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);
assert(!handlers['authnFailure'].called);
assert(handlers['authzFailure'].called);
assert(handlers['authFailure'].called);
assert(!handlers.authnFailure.called);
assert(handlers.authzFailure.called);
assert(handlers.authFailure.called);
});
it('authz fail, authzFailure handles', async function() {
handlers['authzFailure'].shouldHandle = true;
it('authz fail, authzFailure handles', async function () {
handlers.authzFailure.shouldHandle = true;
await agent.get('/').auth('user', 'user-password').expect(200, 'authzFailure');
assert(!handlers['authnFailure'].called);
assert(handlers['authzFailure'].called);
assert(!handlers['authFailure'].called);
assert(!handlers.authnFailure.called);
assert(handlers.authzFailure.called);
assert(!handlers.authFailure.called);
});
it('authz fail, authFailure handles', async function() {
handlers['authFailure'].shouldHandle = true;
it('authz fail, authFailure handles', async function () {
handlers.authFailure.shouldHandle = true;
await agent.get('/').auth('user', 'user-password').expect(200, 'authFailure');
assert(!handlers['authnFailure'].called);
assert(handlers['authzFailure'].called);
assert(handlers['authFailure'].called);
assert(!handlers.authnFailure.called);
assert(handlers.authzFailure.called);
assert(handlers.authFailure.called);
});
it('authzFailure trumps authFailure', async function() {
handlers['authzFailure'].shouldHandle = true;
handlers['authFailure'].shouldHandle = true;
it('authzFailure trumps authFailure', async function () {
handlers.authzFailure.shouldHandle = true;
handlers.authFailure.shouldHandle = true;
await agent.get('/').auth('user', 'user-password').expect(200, 'authzFailure');
assert(handlers['authzFailure'].called);
assert(!handlers['authFailure'].called);
assert(handlers.authzFailure.called);
assert(!handlers.authFailure.called);
});
});
});