From ab8320d15bea052123e7d361c363ddca89bc801a Mon Sep 17 00:00:00 2001 From: John McLear Date: Tue, 14 Jul 2020 18:44:53 +0100 Subject: [PATCH] WIP - Test Coverage: Import & Export include LibreOffice Test Coverage (#4163) Runs on Travis Will only run locally is ``allowAnyoneToImport`` and ``soffice`` or ``abiword`` is set. --- .travis.yml | 4 + src/node/hooks/express/importexport.js | 4 +- .../backend/specs/api/importexportGetPost.js | 257 +++++++++++++----- tests/backend/specs/api/pad.js | 1 - tests/backend/specs/api/test.docx | Bin 0 -> 4205 bytes tests/backend/specs/api/test.odt | Bin 0 -> 8403 bytes tests/backend/specs/api/test.pdf | Bin 0 -> 9388 bytes 7 files changed, 190 insertions(+), 76 deletions(-) create mode 100644 tests/backend/specs/api/test.docx create mode 100644 tests/backend/specs/api/test.odt create mode 100644 tests/backend/specs/api/test.pdf diff --git a/.travis.yml b/.travis.yml index 43695b3b5..cb5a4de59 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,6 +38,10 @@ jobs: - name: "Run the Backend tests" install: - "bin/installDeps.sh" + # Set soffice to /usr/bin/soffice + - "sed 's/\"soffice\": null,/\"soffice\": "/usr/bin/soffice",/g' settings.json.template > settings.json" + # Set allowAnyoneToImport to true + - "sed 's/\"allowAnyoneToImport\": false,/\"allowAnyoneToInput\": true,/g' settings.json > settings.json" - "cd src && npm install && cd -" script: - "tests/frontend/travis/runnerBackend.sh" diff --git a/src/node/hooks/express/importexport.js b/src/node/hooks/express/importexport.js index bf85ae13e..9ddefe8a0 100644 --- a/src/node/hooks/express/importexport.js +++ b/src/node/hooks/express/importexport.js @@ -71,12 +71,12 @@ exports.expressCreateServer = function (hook_name, args, cb) { * * See: https://github.com/ether/etherpad-lite/pull/3833#discussion_r407490205 */ - if (!req.cookies) { + if (!req.cookies && !settings.allowAnyoneToImport) { console.warn(`Unable to import file into "${req.params.pad}". No cookies included in request`); return next(); } - if (!req.cookies.token) { + if (!req.cookies.token && !settings.allowAnyoneToImport) { console.warn(`Unable to import file into "${req.params.pad}". No token in the cookies`); return next(); } diff --git a/tests/backend/specs/api/importexportGetPost.js b/tests/backend/specs/api/importexportGetPost.js index 7bbe88139..aad5fd3ce 100644 --- a/tests/backend/specs/api/importexportGetPost.js +++ b/tests/backend/specs/api/importexportGetPost.js @@ -3,10 +3,14 @@ * Executed using request. Designed to find flaws and bugs */ +// import wont work due to sessions missing +// Waiting on https://github.com/ether/etherpad-lite/pull/4012/files to be merged to be fully functional +// Logic for creating sessions is the sessionandGroups.js test spec + 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 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'); @@ -14,6 +18,9 @@ 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 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'}); @@ -59,13 +66,6 @@ Test. Test. / Try to import an unsupported file to a pad that exists -TODO: Test. - / Try to import a file that depends on Abiword / soffice if it exists - / Try to export a file that depends on Abiword / soffice if it exists - --- TODO: Test. - Try to import to a pad without a session (currently will pass but IMHO in future should fail) - -- TODO: Test. Try to import to a file and abort it half way through @@ -79,39 +79,188 @@ Example Curl command for testing import URI: describe('Imports and Exports', function(){ it('creates a new Pad, imports content to it, checks that content', function(done) { + if(!settings.allowAnyoneToImport){ + console.log("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"); - 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"); + } + }) + } + }); - var req = request.post(host + '/p/'+testPadId+'/import', function (err, res, body) { - if (err) { - throw new Error("Failed to import", err); - } else { + let form = req.form(); - api.get(endPoint('getText')+"&padID="+testPadId) - .expect(function(res){ - if(res.body.data.text === padText.toString()){ - console.log("yay it matches"); - } - }) - .expect(200) - } - }); + form.append('file', padText, { + filename: '/test.txt', + contentType: 'text/plain' + }); - let form = req.form(); - - form.append('file', padText, { - filename: '/test.txt', - contentType: 'text/plain' - }); - - }) - .expect('Content-Type', /json/) - .expect(200, done) + }) + .expect('Content-Type', /json/) + .expect(200, done) + } }); - it('tries to import to a pad that does not exist', function(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' + }); + }); + + xit('exports DOC', 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) { + // expect length to be > 9000 + // 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"); + } + }) + }) + + 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", testPadId); + }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", testPadId); + }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 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"); @@ -158,44 +307,6 @@ describe('Imports and Exports', function(){ }); }); - if(settings.abiword.indexOf("/" === -1) && settings.soffice.indexOf("/" === -1)){ - console.log("Did not test abiword or soffice"); - }else{ - it('Tries to import file type that uses soffice or abioffice', function(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); - } - - request(host + '/p/'+testPadId+'/export/doc', function (errE, resE, bodyE) { - if(resE.body.indexOf("Hello World") === -1) throw new Error("Could not find Hello World in exported contents"); - - api.get(endPoint('getText')+"&padID="+testPadId) - .expect(function(res){ - if(res.body.code !== 0) throw new Error("Could not get pad"); - // Not graceflu but it works - console.warn("HERE"); - }) - .expect(200, done); - }); - } - }); - - let form = req.form(); - - form.append('file', wordDoc, { - filename: '/tmp/test.doc', - contentType: 'application/msword' - }); - - }); - } - - // end of tests }) diff --git a/tests/backend/specs/api/pad.js b/tests/backend/specs/api/pad.js index 3d1693bd3..95138c0b8 100644 --- a/tests/backend/specs/api/pad.js +++ b/tests/backend/specs/api/pad.js @@ -647,7 +647,6 @@ describe('getHTML', function(){ api.get(endPoint('getHTML')+"&padID="+testPadId) .expect(function(res){ var receivedHtml = res.body.data.html.replace("
", "").toLowerCase(); -console.log(receivedHtml); if (receivedHtml !== expectedSpaceHtml) { throw new Error(`HTML received from export is not the one we were expecting. Received: diff --git a/tests/backend/specs/api/test.docx b/tests/backend/specs/api/test.docx new file mode 100644 index 0000000000000000000000000000000000000000..422bb4f037ed4a875f1c87e34a18a4b29d1ecdea GIT binary patch literal 4205 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 T{&S>oArPiGiCtD*j^KX)a-^>F literal 0 HcmV?d00001