mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-04-28 11:26:16 -04:00
add tests from origin/develop
This commit is contained in:
parent
6a3e4c69b8
commit
2df2d7d60a
71 changed files with 18931 additions and 4032 deletions
79
tests/backend/specs/api/api.js
Normal file
79
tests/backend/specs/api/api.js
Normal file
|
@ -0,0 +1,79 @@
|
|||
/**
|
||||
* API specs
|
||||
*
|
||||
* Tests for generic overarching HTTP API related features not related to any
|
||||
* specific part of the data model or domain. For example: tests for versioning
|
||||
* and openapi definitions.
|
||||
*/
|
||||
|
||||
const assert = require('assert');
|
||||
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 path = require('path');
|
||||
|
||||
var validateOpenAPI = require(__dirname + '/../../../../src/node_modules/openapi-schema-validation').validate;
|
||||
|
||||
var filePath = path.join(__dirname, '../../../../APIKEY.txt');
|
||||
|
||||
var apiKey = fs.readFileSync(filePath, { encoding: 'utf-8' });
|
||||
apiKey = apiKey.replace(/\n$/, '');
|
||||
var apiVersion = 1;
|
||||
|
||||
var testPadId = makeid();
|
||||
|
||||
describe('API Versioning', function() {
|
||||
it('errors if can not connect', function(done) {
|
||||
api
|
||||
.get('/api/')
|
||||
.expect(function(res) {
|
||||
apiVersion = res.body.currentVersion;
|
||||
if (!res.body.currentVersion) throw new Error('No version set in API');
|
||||
return;
|
||||
})
|
||||
.expect(200, 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);
|
||||
if (!valid) {
|
||||
const prettyErrors = JSON.stringify(errors, null, 2);
|
||||
throw new Error(`Document is not valid OpenAPI. ${errors.length} validation errors:\n${prettyErrors}`);
|
||||
}
|
||||
return;
|
||||
})
|
||||
.expect(200, done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('jsonp support', function() {
|
||||
it('supports jsonp calls', function(done) {
|
||||
api
|
||||
.get(endPoint('createPad') + '&jsonp=jsonp_1&padID=' + testPadId)
|
||||
.expect(function(res) {
|
||||
if (!res.text.match('jsonp_1')) throw new Error('no jsonp call seen');
|
||||
})
|
||||
.expect('Content-Type', /javascript/)
|
||||
.expect(200, done);
|
||||
});
|
||||
});
|
||||
|
||||
var endPoint = function(point) {
|
||||
return '/api/' + apiVersion + '/' + point + '?apikey=' + apiKey;
|
||||
};
|
||||
|
||||
function makeid() {
|
||||
var text = '';
|
||||
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
|
||||
for (var i = 0; i < 5; i++) {
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
return text;
|
||||
}
|
113
tests/backend/specs/api/characterEncoding.js
Normal file
113
tests/backend/specs/api/characterEncoding.js
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* This file is copied & modified from <basedir>/tests/backend/specs/api/pad.js
|
||||
*
|
||||
* TODO: maybe unify those two files and merge in a single one.
|
||||
*/
|
||||
|
||||
const assert = require('assert');
|
||||
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 path = require('path');
|
||||
const async = require(__dirname+'/../../../../src/node_modules/async');
|
||||
|
||||
var filePath = path.join(__dirname, '../../../../APIKEY.txt');
|
||||
|
||||
var apiKey = fs.readFileSync(filePath, {encoding: 'utf-8'});
|
||||
apiKey = apiKey.replace(/\n$/, "");
|
||||
var apiVersion = 1;
|
||||
var testPadId = makeid();
|
||||
|
||||
describe('Connectivity For Character Encoding', function(){
|
||||
it('can connect', function(done) {
|
||||
api.get('/api/')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200, done)
|
||||
});
|
||||
})
|
||||
|
||||
describe('API Versioning', function(){
|
||||
it('finds the version tag', function(done) {
|
||||
api.get('/api/')
|
||||
.expect(function(res){
|
||||
apiVersion = res.body.currentVersion;
|
||||
if (!res.body.currentVersion) throw new Error("No version set in API");
|
||||
return;
|
||||
})
|
||||
.expect(200, 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';
|
||||
api.get(permErrorURL)
|
||||
.expect(401, done)
|
||||
});
|
||||
})
|
||||
|
||||
describe('createPad', function(){
|
||||
it('creates a new Pad', function(done) {
|
||||
api.get(endPoint('createPad')+"&padID="+testPadId)
|
||||
.expect(function(res){
|
||||
if(res.body.code !== 0) throw new Error("Unable to create new Pad");
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200, done)
|
||||
});
|
||||
})
|
||||
|
||||
describe('setHTML', function(){
|
||||
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) {
|
||||
api.post(endPoint('setHTML'))
|
||||
.send({
|
||||
"padID": testPadId,
|
||||
"html": html,
|
||||
})
|
||||
.expect(function(res){
|
||||
if(res.body.code !== 0) throw new Error("Can't set HTML properly");
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(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("🇼") === -1) {
|
||||
throw new Error("Unable to get the HTML");
|
||||
}
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200, done)
|
||||
});
|
||||
})
|
||||
|
||||
/*
|
||||
|
||||
End of test
|
||||
|
||||
*/
|
||||
|
||||
var endPoint = function(point, version){
|
||||
version = version || apiVersion;
|
||||
return '/api/'+version+'/'+point+'?apikey='+apiKey;
|
||||
}
|
||||
|
||||
function makeid()
|
||||
{
|
||||
var text = "";
|
||||
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
for( var i=0; i < 10; i++ ){
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
return text;
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
var assert = require('assert')
|
||||
supertest = require(__dirname+'/../../../../src/node_modules/supertest'),
|
||||
fs = require('fs'),
|
||||
settings = require(__dirname+'/../../loadSettings').loadSettings(),
|
||||
settings = require(__dirname + '/../../../../src/node/utils/Settings'),
|
||||
api = supertest('http://'+settings.ip+":"+settings.port),
|
||||
path = require('path');
|
||||
|
||||
|
|
10
tests/backend/specs/api/emojis.html
Normal file
10
tests/backend/specs/api/emojis.html
Normal file
File diff suppressed because one or more lines are too long
75
tests/backend/specs/api/fuzzImportTest.js
Normal file
75
tests/backend/specs/api/fuzzImportTest.js
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Fuzz testing the import endpoint
|
||||
*/
|
||||
/*
|
||||
const fs = require('fs');
|
||||
const settings = require(__dirname+'/../../../../tests/container/loadSettings.js').loadSettings();
|
||||
const host = "http://" + settings.ip + ":" + settings.port;
|
||||
const path = require('path');
|
||||
const async = require(__dirname+'/../../../../src/node_modules/async');
|
||||
const request = require(__dirname+'/../../../../src/node_modules/request');
|
||||
const froth = require(__dirname+'/../../../../src/node_modules/mocha-froth');
|
||||
|
||||
var filePath = path.join(__dirname, '../../../../APIKEY.txt');
|
||||
var apiKey = fs.readFileSync(filePath, {encoding: 'utf-8'});
|
||||
apiKey = apiKey.replace(/\n$/, "");
|
||||
var apiVersion = 1;
|
||||
var testPadId = "TEST_fuzz" + makeid();
|
||||
|
||||
var endPoint = function(point, version){
|
||||
version = version || apiVersion;
|
||||
return '/api/'+version+'/'+point+'?apikey='+apiKey;
|
||||
}
|
||||
|
||||
//console.log("Testing against padID", testPadId);
|
||||
//console.log("To watch the test live visit " + host + "/p/" + testPadId);
|
||||
//console.log("Tests will start in 5 seconds, click the URL now!");
|
||||
|
||||
setTimeout(function(){
|
||||
for (let i=1; i<5; i++) { // 5000 runs
|
||||
setTimeout( function timer(){
|
||||
runTest(i);
|
||||
}, i*100 ); // 100 ms
|
||||
}
|
||||
process.exit(0);
|
||||
},5000); // wait 5 seconds
|
||||
|
||||
function runTest(number){
|
||||
request(host + endPoint('createPad') + '&padID=' + testPadId, function(err, res, body){
|
||||
var req = request.post(host + '/p/'+testPadId+'/import', function (err, res, body) {
|
||||
if (err) {
|
||||
throw new Error("FAILURE", err);
|
||||
}else{
|
||||
console.log("Success");
|
||||
}
|
||||
});
|
||||
|
||||
var fN = '/tmp/fuzztest.txt';
|
||||
var cT = 'text/plain';
|
||||
|
||||
if (number % 2 == 0) {
|
||||
fN = froth().toString();
|
||||
cT = froth().toString();
|
||||
}
|
||||
|
||||
let form = req.form();
|
||||
|
||||
form.append('file', froth().toString(), {
|
||||
filename: fN,
|
||||
contentType: cT
|
||||
});
|
||||
console.log("here");
|
||||
});
|
||||
}
|
||||
|
||||
function makeid()
|
||||
{
|
||||
var text = "";
|
||||
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
for( var i=0; i < 5; i++ ){
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
*/
|
BIN
tests/backend/specs/api/image.png
Normal file
BIN
tests/backend/specs/api/image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 355 KiB |
188
tests/backend/specs/api/importexport.js
Normal file
188
tests/backend/specs/api/importexport.js
Normal file
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* ACHTUNG: there is a copied & modified version of this file in
|
||||
* <basedir>/tests/container/spacs/api/pad.js
|
||||
*
|
||||
* TODO: unify those two files, and merge in a single one.
|
||||
*/
|
||||
|
||||
const assert = require('assert');
|
||||
const supertest = require(__dirname+'/../../../../src/node_modules/supertest');
|
||||
const fs = require('fs');
|
||||
const settings = require(__dirname+'/../../../../tests/container/loadSettings.js').loadSettings();
|
||||
const api = supertest('http://'+settings.ip+":"+settings.port);
|
||||
const path = require('path');
|
||||
const async = require(__dirname+'/../../../../src/node_modules/async');
|
||||
|
||||
var filePath = path.join(__dirname, '../../../../APIKEY.txt');
|
||||
|
||||
var apiKey = fs.readFileSync(filePath, {encoding: 'utf-8'});
|
||||
apiKey = apiKey.replace(/\n$/, "");
|
||||
var apiVersion = 1;
|
||||
var lastEdited = "";
|
||||
|
||||
var testImports = {
|
||||
"malformed": {
|
||||
input: '<html><body><li>wtf</ul></body></html>',
|
||||
expectedHTML: '<!DOCTYPE HTML><html><body>wtf<br><br></body></html>',
|
||||
expectedText: 'wtf\n\n'
|
||||
},
|
||||
"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'
|
||||
},
|
||||
"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'
|
||||
},
|
||||
"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'
|
||||
},
|
||||
"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'
|
||||
},
|
||||
|
||||
/*
|
||||
"prefixcorrectlinenumber when introduced none list item - currently not supported see #3450":{
|
||||
input: '<html><body><ol><li>should be 1</li>test<li>should be 2</li></ol></body></html>',
|
||||
expectedHTML: '<!DOCTYPE HTML><html><body><ol start="1" class="number"><li>should be 1</li>test<li>should be 2</li></ol><br></body></html>',
|
||||
expectedText: '\t1. should be 1\n\ttest\n\t2. should be 2\n\n'
|
||||
}
|
||||
,
|
||||
"newlinesshouldntresetlinenumber #2194":{
|
||||
input: '<html><body><ol><li>should be 1</li>test<li>should be 2</li></ol></body></html>',
|
||||
expectedHTML: '<!DOCTYPE HTML><html><body><ol class="number"><li>should be 1</li>test<li>should be 2</li></ol><br></body></html>',
|
||||
expectedText: '\t1. should be 1\n\ttest\n\t2. should be 2\n\n'
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
Object.keys(testImports).forEach(function (testName) {
|
||||
var 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");
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.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)
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.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;
|
||||
if (receivedHtml !== test.expectedHTML) {
|
||||
throw new Error(`HTML received from export is not the one we were expecting.
|
||||
Test Name:
|
||||
${testName}
|
||||
|
||||
Received:
|
||||
${receivedHtml}
|
||||
|
||||
Expected:
|
||||
${test.expectedHTML}
|
||||
|
||||
Which is a different version of the originally imported one:
|
||||
${test.input}`);
|
||||
}
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.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;
|
||||
if (receivedText !== test.expectedText) {
|
||||
throw new Error(`Text received from export is not the one we were expecting.
|
||||
Test Name:
|
||||
${testName}
|
||||
|
||||
Received:
|
||||
${receivedText}
|
||||
|
||||
Expected:
|
||||
${test.expectedText}
|
||||
|
||||
Which is a different version of the originally imported one:
|
||||
${test.input}`);
|
||||
}
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200, done)
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
var endPoint = function(point, version){
|
||||
version = version || apiVersion;
|
||||
return '/api/'+version+'/'+point+'?apikey='+apiKey;
|
||||
}
|
||||
|
||||
function makeid()
|
||||
{
|
||||
var text = "";
|
||||
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
for( var i=0; i < 5; i++ ){
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
function generateLongText(){
|
||||
var text = "";
|
||||
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
for( var i=0; i < 80000; i++ ){
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
410
tests/backend/specs/api/importexportGetPost.js
Normal file
410
tests/backend/specs/api/importexportGetPost.js
Normal file
|
@ -0,0 +1,410 @@
|
|||
/*
|
||||
* Import and Export tests for the /p/whateverPadId/import and /p/whateverPadId/export endpoints.
|
||||
*/
|
||||
|
||||
const assert = require('assert');
|
||||
const supertest = require(__dirname+'/../../../../src/node_modules/supertest');
|
||||
const fs = require('fs');
|
||||
const settings = require(__dirname+'/../../../../src/node/utils/Settings');
|
||||
const host = 'http://127.0.0.1:'+settings.port;
|
||||
const api = supertest('http://'+settings.ip+":"+settings.port);
|
||||
const path = require('path');
|
||||
const async = require(__dirname+'/../../../../src/node_modules/async');
|
||||
const request = require(__dirname+'/../../../../src/node_modules/request');
|
||||
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");
|
||||
var filePath = path.join(__dirname, '../../../../APIKEY.txt');
|
||||
|
||||
var apiKey = fs.readFileSync(filePath, {encoding: 'utf-8'});
|
||||
apiKey = apiKey.replace(/\n$/, "");
|
||||
var apiVersion = 1;
|
||||
var testPadId = makeid();
|
||||
var lastEdited = "";
|
||||
var text = generateLongText();
|
||||
|
||||
describe('Connectivity', function(){
|
||||
it('can connect', function(done) {
|
||||
api.get('/api/')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200, done)
|
||||
});
|
||||
})
|
||||
|
||||
describe('API Versioning', function(){
|
||||
it('finds the version tag', function(done) {
|
||||
api.get('/api/')
|
||||
.expect(function(res){
|
||||
apiVersion = res.body.currentVersion;
|
||||
if (!res.body.currentVersion) throw new Error("No version set in API");
|
||||
return;
|
||||
})
|
||||
.expect(200, done)
|
||||
});
|
||||
})
|
||||
|
||||
/*
|
||||
Tests
|
||||
-----
|
||||
|
||||
Test.
|
||||
/ Create a pad
|
||||
/ Set pad contents
|
||||
/ Try export pad in various formats
|
||||
/ Get pad contents and ensure it matches imported contents
|
||||
|
||||
Test.
|
||||
/ Try to export a pad that doesn't exist // Expect failure
|
||||
|
||||
Test.
|
||||
/ Try to import an unsupported file to a pad that exists
|
||||
|
||||
-- TODO: Test.
|
||||
Try to import to a file and abort it half way through
|
||||
|
||||
Test.
|
||||
Try to import to files of varying size.
|
||||
|
||||
Example Curl command for testing import URI:
|
||||
curl -s -v --form file=@/home/jose/test.txt http://127.0.0.1:9001/p/foo/import
|
||||
*/
|
||||
|
||||
describe('Imports and Exports', function(){
|
||||
it('creates a new Pad, imports content to it, checks that content', function(done) {
|
||||
if(!settings.allowAnyoneToImport){
|
||||
console.warn("not anyone can import so not testing -- to include this test set allowAnyoneToImport to true in settings.json");
|
||||
done();
|
||||
}else{
|
||||
api.get(endPoint('createPad')+"&padID="+testPadId)
|
||||
.expect(function(res){
|
||||
if(res.body.code !== 0) throw new Error("Unable to create new Pad");
|
||||
|
||||
var req = request.post(host + '/p/'+testPadId+'/import', function (err, res, body) {
|
||||
if (err) {
|
||||
throw new Error("Failed to import", err);
|
||||
} else {
|
||||
api.get(endPoint('getText')+"&padID="+testPadId)
|
||||
.expect(function(res){
|
||||
if(res.body.data.text !== padText.toString()){
|
||||
throw new Error("text is wrong on export");
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
let form = req.form();
|
||||
|
||||
form.append('file', padText, {
|
||||
filename: '/test.txt',
|
||||
contentType: 'text/plain'
|
||||
});
|
||||
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200, done)
|
||||
}
|
||||
});
|
||||
|
||||
// For some reason word import does not work in testing..
|
||||
// TODO: fix support for .doc files..
|
||||
xit('Tries to import .doc that uses soffice or abiword', function(done) {
|
||||
if(!settings.allowAnyoneToImport) return done();
|
||||
if((settings.abiword && settings.abiword.indexOf("/" === -1)) && (settings.office && settings.soffice.indexOf("/" === -1))) return done();
|
||||
|
||||
var req = request.post(host + '/p/'+testPadId+'/import', function (err, res, body) {
|
||||
if (err) {
|
||||
throw new Error("Failed to import", err);
|
||||
} else {
|
||||
if(res.body.indexOf("FrameCall('undefined', 'ok');") === -1){
|
||||
throw new Error("Failed DOC import", testPadId);
|
||||
}else{
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let form = req.form();
|
||||
form.append('file', wordDoc, {
|
||||
filename: '/test.doc',
|
||||
contentType: 'application/msword'
|
||||
});
|
||||
});
|
||||
|
||||
it('exports DOC', function(done) {
|
||||
if(!settings.allowAnyoneToImport) return done();
|
||||
if((settings.abiword && settings.abiword.indexOf("/" === -1)) && (settings.office && settings.soffice.indexOf("/" === -1))) return done();
|
||||
try{
|
||||
request(host + '/p/'+testPadId+'/export/doc', function (err, res, body) {
|
||||
// TODO: At some point checking that the contents is correct would be suitable
|
||||
if(body.length >= 9000){
|
||||
done();
|
||||
}else{
|
||||
throw new Error("Word Document export length is not right");
|
||||
}
|
||||
})
|
||||
}catch(e){
|
||||
throw new Error(e);
|
||||
}
|
||||
})
|
||||
|
||||
it('Tries to import .docx that uses soffice or abiword', function(done) {
|
||||
if(!settings.allowAnyoneToImport) return done();
|
||||
if((settings.abiword && settings.abiword.indexOf("/" === -1)) && (settings.office && settings.soffice.indexOf("/" === -1))) return done();
|
||||
|
||||
var req = request.post(host + '/p/'+testPadId+'/import', function (err, res, body) {
|
||||
if (err) {
|
||||
throw new Error("Failed to import", err);
|
||||
} else {
|
||||
if(res.body.indexOf("FrameCall('undefined', 'ok');") === -1){
|
||||
throw new Error("Failed DOCX import");
|
||||
}else{
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let form = req.form();
|
||||
form.append('file', wordXDoc, {
|
||||
filename: '/test.docx',
|
||||
contentType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
||||
});
|
||||
});
|
||||
|
||||
it('exports DOC from imported DOCX', function(done) {
|
||||
if(!settings.allowAnyoneToImport) return done();
|
||||
if((settings.abiword && settings.abiword.indexOf("/" === -1)) && (settings.office && settings.soffice.indexOf("/" === -1))) return done();
|
||||
request(host + '/p/'+testPadId+'/export/doc', function (err, res, body) {
|
||||
// TODO: At some point checking that the contents is correct would be suitable
|
||||
if(body.length >= 9100){
|
||||
done();
|
||||
}else{
|
||||
throw new Error("Word Document export length is not right");
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('Tries to import .pdf that uses soffice or abiword', function(done) {
|
||||
if(!settings.allowAnyoneToImport) return done();
|
||||
if((settings.abiword && settings.abiword.indexOf("/" === -1)) && (settings.office && settings.soffice.indexOf("/" === -1))) return done();
|
||||
|
||||
var req = request.post(host + '/p/'+testPadId+'/import', function (err, res, body) {
|
||||
if (err) {
|
||||
throw new Error("Failed to import", err);
|
||||
} else {
|
||||
if(res.body.indexOf("FrameCall('undefined', 'ok');") === -1){
|
||||
throw new Error("Failed PDF import");
|
||||
}else{
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let form = req.form();
|
||||
form.append('file', pdfDoc, {
|
||||
filename: '/test.pdf',
|
||||
contentType: 'application/pdf'
|
||||
});
|
||||
});
|
||||
|
||||
it('exports PDF', function(done) {
|
||||
if(!settings.allowAnyoneToImport) return done();
|
||||
if((settings.abiword && settings.abiword.indexOf("/" === -1)) && (settings.office && settings.soffice.indexOf("/" === -1))) return done();
|
||||
request(host + '/p/'+testPadId+'/export/pdf', function (err, res, body) {
|
||||
// TODO: At some point checking that the contents is correct would be suitable
|
||||
if(body.length >= 1000){
|
||||
done();
|
||||
}else{
|
||||
throw new Error("PDF Document export length is not right");
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('Tries to import .odt that uses soffice or abiword', function(done) {
|
||||
if(!settings.allowAnyoneToImport) return done();
|
||||
if((settings.abiword && settings.abiword.indexOf("/" === -1)) && (settings.office && settings.soffice.indexOf("/" === -1))) return done();
|
||||
|
||||
var req = request.post(host + '/p/'+testPadId+'/import', function (err, res, body) {
|
||||
if (err) {
|
||||
throw new Error("Failed to import", err);
|
||||
} else {
|
||||
if(res.body.indexOf("FrameCall('undefined', 'ok');") === -1){
|
||||
throw new Error("Failed ODT import", testPadId);
|
||||
}else{
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let form = req.form();
|
||||
form.append('file', odtDoc, {
|
||||
filename: '/test.odt',
|
||||
contentType: 'application/odt'
|
||||
});
|
||||
});
|
||||
|
||||
it('exports ODT', function(done) {
|
||||
if(!settings.allowAnyoneToImport) return done();
|
||||
if((settings.abiword && settings.abiword.indexOf("/" === -1)) && (settings.office && settings.soffice.indexOf("/" === -1))) return done();
|
||||
request(host + '/p/'+testPadId+'/export/odt', function (err, res, body) {
|
||||
// TODO: At some point checking that the contents is correct would be suitable
|
||||
if(body.length >= 7000){
|
||||
done();
|
||||
}else{
|
||||
throw new Error("ODT Document export length is not right");
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('Tries to import .etherpad', function(done) {
|
||||
if(!settings.allowAnyoneToImport) return done();
|
||||
|
||||
var req = request.post(host + '/p/'+testPadId+'/import', function (err, res, body) {
|
||||
if (err) {
|
||||
throw new Error("Failed to import", err);
|
||||
} else {
|
||||
if(res.body.indexOf("FrameCall(\'true\', \'ok\');") === -1){
|
||||
throw new Error("Failed Etherpad import", err, testPadId);
|
||||
}else{
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let form = req.form();
|
||||
form.append('file', etherpadDoc, {
|
||||
filename: '/test.etherpad',
|
||||
contentType: 'application/etherpad'
|
||||
});
|
||||
});
|
||||
|
||||
it('exports Etherpad', function(done) {
|
||||
request(host + '/p/'+testPadId+'/export/etherpad', function (err, res, body) {
|
||||
// TODO: At some point checking that the contents is correct would be suitable
|
||||
if(body.indexOf("hello") !== -1){
|
||||
done();
|
||||
}else{
|
||||
console.error("body");
|
||||
throw new Error("Etherpad Document does not include hello");
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('exports HTML for this Etherpad file', function(done) {
|
||||
request(host + '/p/'+testPadId+'/export/html', function (err, res, body) {
|
||||
|
||||
// broken pre fix export -- <ul class="bullet"></li><ul class="bullet"></ul></li></ul>
|
||||
var expectedHTML = '<ul class="bullet"><li><ul class="bullet"><li>hello</ul></li></ul>';
|
||||
// expect body to include
|
||||
if(body.indexOf(expectedHTML) !== -1){
|
||||
done();
|
||||
}else{
|
||||
console.error(body);
|
||||
throw new Error("Exported HTML nested list items is not right", body);
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('tries to import Plain Text to a pad that does not exist', function(done) {
|
||||
var req = request.post(host + '/p/'+testPadId+testPadId+testPadId+'/import', function (err, res, body) {
|
||||
if (res.statusCode === 200) {
|
||||
throw new Error("Was able to import to a pad that doesn't exist");
|
||||
}else{
|
||||
// Wasn't able to write to a pad that doesn't exist, this is expected behavior
|
||||
api.get(endPoint('getText')+"&padID="+testPadId+testPadId+testPadId)
|
||||
.expect(function(res){
|
||||
if(res.body.code !== 1) throw new Error("Pad Exists");
|
||||
})
|
||||
.expect(200, done)
|
||||
}
|
||||
|
||||
let form = req.form();
|
||||
|
||||
form.append('file', padText, {
|
||||
filename: '/test.txt',
|
||||
contentType: 'text/plain'
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
it('Tries to import unsupported file type', function(done) {
|
||||
if(settings.allowUnknownFileEnds === true){
|
||||
console.log("allowing unknown file ends so skipping this test");
|
||||
return done();
|
||||
}
|
||||
|
||||
var req = request.post(host + '/p/'+testPadId+'/import', function (err, res, body) {
|
||||
if (err) {
|
||||
throw new Error("Failed to import", err);
|
||||
} else {
|
||||
if(res.body.indexOf("FrameCall('undefined', 'ok');") !== -1){
|
||||
console.log("worked");
|
||||
throw new Error("You shouldn't be able to import this file", testPadId);
|
||||
}
|
||||
return done();
|
||||
}
|
||||
});
|
||||
|
||||
let form = req.form();
|
||||
form.append('file', padText, {
|
||||
filename: '/test.xasdasdxx',
|
||||
contentType: 'weirdness/jobby'
|
||||
});
|
||||
});
|
||||
|
||||
// end of tests
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var endPoint = function(point, version){
|
||||
version = version || apiVersion;
|
||||
return '/api/'+version+'/'+point+'?apikey='+apiKey;
|
||||
}
|
||||
|
||||
function makeid()
|
||||
{
|
||||
var text = "";
|
||||
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
for( var i=0; i < 5; i++ ){
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
function generateLongText(){
|
||||
var text = "";
|
||||
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
for( var i=0; i < 80000; i++ ){
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
54
tests/backend/specs/api/instance.js
Normal file
54
tests/backend/specs/api/instance.js
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Tests for the instance-level APIs
|
||||
*
|
||||
* Section "GLOBAL FUNCTIONS" in src/node/db/API.js
|
||||
*/
|
||||
const assert = require('assert');
|
||||
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 path = require('path');
|
||||
|
||||
var filePath = path.join(__dirname, '../../../../APIKEY.txt');
|
||||
|
||||
var apiKey = fs.readFileSync(filePath, {encoding: 'utf-8'});
|
||||
apiKey = apiKey.replace(/\n$/, "");
|
||||
|
||||
var apiVersion = '1.2.14';
|
||||
|
||||
describe('Connectivity for instance-level API tests', function() {
|
||||
it('can connect', function(done) {
|
||||
api.get('/api/')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200, 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");
|
||||
|
||||
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)}`);
|
||||
}
|
||||
|
||||
if (!(('totalSessions' in res.body.data) && (typeof res.body.data.totalSessions === 'number'))) {
|
||||
throw new Error(`Response to getStats() does not contain field totalSessions, or it's not a number: ${JSON.stringify(res.body.data)}`);
|
||||
}
|
||||
|
||||
if (!(('totalActivePads' in res.body.data) && (typeof res.body.data.totalActivePads === 'number'))) {
|
||||
throw new Error(`Response to getStats() does not contain field totalActivePads, or it's not a number: ${JSON.stringify(res.body.data)}`);
|
||||
}
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200, done);
|
||||
});
|
||||
});
|
||||
|
||||
var endPoint = function(point, version){
|
||||
version = version || apiVersion;
|
||||
return '/api/'+version+'/'+point+'?apikey='+apiKey;
|
||||
}
|
|
@ -1,10 +1,17 @@
|
|||
var assert = require('assert')
|
||||
supertest = require(__dirname+'/../../../../src/node_modules/supertest'),
|
||||
fs = require('fs'),
|
||||
settings = require(__dirname+'/../../loadSettings').loadSettings(),
|
||||
api = supertest('http://'+settings.ip+":"+settings.port),
|
||||
path = require('path'),
|
||||
async = require(__dirname+'/../../../../src/node_modules/async');
|
||||
/*
|
||||
* ACHTUNG: there is a copied & modified version of this file in
|
||||
* <basedir>/tests/container/specs/api/pad.js
|
||||
*
|
||||
* TODO: unify those two files, and merge in a single one.
|
||||
*/
|
||||
|
||||
const assert = require('assert');
|
||||
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 path = require('path');
|
||||
const async = require(__dirname+'/../../../../src/node_modules/async');
|
||||
|
||||
var filePath = path.join(__dirname, '../../../../APIKEY.txt');
|
||||
|
||||
|
@ -26,10 +33,23 @@ var ulHtml = '<!doctype html><html><body><ul class="bullet"><li>one</li><li>two<
|
|||
* textually, but at least it remains standard compliant and has an equal DOM
|
||||
* structure.
|
||||
*/
|
||||
var expectedHtml = '<!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</ul></li></ul><ol class="number"><li>item<ol class="number"><li>item1</li><li>item2</ol></li></ol></body></html>';
|
||||
var expectedHtml = '<!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</ul></li></ul><ol start="1" class="number"><li>item<ol start="2" class="number"><li>item1</li><li>item2</ol></li></ol></body></html>';
|
||||
|
||||
/*
|
||||
* Html document with space between list items, to test its import and
|
||||
* verify it is exported back correctly
|
||||
*/
|
||||
var ulSpaceHtml = '<!doctype html><html><body><ul class="bullet"> <li>one</li></ul></body></html>';
|
||||
|
||||
/*
|
||||
* When exported back, Etherpad produces an html which is not exactly the same
|
||||
* textually, but at least it remains standard compliant and has an equal DOM
|
||||
* structure.
|
||||
*/
|
||||
var expectedSpaceHtml = '<!doctype html><html><body><ul class="bullet"><li>one</ul></body></html>';
|
||||
|
||||
describe('Connectivity', function(){
|
||||
it('errors if can not connect', function(done) {
|
||||
it('can connect', function(done) {
|
||||
api.get('/api/')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200, done)
|
||||
|
@ -37,7 +57,7 @@ describe('Connectivity', function(){
|
|||
})
|
||||
|
||||
describe('API Versioning', function(){
|
||||
it('errors if can not connect', function(done) {
|
||||
it('finds the version tag', function(done) {
|
||||
api.get('/api/')
|
||||
.expect(function(res){
|
||||
apiVersion = res.body.currentVersion;
|
||||
|
@ -49,7 +69,7 @@ describe('API Versioning', function(){
|
|||
})
|
||||
|
||||
describe('Permission', function(){
|
||||
it('errors if can connect without correct APIKey', function(done) {
|
||||
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';
|
||||
|
@ -104,7 +124,7 @@ describe('deletePad', function(){
|
|||
it('deletes a Pad', function(done) {
|
||||
api.get(endPoint('deletePad')+"&padID="+testPadId)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200, done)
|
||||
.expect(200, done) // @TODO: we shouldn't expect 200 here since the pad may not exist
|
||||
});
|
||||
})
|
||||
|
||||
|
@ -166,6 +186,19 @@ describe('getHTML', function(){
|
|||
});
|
||||
})
|
||||
|
||||
describe('listAllPads', function () {
|
||||
it('list all pads', function (done) {
|
||||
api.get(endPoint('listAllPads'))
|
||||
.expect(function (res) {
|
||||
if (res.body.data.padIDs.includes(testPadId) !== true) {
|
||||
throw new Error('Unable to find pad in pad list')
|
||||
}
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('deletePad', function(){
|
||||
it('deletes a Pad', function(done) {
|
||||
api.get(endPoint('deletePad')+"&padID="+testPadId)
|
||||
|
@ -177,6 +210,19 @@ describe('deletePad', function(){
|
|||
});
|
||||
})
|
||||
|
||||
describe('listAllPads', function () {
|
||||
it('list all pads', function (done) {
|
||||
api.get(endPoint('listAllPads'))
|
||||
.expect(function (res) {
|
||||
if (res.body.data.padIDs.includes(testPadId) !== false) {
|
||||
throw new Error('Test pad should not be in pads list')
|
||||
}
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getHTML', function(){
|
||||
it('get the HTML of a Pad -- Should return a failure', function(done) {
|
||||
api.get(endPoint('getHTML')+"&padID="+testPadId)
|
||||
|
@ -204,7 +250,7 @@ describe('getText', function(){
|
|||
api.get(endPoint('getText')+"&padID="+testPadId)
|
||||
.expect(function(res){
|
||||
if(res.body.data.text !== "testText\n") throw new Error("Pad Creation with text")
|
||||
})
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200, done)
|
||||
});
|
||||
|
@ -212,7 +258,11 @@ describe('getText', function(){
|
|||
|
||||
describe('setText', function(){
|
||||
it('creates a new Pad with text', function(done) {
|
||||
api.get(endPoint('setText')+"&padID="+testPadId+"&text=testTextTwo")
|
||||
api.post(endPoint('setText'))
|
||||
.send({
|
||||
"padID": testPadId,
|
||||
"text": "testTextTwo",
|
||||
})
|
||||
.expect(function(res){
|
||||
if(res.body.code !== 0) throw new Error("Pad setting text failed");
|
||||
})
|
||||
|
@ -327,7 +377,11 @@ describe('getLastEdited', function(){
|
|||
|
||||
describe('setText', function(){
|
||||
it('creates a new Pad with text', function(done) {
|
||||
api.get(endPoint('setText')+"&padID="+testPadId+"&text=testTextTwo")
|
||||
api.post(endPoint('setText'))
|
||||
.send({
|
||||
"padID": testPadId,
|
||||
"text": "testTextTwo",
|
||||
})
|
||||
.expect(function(res){
|
||||
if(res.body.code !== 0) throw new Error("Pad setting text failed");
|
||||
})
|
||||
|
@ -387,7 +441,8 @@ describe('createPad', function(){
|
|||
|
||||
describe('setText', function(){
|
||||
it('Sets text on a pad Id', function(done) {
|
||||
api.get(endPoint('setText')+"&padID="+testPadId+"&text="+text)
|
||||
api.post(endPoint('setText')+"&padID="+testPadId)
|
||||
.field({text: text})
|
||||
.expect(function(res){
|
||||
if(res.body.code !== 0) throw new Error("Pad Set Text failed")
|
||||
})
|
||||
|
@ -410,7 +465,8 @@ describe('getText', function(){
|
|||
|
||||
describe('setText', function(){
|
||||
it('Sets text on a pad Id including an explicit newline', function(done) {
|
||||
api.get(endPoint('setText')+"&padID="+testPadId+"&text="+text+'%0A')
|
||||
api.post(endPoint('setText')+"&padID="+testPadId)
|
||||
.field({text: text+'\n'})
|
||||
.expect(function(res){
|
||||
if(res.body.code !== 0) throw new Error("Pad Set Text failed")
|
||||
})
|
||||
|
@ -524,9 +580,13 @@ describe('getText', function(){
|
|||
describe('setHTML', function(){
|
||||
it('Sets the HTML of a Pad attempting to pass ugly HTML', function(done) {
|
||||
var html = "<div><b>Hello HTML</title></head></div>";
|
||||
api.get(endPoint('setHTML')+"&padID="+testPadId+"&html="+html)
|
||||
api.post(endPoint('setHTML'))
|
||||
.send({
|
||||
"padID": testPadId,
|
||||
"html": html,
|
||||
})
|
||||
.expect(function(res){
|
||||
if(res.body.code !== 1) throw new Error("Allowing crappy HTML to be imported")
|
||||
if(res.body.code !== 0) throw new Error("Crappy HTML Can't be Imported[we weren't able to sanitize it']")
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200, done)
|
||||
|
@ -535,7 +595,11 @@ describe('setHTML', function(){
|
|||
|
||||
describe('setHTML', function(){
|
||||
it('Sets the HTML of a Pad with complex nested lists of different types', function(done) {
|
||||
api.get(endPoint('setHTML')+"&padID="+testPadId+"&html="+ulHtml)
|
||||
api.post(endPoint('setHTML'))
|
||||
.send({
|
||||
"padID": testPadId,
|
||||
"html": ulHtml,
|
||||
})
|
||||
.expect(function(res){
|
||||
if(res.body.code !== 0) throw new Error("List HTML cant be imported")
|
||||
})
|
||||
|
@ -567,11 +631,44 @@ describe('getHTML', function(){
|
|||
});
|
||||
})
|
||||
|
||||
describe('setHTML', function(){
|
||||
it('Sets the HTML of a Pad with white space between list items', function(done) {
|
||||
api.get(endPoint('setHTML')+"&padID="+testPadId+"&html="+ulSpaceHtml)
|
||||
.expect(function(res){
|
||||
if(res.body.code !== 0) throw new Error("List HTML cant be imported")
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200, done)
|
||||
});
|
||||
})
|
||||
|
||||
describe('getHTML', function(){
|
||||
it('Gets back the HTML of a Pad with complex nested lists of different types', function(done) {
|
||||
api.get(endPoint('getHTML')+"&padID="+testPadId)
|
||||
.expect(function(res){
|
||||
var receivedHtml = res.body.data.html.replace("<br></body>", "</body>").toLowerCase();
|
||||
if (receivedHtml !== expectedSpaceHtml) {
|
||||
throw new Error(`HTML received from export is not the one we were expecting.
|
||||
Received:
|
||||
${receivedHtml}
|
||||
|
||||
Expected:
|
||||
${expectedSpaceHtml}
|
||||
|
||||
Which is a slightly modified version of the originally imported one:
|
||||
${ulSpaceHtml}`);
|
||||
}
|
||||
})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200, done)
|
||||
});
|
||||
})
|
||||
|
||||
describe('createPad', function(){
|
||||
it('errors if pad can be created', function(done) {
|
||||
var badUrlChars = ["/", "%23", "%3F", "%26"];
|
||||
async.map(
|
||||
badUrlChars,
|
||||
badUrlChars,
|
||||
function (badUrlChar, cb) {
|
||||
api.get(endPoint('createPad')+"&padID="+badUrlChar)
|
||||
.expect(function(res){
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
var assert = require('assert')
|
||||
supertest = require(__dirname+'/../../../../src/node_modules/supertest'),
|
||||
fs = require('fs'),
|
||||
settings = require(__dirname+'/../../loadSettings').loadSettings(),
|
||||
settings = require(__dirname + '/../../../../src/node/utils/Settings'),
|
||||
api = supertest('http://'+settings.ip+":"+settings.port),
|
||||
path = require('path');
|
||||
|
||||
|
|
BIN
tests/backend/specs/api/test.doc
Normal file
BIN
tests/backend/specs/api/test.doc
Normal file
Binary file not shown.
BIN
tests/backend/specs/api/test.docx
Normal file
BIN
tests/backend/specs/api/test.docx
Normal file
Binary file not shown.
1
tests/backend/specs/api/test.etherpad
Normal file
1
tests/backend/specs/api/test.etherpad
Normal file
|
@ -0,0 +1 @@
|
|||
{"pad:Pd4b1Kgvv9qHZZtj8yzl":{"atext":{"text":"*hello\n","attribs":"*0*1*5*3*4+1*0+5|1+1"},"pool":{"numToAttrib":{"0":["author","a.ElbBWNTxmtRrfFqn"],"1":["insertorder","first"],"2":["list","bullet1"],"3":["lmkr","1"],"4":["start","1"],"5":["list","bullet2"]},"nextNum":6},"head":5,"chatHead":-1,"publicStatus":false,"passwordHash":null,"savedRevisions":[]},"globalAuthor:a.ElbBWNTxmtRrfFqn":{"colorId":50,"name":null,"timestamp":1595111151414,"padIDs":"Pd4b1Kgvv9qHZZtj8yzl"},"pad:Pd4b1Kgvv9qHZZtj8yzl:revs:0":{"changeset":"Z:1>bj|7+bj$Welcome to Etherpad!\n\nThis pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!\n\nGet involved with Etherpad at https://etherpad.org\n\nWarning: DirtyDB is used. This is fine for testing but not recommended for production. -- To suppress these warning messages change suppressErrorsInPadText to true in your settings.json\n","meta":{"author":"","timestamp":1595111092400,"pool":{"numToAttrib":{},"attribToNum":{},"nextNum":0},"atext":{"text":"Welcome to Etherpad!\n\nThis pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!\n\nGet involved with Etherpad at https://etherpad.org\n\nWarning: DirtyDB is used. This is fine for testing but not recommended for production. -- To suppress these warning messages change suppressErrorsInPadText to true in your settings.json\n\n","attribs":"|8+bk"}}},"pad:Pd4b1Kgvv9qHZZtj8yzl:revs:1":{"changeset":"Z:bk<bj|7-bj$","meta":{"author":"a.ElbBWNTxmtRrfFqn","timestamp":1595111112138}},"pad:Pd4b1Kgvv9qHZZtj8yzl:revs:2":{"changeset":"Z:1>1*0*1*2*3*4+1$*","meta":{"author":"a.ElbBWNTxmtRrfFqn","timestamp":1595111119434}},"pad:Pd4b1Kgvv9qHZZtj8yzl:revs:3":{"changeset":"Z:2>0*5*4=1$","meta":{"author":"a.ElbBWNTxmtRrfFqn","timestamp":1595111127471}},"pad:Pd4b1Kgvv9qHZZtj8yzl:revs:4":{"changeset":"Z:2>2=1*0+2$he","meta":{"author":"a.ElbBWNTxmtRrfFqn","timestamp":1595111128230}},"pad:Pd4b1Kgvv9qHZZtj8yzl:revs:5":{"changeset":"Z:4>3=3*0+3$llo","meta":{"author":"a.ElbBWNTxmtRrfFqn","timestamp":1595111128727}}}
|
BIN
tests/backend/specs/api/test.odt
Normal file
BIN
tests/backend/specs/api/test.odt
Normal file
Binary file not shown.
BIN
tests/backend/specs/api/test.pdf
Normal file
BIN
tests/backend/specs/api/test.pdf
Normal file
Binary file not shown.
1
tests/backend/specs/api/test.txt
Normal file
1
tests/backend/specs/api/test.txt
Normal file
|
@ -0,0 +1 @@
|
|||
This is the contents we insert into pads when testing the import functions. Thanks for using Etherpad!
|
|
@ -1,10 +1,12 @@
|
|||
var 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');
|
||||
|
||||
describe('tidyHtml', function() {
|
||||
before(function(done) {
|
||||
|
@ -16,6 +18,10 @@ describe('tidyHtml', function() {
|
|||
});
|
||||
});
|
||||
|
||||
function tidy(file, callback) {
|
||||
return nodeify(TidyHtml.tidy(file), callback);
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -23,10 +29,11 @@ describe('tidyHtml', function() {
|
|||
}
|
||||
|
||||
// Try to tidy up a bad HTML file
|
||||
var tmpDir = process.env.TEMP || "/tmp";
|
||||
const tmpDir = os.tmpdir();
|
||||
|
||||
var 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>');
|
||||
TidyHtml.tidy(tmpFile, function(err){
|
||||
tidy(tmpFile, function(err){
|
||||
assert.ok(!err);
|
||||
|
||||
// Read the file again
|
||||
|
@ -55,7 +62,7 @@ describe('tidyHtml', function() {
|
|||
this.skip();
|
||||
}
|
||||
|
||||
TidyHtml.tidy('/some/none/existing/file.html', function(err) {
|
||||
tidy('/some/none/existing/file.html', function(err) {
|
||||
assert.ok(err);
|
||||
return done();
|
||||
});
|
||||
|
|
175
tests/backend/specs/contentcollector.js
Normal file
175
tests/backend/specs/contentcollector.js
Normal file
|
@ -0,0 +1,175 @@
|
|||
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",
|
||||
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'
|
||||
],
|
||||
expectedText: [
|
||||
'*one', '*1.1', '*two'
|
||||
]
|
||||
},
|
||||
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 : [
|
||||
'*0*1*2+1+3',
|
||||
'*0*1*2+1+3',
|
||||
'*0*1*2+1+1',
|
||||
'*0*1*2+1+1',
|
||||
'*0*1*2+1+1',
|
||||
'*0*3*2+1+1',
|
||||
'*0*3*2+1+1',
|
||||
'*0*4*2*5+1+4',
|
||||
'*0*6*2*7+1+5',
|
||||
'*0*6*2*7+1+5'
|
||||
],
|
||||
expectedText: [
|
||||
'*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"]
|
||||
}
|
||||
,
|
||||
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"]
|
||||
},
|
||||
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"
|
||||
},
|
||||
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?"
|
||||
},
|
||||
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
|
||||
},
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// For each test..
|
||||
for (let test in tests){
|
||||
let testObj = tests[test];
|
||||
|
||||
describe(test, function() {
|
||||
if(testObj.disabled){
|
||||
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
|
||||
// Create an empty attribute pool
|
||||
var 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);
|
||||
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;
|
||||
|
||||
// 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)
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
// Check recieved attributes matches the expected attributes
|
||||
if(arraysEqual(recievedAttributes, expectedAttributes)){
|
||||
// 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);
|
||||
throw new Error();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
function arraysEqual(a, b) {
|
||||
if (a === b) return true;
|
||||
if (a == null || b == null) return false;
|
||||
if (a.length != b.length) return false;
|
||||
|
||||
// If you don't care about the order of the elements inside
|
||||
// the array, you should sort both arrays here.
|
||||
// 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) {
|
||||
if (a[i] !== b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
87
tests/backend/specs/minify.js
Normal file
87
tests/backend/specs/minify.js
Normal file
|
@ -0,0 +1,87 @@
|
|||
|
||||
const request = require(__dirname+'/../../../src/node_modules/supertest')
|
||||
|
||||
/**
|
||||
* types
|
||||
* woff
|
||||
* WASM
|
||||
* html
|
||||
* woff2
|
||||
* javascript
|
||||
*
|
||||
* require-js stuff
|
||||
*
|
||||
* try to require every single file in src/
|
||||
*/
|
||||
|
||||
/**
|
||||
* test header
|
||||
* test header with garbage
|
||||
* if-modified-since
|
||||
* last-modfied
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// ensure minification is true
|
||||
describe('Minify', function(done) {
|
||||
it('serves fonts', function(done) {
|
||||
request('http://localhost:9001')
|
||||
.get('/p/MINIFY_TEST_123')
|
||||
//.set('Accept', 'application/json')
|
||||
//.expect('Content-Type', /json/)
|
||||
.expect(200, done);
|
||||
});
|
||||
});
|
||||
//const assert = require('assert');
|
||||
//const supertest = require(__dirname+'/../../../../src/node_modules/supertest');
|
||||
//const fs = require('fs');
|
||||
//const settings = require(__dirname+'/../../../../src/node/utils/Settings');
|
||||
//const host = 'http://127.0.0.1:'+settings.port;
|
||||
//const api = supertest('http://'+settings.ip+":"+settings.port);
|
||||
//const path = require('path');
|
||||
//const async = require(__dirname+'/../../../../src/node_modules/async');
|
||||
//const request = require(__dirname+'/../../../../src/node_modules/request');
|
||||
//var filePath = path.join(__dirname, '../../../../APIKEY.txt');
|
||||
//
|
||||
//var apiKey = fs.readFileSync(filePath, {encoding: 'utf-8'});
|
||||
//apiKey = apiKey.replace(/\n$/, "");
|
||||
//var apiVersion = 1;
|
||||
//var testPadId = makeid();
|
||||
//var lastEdited = "";
|
||||
//var text = generateLongText();
|
||||
//
|
||||
//describe('Connectivity', function(){
|
||||
// it('can connect', function(done) {
|
||||
// api.get('/api/')
|
||||
// .expect('Content-Type', /json/)
|
||||
// .expect(200, done)
|
||||
// });
|
||||
//})
|
||||
//
|
||||
//describe('API Versioning', function(){
|
||||
// it('finds the version tag', function(done) {
|
||||
// api.get('/api/')
|
||||
// .expect(function(res){
|
||||
// apiVersion = res.body.currentVersion;
|
||||
// if (!res.body.currentVersion) throw new Error("No version set in API");
|
||||
// return;
|
||||
// })
|
||||
// .expect(200, done)
|
||||
// });
|
||||
//})
|
||||
//
|
||||
//function makeid()
|
||||
//{
|
||||
// var text = "";
|
||||
// var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
//
|
||||
// for( var i=0; i < 5; i++ ){
|
||||
// text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
// }
|
||||
// return text;
|
||||
//}
|
||||
//
|
Loading…
Add table
Add a link
Reference in a new issue