Merge branch 'develop'

This commit is contained in:
John McLear 2021-02-18 14:37:06 +00:00
commit 65867eb895
No known key found for this signature in database
GPG Key ID: 599378BB471BCE1C
41 changed files with 563 additions and 401 deletions

View File

@ -30,9 +30,8 @@ jobs:
- name: Install all dependencies and symlink for ep_etherpad-lite
run: src/bin/installDeps.sh
# configures some settings and runs npm run test
- name: Run the backend tests
run: src/tests/frontend/travis/runnerBackend.sh
run: cd src && npm test
withplugins:
# run on pushes to any branch
@ -84,6 +83,5 @@ jobs:
- name: Install all dependencies and symlink for ep_etherpad-lite
run: src/bin/installDeps.sh
# configures some settings and runs npm run test
- name: Run the backend tests
run: src/tests/frontend/travis/runnerBackend.sh
run: cd src && npm test

View File

@ -64,7 +64,7 @@ jobs:
- "src/bin/installDeps.sh"
- "cd src && npm install && cd -"
script:
- "src/tests/frontend/travis/runnerBackend.sh"
- "cd src && npm test"
- name: "Test the Dockerfile"
install:
- "cd src && npm install && cd -"
@ -107,7 +107,7 @@ jobs:
- *install_plugins
- "cd src && npm install && cd -"
script:
- "src/tests/frontend/travis/runnerBackend.sh"
- "cd src && npm test"
- name: "Test the Dockerfile"
install:
- "cd src && npm install && cd -"

View File

@ -1,3 +1,15 @@
# 1.8.9
### Notable fixes
* Fixed HTTP 400 error when importing via the UI.
* Fixed "Error: spawn npm ENOENT" crash on startup in Windows.
### Notable enhancements
* Removed some unnecessary arrow key handling logic.
* Dependency updates.
# 1.8.8
### Security patches

View File

@ -15,6 +15,22 @@ LABEL maintainer="Etherpad team, https://github.com/ether/etherpad-lite"
# ETHERPAD_PLUGINS="ep_codepad ep_author_neat"
ARG ETHERPAD_PLUGINS=
# Control whether abiword will be installed, enabling exports to DOC/PDF/ODT formats.
# By default, it is not installed.
# If given any value, abiword will be installed.
#
# EXAMPLE:
# INSTALL_ABIWORD=true
ARG INSTALL_ABIWORD=
# Control whether libreoffice will be installed, enabling exports to DOC/PDF/ODT formats.
# By default, it is not installed.
# If given any value, libreoffice will be installed.
#
# EXAMPLE:
# INSTALL_LIBREOFFICE=true
ARG INSTALL_SOFFICE=
# By default, Etherpad container is built and run in "production" mode. This is
# leaner (development dependencies are not installed) and runs faster (among
# other things, assets are minified & compressed).
@ -28,6 +44,13 @@ RUN useradd --uid 5001 --create-home etherpad
RUN mkdir /opt/etherpad-lite && chown etherpad:0 /opt/etherpad-lite
# install abiword for DOC/PDF/ODT export
RUN [ -z "${INSTALL_ABIWORD}" ] || (apt update && apt -y install abiword && apt clean && rm -rf /var/lib/apt/lists/*)
# install libreoffice for DOC/PDF/ODT export
# the mkdir is needed for configuration of openjdk-11-jre-headless, see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=863199
RUN [ -z "${INSTALL_SOFFICE}" ] || (apt update && mkdir -p /usr/share/man/man1 && apt -y install libreoffice && apt clean && rm -rf /var/lib/apt/lists/*)
USER etherpad
WORKDIR /opt/etherpad-lite
@ -51,4 +74,4 @@ COPY --chown=etherpad:0 ./settings.json.docker /opt/etherpad-lite/settings.json
RUN chmod -R g=u .
EXPOSE 9001
CMD ["node", "--experimental-worker", "node_modules/ep_etherpad-lite/node/server.js"]
CMD ["node", "--experimental-worker", "src/node/server.js"]

View File

@ -66,7 +66,7 @@ Update to the latest version with `git pull origin`, then run
If cloning to a subdirectory within another project, you may need to do the following:
1. Start the server manually (e.g. `node/node_modules/ep_etherpad-lite/node/server.js`)
1. Start the server manually (e.g. `node src/node/server.js`)
2. Edit the db `filename` in `settings.json` to the relative directory with the file (e.g. `application/lib/etherpad-lite/var/dirty.db`)
3. Add auto-generated files to the main project `.gitignore`

View File

@ -29,6 +29,30 @@ The variable value has to be a space separated, double quoted list of plugin nam
Some plugins will need personalized settings. Just refer to the previous section, and include them in your custom `settings.json.docker`.
### Rebuilding including export functionality for DOC/PDF/ODT
If you want to be able to export your pads to DOC/PDF/ODT files, you can install
either Abiword or Libreoffice via setting a build variable.
#### Via Abiword
For installing Abiword, set the `INSTALL_ABIWORD` build variable to any value.
Also, you will need to configure the path to the abiword executable
via setting the `abiword` property in `<BASEDIR>/settings.json.docker` to
`/usr/bin/abiword` or via setting the environment variable `ABIWORD` to
`/usr/bin/abiword`.
#### Via Libreoffice
For installing Libreoffice instead, set the `INSTALL_SOFFICE` build variable
to any value.
Also, you will need to configure the path to the libreoffice executable
via setting the `soffice` property in `<BASEDIR>/settings.json.docker` to
`/usr/bin/soffice` or via setting the environment variable `SOFFICE` to
`/usr/bin/soffice`.
### Examples
Build a Docker image from the currently checked-out code:
@ -168,8 +192,8 @@ For the editor container, you can also make it full width by adding `full-width-
| `IMPORT_MAX_FILE_SIZE` | maximum allowed file size when importing a pad, in bytes. | `52428800` (50 MB) |
| `IMPORT_EXPORT_MAX_REQ_PER_IP` | maximum number of import/export calls per IP. | `10` |
| `IMPORT_EXPORT_RATE_LIMIT_WINDOW` | the call rate for import/export requests will be estimated in this time window (in milliseconds) | `90000` |
| `COMMIT_RATE_LIMIT_DURATION` | duration of the rate limit window for commits by individual users/IPs (in seconds) | `1` |
| `COMMIT_RATE_LIMIT_POINTS` | maximum number of changes per IP to allow during the rate limit window | `10` |
| `COMMIT_RATE_LIMIT_DURATION` | duration of the rate limit window for commits by individual users/IPs (in seconds) | `1` |
| `COMMIT_RATE_LIMIT_POINTS` | maximum number of changes per IP to allow during the rate limit window | `10` |
| `SUPPRESS_ERRORS_IN_PAD_TEXT` | Should we suppress errors from being visible in the default Pad Text? | `false` |
| `REQUIRE_SESSION` | If this option is enabled, a user must have a session to access pads. This effectively allows only group pads to be accessed. | `false` |
| `EDIT_ONLY` | Users may edit pads but not create new ones. Pad creation is only via the API. This applies both to group pads and regular pads. | `false` |

View File

@ -7,7 +7,7 @@ fatal() { error "$@"; exit 1; }
is_cmd() { command -v "$@" >/dev/null 2>&1; }
# Move to the folder where ep-lite is installed
cd "$(dirname "$0")"/..
cd "$(cd "${0%/*}" && pwd -P)/../.."
# Is wget installed?
is_cmd wget || fatal "Please install wget"
@ -54,7 +54,7 @@ rm -rf "$TMP_FOLDER"/src/node_modules/nodemailer/node_modules/mailcomposer/node_
log "create the zip..."
cd "$TMP_FOLDER"
zip -9 -r "$START_FOLDER"/etherpad-lite-win.zip ./*
zip -9 -r "$START_FOLDER"/etherpad-lite-win.zip ./* -x var
log "clean up..."
rm -rf "$TMP_FOLDER"

View File

@ -34,7 +34,6 @@ rm -rf src/node_modules
src/bin/installDeps.sh "$@" || exit 1
#Move to the node folder and start
echo "Started Etherpad..."
echo "Starting Etherpad..."
SCRIPTPATH=$(pwd -P)
node $(compute_node_args) "${SCRIPTPATH}/node_modules/ep_etherpad-lite/node/server.js" "$@"
exec node $(compute_node_args) src/node/server.js "$@"

View File

@ -20,7 +20,7 @@ end script
script
cd $EPHOME/
exec su -s /bin/sh -c 'exec "$0" "$@"' $EPUSER -- node node_modules/ep_etherpad-lite/node/server.js \
exec su -s /bin/sh -c 'exec "$0" "$@"' $EPUSER -- node src/node/server.js \
>> $EPLOGS/access.log \
2>> $EPLOGS/error.log
echo "Etherpad is running on http://localhost:9001 - To change settings edit /opt/etherpad/settings.json"

View File

@ -16,4 +16,4 @@ echo "Open 'chrome://inspect' on Chrome to start debugging."
# Use 0.0.0.0 to allow external connections to the debugger
# (ex: running Etherpad on a docker container). Use default port # (9229)
node $(compute_node_args) --inspect=0.0.0.0:9229 node_modules/ep_etherpad-lite/node/server.js "$@"
exec node $(compute_node_args) --inspect=0.0.0.0:9229 src/node/server.js "$@"

View File

@ -5,9 +5,9 @@
"requires": true,
"dependencies": {
"marked": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/marked/-/marked-0.8.2.tgz",
"integrity": "sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw=="
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/marked/-/marked-1.1.1.tgz",
"integrity": "sha512-mJzT8D2yPxoPh7h0UXkB+dBj4FykPJ2OIfxAWeIHrvoHDkFxukV/29QxoFQoPM6RLEwhIFdJpmKBlqVM3s2ZIw=="
}
}
}

View File

@ -7,7 +7,7 @@
"node": ">=10.17.0"
},
"dependencies": {
"marked": "0.8.2"
"marked": "1.1.1"
},
"devDependencies": {},
"optionalDependencies": {},

View File

@ -19,4 +19,4 @@ cd "${MY_DIR}/../.." || exit 1
echo "Running directly, without checking/installing dependencies"
# run Etherpad main class
node $(compute_node_args) "node_modules/ep_etherpad-lite/node/server.js" "$@"
exec node $(compute_node_args) src/node/server.js "$@"

View File

@ -43,9 +43,8 @@ jobs:
cd node_modules/${{github.event.repository.name}}
npm ci
# configures some settings and runs npm run test
- name: Run the backend tests
run: src/tests/frontend/travis/runnerBackend.sh
run: cd src && npm test
##ETHERPAD_NPM_V=1
##ETHERPAD_NPM_V=2
## NPM configuration automatically created using src/bin/plugins/updateAllPluginsScript.sh

View File

@ -6,8 +6,12 @@ process.on('unhandledRejection', (err) => { throw err; });
const fs = require('fs');
const childProcess = require('child_process');
const log4js = require('log4js');
const path = require('path');
const semver = require('semver');
log4js.replaceConsole();
/*
Usage
@ -25,10 +29,80 @@ if (!release) {
throw new Error('No release type included');
}
const cwd = path.join(fs.realpathSync(__dirname), '../../');
process.chdir(cwd);
// Run command capturing stdout. Trailing newlines are stripped (like the shell does).
const runc =
(cmd, opts = {}) => childProcess.execSync(cmd, {encoding: 'utf8', ...opts}).replace(/\n+$/, '');
// Run command without capturing stdout.
const run = (cmd, opts = {}) => childProcess.execSync(cmd, {stdio: 'inherit', ...opts});
const readJson = (filename) => JSON.parse(fs.readFileSync(filename, {encoding: 'utf8', flag: 'r'}));
const writeJson = (filename, obj) => {
let json = JSON.stringify(obj, null, 2);
if (json !== '' && !json.endsWith('\n')) json += '\n';
fs.writeFileSync(filename, json);
};
const assertWorkDirClean = (opts = {}) => {
opts.cwd = runc('git rev-parse --show-cdup', opts) || cwd;
const m = runc('git diff-files --name-status', opts);
if (m !== '') throw new Error(`modifications in working directory ${opts.cwd}:\n${m}`);
const u = runc('git ls-files -o --exclude-standard', opts);
if (u !== '') throw new Error(`untracked files in working directory ${opts.cwd}:\n${u}`);
const s = runc('git diff-index --cached --name-status HEAD', opts);
if (s !== '') throw new Error(`uncommitted changes in working directory ${opts.cwd}:\n${s}`);
};
const assertBranchCheckedOut = (branch, opts = {}) => {
const b = runc('git symbolic-ref HEAD', opts);
if (b !== `refs/heads/${branch}`) {
const d = opts.cwd ? path.resolve(cwd, opts.cwd) : cwd;
throw new Error(`${branch} must be checked out (cwd: ${d})`);
}
};
const assertUpstreamOk = (branch, opts = {}) => {
const upstream = runc(`git rev-parse --symbolic-full-name ${branch}@{u}`, opts);
if (!(new RegExp(`^refs/remotes/[^/]+/${branch}`)).test(upstream)) {
throw new Error(`${branch} should track origin/${branch}; see git branch --set-upstream-to`);
}
try {
run(`git merge-base --is-ancestor ${branch} ${branch}@{u}`);
} catch (err) {
if (err.status !== 1) throw err;
throw new Error(`${branch} is ahead of origin/${branch}; do you need to push?`);
}
};
const dirExists = (dir) => {
try {
return fs.statSync(dir).isDirectory();
} catch (err) {
if (err.code !== 'ENOENT') throw err;
return false;
}
};
// Sanity checks for Etherpad repo.
assertWorkDirClean();
assertBranchCheckedOut('develop');
assertUpstreamOk('develop');
assertUpstreamOk('master');
// Sanity checks for documentation repo.
if (!dirExists('../ether.github.com')) {
throw new Error('please clone documentation repo: ' +
'(cd .. && git clone git@github.com:ether/ether.github.com.git)');
}
assertWorkDirClean({cwd: '../ether.github.com/'});
assertBranchCheckedOut('master', {cwd: '../ether.github.com/'});
assertUpstreamOk('master', {cwd: '../ether.github.com/'});
const changelog = fs.readFileSync('CHANGELOG.md', {encoding: 'utf8', flag: 'r'});
let packageJson = fs.readFileSync('./src/package.json', {encoding: 'utf8', flag: 'r'});
packageJson = JSON.parse(packageJson);
const currentVersion = packageJson.version;
const pkg = readJson('./src/package.json');
const currentVersion = pkg.version;
const newVersion = semver.inc(currentVersion, release);
if (!newVersion) {
@ -36,40 +110,95 @@ if (!newVersion) {
throw new Error('Unable to generate new version from input');
}
const changelogIncludesVersion = changelog.indexOf(newVersion) !== -1;
if (!changelogIncludesVersion) {
if (!changelog.startsWith(`# ${newVersion}\n`)) {
throw new Error(`No changelog record for ${newVersion}, please create changelog record`);
}
console.log('Okay looks good, lets create the package.json and package-lock.json');
// ////////////////////////////////////////////////////////////////////////////////////////////////
// Done with sanity checks, now it's time to make changes.
packageJson.version = newVersion;
try {
console.log('Updating develop branch...');
run('git pull --ff-only');
fs.writeFileSync('src/package.json', JSON.stringify(packageJson, null, 2));
console.log(`Bumping ${release} version (to ${newVersion})...`);
pkg.version = newVersion;
// run npm version `release` where release is patch, minor or major
childProcess.execSync('npm install --package-lock-only', {cwd: 'src/'});
// run npm install --package-lock-only <-- required???
writeJson('./src/package.json', pkg);
childProcess.execSync(`git checkout -b release/${newVersion}`);
childProcess.execSync('git add src/package.json');
childProcess.execSync('git add src/package-lock.json');
childProcess.execSync('git commit -m "bump version"');
childProcess.execSync(`git push origin release/${newVersion}`);
// run npm version `release` where release is patch, minor or major
run('npm install --package-lock-only', {cwd: 'src/'});
// run npm install --package-lock-only <-- required???
// Many users will be using the latest LTS version of npm, and the latest LTS version of npm uses
// lockfileVersion 1. Enforce v1 so that users don't see a (benign) compatibility warning.
if (readJson('./src/package-lock.json').lockfileVersion !== 1) {
throw new Error('Please regenerate package-lock.json with npm v6.x.');
}
childProcess.execSync('make docs');
childProcess.execSync('git clone git@github.com:ether/ether.github.com.git');
childProcess.execSync(`cp -R out/doc/ ether.github.com/doc/v${newVersion}`);
run('git add src/package.json');
run('git add src/package-lock.json');
run('git commit -m "bump version"');
console.log('Switching to master...');
run('git checkout master');
console.log('Updating master branch...');
run('git pull --ff-only');
console.log('Merging develop into master...');
run('git merge --no-ff --no-edit develop');
console.log(`Creating ${newVersion} tag...`);
run(`git tag -s '${newVersion}' -m '${newVersion}'`);
console.log('Switching back to develop...');
run('git checkout develop');
console.log('Merging master into develop...');
run('git merge --no-ff --no-edit master');
} catch (err) {
console.error(err.toString());
console.warn('Resetting repository...');
console.warn('Resetting master...');
run('git checkout -f master');
run('git reset --hard @{u}');
console.warn('Resetting develop...');
run('git checkout -f develop');
run('git reset --hard @{u}');
console.warn(`Deleting ${newVersion} tag...`);
run(`git rev-parse -q --verify refs/tags/'${newVersion}' >/dev/null || exit 0; ` +
`git tag -d '${newVersion}'`);
throw err;
}
console.log('Once merged into master please run the following commands');
console.log(`git tag -a ${newVersion} -m ${newVersion} && git push origin master`);
console.log(`cd ether.github.com && git add . && git commit -m '${newVersion} docs'`);
console.log('Build the windows zip');
try {
console.log('Building documentation...');
run('make docs');
console.log('Updating ether.github.com master branch...');
run('git pull --ff-only', {cwd: '../ether.github.com/'});
console.log('Committing documentation...');
run(`cp -R out/doc/ ../ether.github.com/doc/v'${newVersion}'`);
run(`rm -f latest && ln -s 'v${newVersion}' latest`, {cwd: '../ether.github.com/doc/'});
run('git add .', {cwd: '../ether.github.com/'});
run(`git commit -m '${newVersion} docs'`, {cwd: '../ether.github.com/'});
} catch (err) {
console.error(err.toString());
console.warn('Resetting repository...');
console.warn('Resetting master...');
run('git checkout -f master', {cwd: '../ether.github.com/'});
run('git reset --hard @{u}', {cwd: '../ether.github.com/'});
throw err;
}
console.log('Done.');
console.log('Review the new commits and the new tag:');
console.log(' git log --graph --date-order --boundary --oneline --decorate develop@{u}..develop');
console.log(` git show '${newVersion}'`);
console.log(' (cd ../ether.github.com && git show)');
console.log('If everything looks good then push:');
console.log(` git push origin master develop '${newVersion}'`);
console.log(' (cd ../ether.github.com && git push)');
console.log('Create a Windows build:');
console.log(' bin/buildForWindows.sh');
console.log('Visit https://github.com/ether/etherpad-lite/releases/new and create a new release ' +
`with 'master' as the target and the version is ${newVersion}. Include the windows ` +
'zip as an asset');
console.log(`Once the new docs are uploaded then modify the download
link on etherpad.org and then pull master onto develop`);
console.log('Once the new docs are uploaded then modify the download links (replace ' +
`${currentVersion} with ${newVersion} on etherpad.org and then pull master onto ` +
'develop)');
console.log('Finally go public with an announcement via our comms channels :)');

View File

@ -32,5 +32,4 @@ src/bin/installDeps.sh "$@" || exit 1
# Move to the node folder and start
log "Starting Etherpad..."
SCRIPTPATH=$(pwd -P)
exec node $(compute_node_args) "$SCRIPTPATH/node_modules/ep_etherpad-lite/node/server.js" "$@"
exec node $(compute_node_args) src/node/server.js "$@"

View File

@ -84,7 +84,8 @@
"name": "socketio",
"hooks": {
"expressCloseServer": "ep_etherpad-lite/node/hooks/express/socketio",
"expressCreateServer": "ep_etherpad-lite/node/hooks/express/socketio"
"expressCreateServer": "ep_etherpad-lite/node/hooks/express/socketio",
"socketio": "ep_etherpad-lite/node/handler/PadMessageHandler"
}
},
{

View File

@ -103,20 +103,16 @@ const doImport = async (req, res, padId) => {
// locally wrapped Promise, since form.parse requires a callback
let srcFile = await new Promise((resolve, reject) => {
form.parse(req, (err, fields, files) => {
if (err || files.file === undefined) {
// the upload failed, stop at this point
if (err) {
console.warn(`Uploading Error: ${err.stack}`);
}
if (err != null) {
logger.warn(`Import failed due to form error: ${err.stack || err}`);
// I hate doing indexOf here but I can't see anything to use...
if (err && err.stack && err.stack.indexOf('maxFileSize') !== -1) {
return reject(new ImportError('maxFileSize'));
}
return reject(new ImportError('uploadFailed'));
}
if (!files.file) { // might not be a graceful fix but it works
if (!files.file) {
logger.warn('Import failed because form had no file');
return reject(new ImportError('uploadFailed'));
}
resolve(files.file.path);
@ -140,7 +136,7 @@ const doImport = async (req, res, padId) => {
srcFile = path.join(path.dirname(srcFile), `${path.basename(srcFile, fileEnding)}.txt`);
await fs.rename(oldSrcFile, srcFile);
} else {
console.warn('Not allowing unknown file type to be imported', fileEnding);
logger.warn(`Not allowing unknown file type to be imported: ${fileEnding}`);
throw new ImportError('uploadFailed');
}
}
@ -188,7 +184,7 @@ const doImport = async (req, res, padId) => {
convertor.convertFile(srcFile, destFile, exportExtension, (err) => {
// catch convert errors
if (err) {
console.warn('Converting Error:', err);
logger.warn(`Converting Error: ${err.stack || err}`);
return reject(new ImportError('convertFailed'));
}
resolve();
@ -205,6 +201,7 @@ const doImport = async (req, res, padId) => {
const isAscii = !Array.prototype.some.call(buf, (c) => (c > 240));
if (!isAscii) {
logger.warn('Attempt to import non-ASCII file');
throw new ImportError('uploadFailed');
}
}
@ -230,8 +227,8 @@ const doImport = async (req, res, padId) => {
if (importHandledByPlugin || useConvertor || fileIsHTML) {
try {
await importHtml.setPadHTML(pad, text);
} catch (e) {
logger.warn('Error importing, possibly caused by malformed HTML');
} catch (err) {
logger.warn(`Error importing, possibly caused by malformed HTML: ${err.stack || err}`);
}
} else {
await pad.setText(text);

View File

@ -40,10 +40,14 @@ const nodeify = require('nodeify');
const {RateLimiterMemory} = require('rate-limiter-flexible');
const webaccess = require('../hooks/express/webaccess');
const rateLimiter = new RateLimiterMemory({
points: settings.commitRateLimiting.points,
duration: settings.commitRateLimiting.duration,
});
let rateLimiter;
exports.socketio = () => {
// The rate limiter is created in this hook so that restarting the server resets the limiter. The
// settings.commitRateLimiting object is passed directly to the rate limiter so that the limits
// can be dynamically changed during runtime by modifying its properties.
rateLimiter = new RateLimiterMemory(settings.commitRateLimiting);
};
/**
* A associative array that saves information about a session

View File

@ -10,15 +10,15 @@ const rateLimit = require('express-rate-limit');
const securityManager = require('../../db/SecurityManager');
const webaccess = require('./webaccess');
settings.importExportRateLimiting.onLimitReached = (req, res, options) => {
// when the rate limiter triggers, write a warning in the logs
console.warn('Import/Export rate limiter triggered on ' +
`"${req.originalUrl}" for IP address ${req.ip}`);
};
const limiter = rateLimit(settings.importExportRateLimiting);
exports.expressCreateServer = (hookName, args, cb) => {
settings.importExportRateLimiting.onLimitReached = (req, res, options) => {
// when the rate limiter triggers, write a warning in the logs
console.warn('Import/Export rate limiter triggered on ' +
`"${req.originalUrl}" for IP address ${req.ip}`);
};
// The rate limiter is created in this hook so that restarting the server resets the limiter.
const limiter = rateLimit(settings.importExportRateLimiting);
// handle export requests
args.app.use('/p/:pad/:rev?/export/:type', limiter);
args.app.get('/p/:pad/:rev?/export/:type', (req, res, next) => {

View File

@ -1,6 +1,6 @@
'use strict';
const childProcess = require('child_process');
const spawn = require('cross-spawn');
const log4js = require('log4js');
const path = require('path');
const settings = require('./Settings');
@ -28,14 +28,14 @@ const logLines = (readable, logLineFn) => {
};
/**
* Similar to `util.promisify(childProcess.exec)`, except:
* Similar to `util.promisify(child_rocess.exec)`, except:
* - `cwd` defaults to the Etherpad root directory.
* - PATH is prefixed with src/node_modules/.bin so that utilities from installed dependencies
* (e.g., npm) are preferred over system utilities.
* - Output is passed to logger callback functions by default. See below for details.
*
* @param args Array of command-line arguments, where `args[0]` is the command to run.
* @param opts Optional options that will be passed to `childProcess.spawn()` with two extensions:
* @param opts Optional options that will be passed to `child_process.spawn()` with two extensions:
* - `stdoutLogger`: Callback that is called each time a line of text is written to stdout (utf8
* is assumed). The line (without trailing newline) is passed as the only argument. If null,
* stdout is not logged. If unset, defaults to no-op. Ignored if stdout is not a pipe.
@ -48,7 +48,7 @@ module.exports = exports = (args, opts = {}) => {
logger.debug(`Executing command: ${args.join(' ')}`);
const {stdoutLogger = () => {}, stderrLogger = () => {}} = opts;
// Avoid confusing childProcess.spawn() with our extensions.
// Avoid confusing child_process.spawn() with our extensions.
opts = {...opts}; // Make a copy to avoid mutating the caller's copy.
delete opts.stdoutLogger;
delete opts.stderrLogger;
@ -70,7 +70,7 @@ module.exports = exports = (args, opts = {}) => {
// process's `exit` handler so that we get a useful stack trace.
const procFailedErr = new Error(`Command exited non-zero: ${args.join(' ')}`);
const proc = childProcess.spawn(args[0], args.slice(1), {cwd: settings.root, ...opts, env});
const proc = spawn(args[0], args.slice(1), {cwd: settings.root, ...opts, env});
if (proc.stdout != null && stdoutLogger != null) logLines(proc.stdout, stdoutLogger);
if (proc.stderr != null && stderrLogger != null) logLines(proc.stderr, stderrLogger);
const p = new Promise((resolve, reject) => {

118
src/package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "ep_etherpad-lite",
"version": "1.8.8",
"version": "1.8.9",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -1281,7 +1281,6 @@
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"dev": true,
"requires": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@ -1292,7 +1291,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"requires": {
"isexe": "^2.0.0"
}
@ -2028,9 +2026,9 @@
"integrity": "sha1-7Y8E6f0szsOgBVu20t/p2ZkS5+I="
},
"etherpad-yajsml": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/etherpad-yajsml/-/etherpad-yajsml-0.0.2.tgz",
"integrity": "sha1-HCTSaLCUduY30EnN2xxt+McptG4="
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/etherpad-yajsml/-/etherpad-yajsml-0.0.4.tgz",
"integrity": "sha512-rxpEOMZmv6DOCQeaDo6tztneaKF9ZxbLo/+hQcV+hn0lNrxJZ7MKIPD2pTWWnNLj6gFFfs6QQ67RfMNWIr3fSA=="
},
"event-target-shim": {
"version": "5.0.1",
@ -2082,9 +2080,9 @@
}
},
"express-rate-limit": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.1.1.tgz",
"integrity": "sha512-puA1zcCx/quwWUOU6pT6daCt6t7SweD9wKChKhb+KSgFMKRwS81C224hiSAUANw/gnSHiwEhgozM/2ezEBZPeA=="
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.2.3.tgz",
"integrity": "sha512-cjQH+oDrEPXxc569XvxhHC6QXqJiuBT6BhZ70X3bdAImcnHnTNMVuMAJaT0TXPoRiEErUrVPRcOTpZpM36VbOQ=="
},
"express-session": {
"version": "1.17.1",
@ -2245,9 +2243,9 @@
}
},
"formidable": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz",
"integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg=="
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz",
"integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q=="
},
"forwarded": {
"version": "0.1.2",
@ -3737,9 +3735,9 @@
"dev": true
},
"npm": {
"version": "6.14.8",
"resolved": "https://registry.npmjs.org/npm/-/npm-6.14.8.tgz",
"integrity": "sha512-HBZVBMYs5blsj94GTeQZel7s9odVuuSUHy1+AlZh7rPVux1os2ashvEGLy/STNK7vUjbrCg5Kq9/GXisJgdf6A==",
"version": "6.14.11",
"resolved": "https://registry.npmjs.org/npm/-/npm-6.14.11.tgz",
"integrity": "sha512-1Zh7LjuIoEhIyjkBflSSGzfjuPQwDlghNloppjruOH5bmj9midT9qcNT0tRUZRR04shU9ekrxNy9+UTBrqeBpQ==",
"requires": {
"JSONStream": "^1.3.5",
"abbrev": "~1.1.1",
@ -3778,7 +3776,7 @@
"infer-owner": "^1.0.4",
"inflight": "~1.0.6",
"inherits": "^2.0.4",
"ini": "^1.3.5",
"ini": "^1.3.8",
"init-package-json": "^1.10.3",
"is-cidr": "^3.0.0",
"json-parse-better-errors": "^1.0.2",
@ -3821,10 +3819,10 @@
"npm-pick-manifest": "^3.0.2",
"npm-profile": "^4.0.4",
"npm-registry-fetch": "^4.0.7",
"npm-user-validate": "~1.0.0",
"npm-user-validate": "^1.0.1",
"npmlog": "~4.1.2",
"once": "~1.4.0",
"opener": "^1.5.1",
"opener": "^1.5.2",
"osenv": "^0.1.5",
"pacote": "^9.5.12",
"path-is-inside": "~1.0.2",
@ -3892,16 +3890,6 @@
"humanize-ms": "^1.2.1"
}
},
"ajv": {
"version": "5.5.2",
"bundled": true,
"requires": {
"co": "^4.6.0",
"fast-deep-equal": "^1.0.0",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.3.0"
}
},
"ansi-align": {
"version": "2.0.0",
"bundled": true,
@ -4187,10 +4175,6 @@
"mkdirp": "~0.5.0"
}
},
"co": {
"version": "4.6.0",
"bundled": true
},
"code-point-at": {
"version": "1.1.0",
"bundled": true
@ -4579,10 +4563,6 @@
"version": "1.3.0",
"bundled": true
},
"fast-deep-equal": {
"version": "1.1.0",
"bundled": true
},
"fast-json-stable-stringify": {
"version": "2.0.0",
"bundled": true
@ -4867,11 +4847,31 @@
"bundled": true
},
"har-validator": {
"version": "5.1.0",
"version": "5.1.5",
"bundled": true,
"requires": {
"ajv": "^5.3.0",
"ajv": "^6.12.3",
"har-schema": "^2.0.0"
},
"dependencies": {
"ajv": {
"version": "6.12.6",
"bundled": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"fast-deep-equal": {
"version": "3.1.3",
"bundled": true
},
"json-schema-traverse": {
"version": "0.4.1",
"bundled": true
}
}
},
"has": {
@ -4976,7 +4976,7 @@
"bundled": true
},
"ini": {
"version": "1.3.5",
"version": "1.3.8",
"bundled": true
},
"init-package-json": {
@ -5114,10 +5114,6 @@
"version": "0.2.3",
"bundled": true
},
"json-schema-traverse": {
"version": "0.3.1",
"bundled": true
},
"json-stringify-safe": {
"version": "5.0.1",
"bundled": true
@ -5688,7 +5684,7 @@
}
},
"npm-user-validate": {
"version": "1.0.0",
"version": "1.0.1",
"bundled": true
},
"npmlog": {
@ -5733,7 +5729,7 @@
}
},
"opener": {
"version": "1.5.1",
"version": "1.5.2",
"bundled": true
},
"os-homedir": {
@ -6563,6 +6559,19 @@
"xdg-basedir": "^3.0.0"
}
},
"uri-js": {
"version": "4.4.0",
"bundled": true,
"requires": {
"punycode": "^2.1.0"
},
"dependencies": {
"punycode": {
"version": "2.1.1",
"bundled": true
}
}
},
"url-parse-lax": {
"version": "1.0.0",
"bundled": true,
@ -7086,8 +7095,7 @@
"path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
},
"path-parse": {
"version": "1.0.6",
@ -7536,9 +7544,9 @@
"integrity": "sha1-gRwwAxNoYTPvAAcSXjsO1wCXiBU="
},
"semver": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
"integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg=="
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
},
"send": {
"version": "0.17.1",
@ -7610,7 +7618,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"requires": {
"shebang-regex": "^3.0.0"
}
@ -7618,8 +7625,7 @@
"shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
},
"signal-exit": {
"version": "3.0.3",
@ -8292,9 +8298,9 @@
}
},
"tinycon": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/tinycon/-/tinycon-0.0.1.tgz",
"integrity": "sha1-beEM1SGaHxIdmgokssEbP7JN/+0="
"version": "0.6.8",
"resolved": "https://registry.npmjs.org/tinycon/-/tinycon-0.6.8.tgz",
"integrity": "sha1-59oiPj7gy/nbeWP6M1aZuyF3enM="
},
"to-array": {
"version": "0.1.4",

View File

@ -36,14 +36,15 @@
"cheerio": "0.22.0",
"clean-css": "4.2.3",
"cookie-parser": "1.4.5",
"cross-spawn": "^7.0.3",
"ejs": "^3.1.6",
"etherpad-require-kernel": "1.0.9",
"etherpad-yajsml": "0.0.2",
"etherpad-yajsml": "0.0.4",
"express": "4.17.1",
"express-rate-limit": "5.1.1",
"express-rate-limit": "5.2.3",
"express-session": "1.17.1",
"find-root": "1.1.0",
"formidable": "1.2.1",
"formidable": "1.2.2",
"http-errors": "1.8.0",
"js-cookie": "^2.2.1",
"jsonminify": "0.4.1",
@ -53,7 +54,7 @@
"measured-core": "1.51.1",
"mime-types": "^2.1.27",
"nodeify": "1.0.1",
"npm": "6.14.8",
"npm": "6.14.11",
"openapi-backend": "2.4.1",
"proxy-addr": "^2.0.6",
"rate-limiter-flexible": "^2.1.4",
@ -62,12 +63,12 @@
"request": "2.88.2",
"resolve": "1.19.0",
"security": "1.0.0",
"semver": "5.6.0",
"semver": "5.7.1",
"socket.io": "^2.4.1",
"terser": "^4.7.0",
"threads": "^1.4.0",
"tiny-worker": "^2.3.0",
"tinycon": "0.0.1",
"tinycon": "0.6.8",
"ueberdb2": "^1.2.5",
"underscore": "1.12.0",
"unorm": "1.4.1",
@ -236,6 +237,6 @@
"test": "mocha --timeout 120000 --recursive tests/backend/specs ../node_modules/ep_*/static/tests/backend/specs",
"test-container": "mocha --timeout 5000 tests/container/specs/api"
},
"version": "1.8.8",
"version": "1.8.9",
"license": "Apache-2.0"
}

View File

@ -3197,28 +3197,6 @@ function Ace2Inner() {
scroll.setScrollY(caretOffsetTop);
}, 200);
}
// scroll to viewport when user presses arrow keys and caret is out of the viewport
if ((evt.which === 37 || evt.which === 38 || evt.which === 39 || evt.which === 40)) {
// we use arrowKeyWasReleased to avoid triggering the animation when a key
// is continuously pressed
// this makes the scroll smooth
if (!continuouslyPressingArrowKey(type)) {
// the caret position is not synchronized with the rep.
// For example, when an user presses arrow
// We use getSelection() instead of rep to get the caret position.
// This avoids errors like when down to scroll the pad without releasing the key.
// When the key is released the rep is not
// synchronized, so we don't get the right node where caret is.
const selection = getSelection();
if (selection) {
const arrowUp = evt.which === 38;
const innerHeight = getInnerHeight();
scroll.scrollWhenPressArrowKeys(arrowUp, rep, innerHeight);
}
}
}
}
if (type === 'keydown') {
@ -3263,19 +3241,6 @@ function Ace2Inner() {
};
let thisKeyDoesntTriggerNormalize = false;
let arrowKeyWasReleased = true;
const continuouslyPressingArrowKey = (type) => {
let firstTimeKeyIsContinuouslyPressed = false;
if (type === 'keyup') {
arrowKeyWasReleased = true;
} else if (type === 'keydown' && arrowKeyWasReleased) {
firstTimeKeyIsContinuouslyPressed = true;
arrowKeyWasReleased = false;
}
return !firstTimeKeyIsContinuouslyPressed;
};
const doUndoRedo = (which) => {
// precond: normalized DOM

View File

@ -42,7 +42,7 @@ const padimpexp = (() => {
$('#importmessagefail').fadeOut('fast');
};
const fileInputSubmit = (e) => {
const fileInputSubmit = function (e) {
e.preventDefault();
$('#importmessagefail').fadeOut('fast');
if (!window.confirm(html10n.get('pad.impexp.confirmimport'))) return;

View File

@ -45,6 +45,8 @@ exports.init = async function () {
// Start the Etherpad server on a random unused port.
settings.port = 0;
settings.ip = 'localhost';
settings.importExportRateLimiting = {max: 0};
settings.commitRateLimiting = {duration: 0.001, points: 1e6};
exports.httpServer = await server.start();
exports.baseUrl = `http://localhost:${exports.httpServer.address().port}`;
exports.logger.debug(`HTTP server at ${exports.baseUrl}`);

View File

@ -8,19 +8,21 @@
const common = require('../../common');
const fs = require('fs');
const settings = require('../../../../node/utils/Settings');
const supertest = require('supertest');
const api = supertest(`http://${settings.ip}:${settings.port}`);
let agent;
const apiKey = common.apiKey;
let apiVersion = 1;
const testPadId = makeid();
const endPoint = (point, version) => `/api/${version || apiVersion}/${point}?apikey=${apiKey}`;
describe(__filename, function () {
before(async function () { agent = await common.init(); });
describe('Connectivity For Character Encoding', function () {
it('can connect', function (done) {
this.timeout(250);
api.get('/api/')
agent.get('/api/')
.expect('Content-Type', /json/)
.expect(200, done);
});
@ -29,7 +31,7 @@ describe(__filename, function () {
describe('API Versioning', function () {
this.timeout(150);
it('finds the version tag', function (done) {
api.get('/api/')
agent.get('/api/')
.expect((res) => {
apiVersion = res.body.currentVersion;
if (!res.body.currentVersion) throw new Error('No version set in API');
@ -45,7 +47,7 @@ describe(__filename, function () {
// 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
const permErrorURL = `/api/${apiVersion}/createPad?apikey=password&padID=test`;
api.get(permErrorURL)
agent.get(permErrorURL)
.expect(401, done);
});
});
@ -53,7 +55,7 @@ describe(__filename, function () {
describe('createPad', function () {
it('creates a new Pad', function (done) {
this.timeout(150);
api.get(`${endPoint('createPad')}&padID=${testPadId}`)
agent.get(`${endPoint('createPad')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Unable to create new Pad');
})
@ -66,7 +68,7 @@ describe(__filename, function () {
it('Sets the HTML of a Pad attempting to weird utf8 encoded content', function (done) {
this.timeout(1000);
fs.readFile('tests/backend/specs/api/emojis.html', 'utf8', (err, html) => {
api.post(endPoint('setHTML'))
agent.post(endPoint('setHTML'))
.send({
padID: testPadId,
html,
@ -83,7 +85,7 @@ describe(__filename, function () {
describe('getHTML', function () {
it('get the HTML of Pad with emojis', function (done) {
this.timeout(400);
api.get(`${endPoint('getHTML')}&padID=${testPadId}`)
agent.get(`${endPoint('getHTML')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.data.html.indexOf('&#127484') === -1) {
throw new Error('Unable to get the HTML');
@ -101,11 +103,6 @@ describe(__filename, function () {
*/
var endPoint = function (point, version) {
version = version || apiVersion;
return `/api/${version}/${point}?apikey=${apiKey}`;
};
function makeid() {
let text = '';
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

View File

@ -1,18 +1,22 @@
const common = require('../../common');
const settings = require('../../../../node/utils/Settings');
const supertest = require('supertest');
'use strict';
const api = supertest(`http://${settings.ip}:${settings.port}`);
const common = require('../../common');
let agent;
const apiKey = common.apiKey;
let apiVersion = 1;
let authorID = '';
const padID = makeid();
const timestamp = Date.now();
const endPoint = (point) => `/api/${apiVersion}/${point}?apikey=${apiKey}`;
describe(__filename, function () {
before(async function () { agent = await common.init(); });
describe('API Versioning', function () {
it('errors if can not connect', function (done) {
api.get('/api/')
agent.get('/api/')
.expect((res) => {
apiVersion = res.body.currentVersion;
if (!res.body.currentVersion) throw new Error('No version set in API');
@ -37,7 +41,7 @@ describe(__filename, function () {
describe('createPad', function () {
this.timeout(400);
it('creates a new Pad', function (done) {
api.get(`${endPoint('createPad')}&padID=${padID}`)
agent.get(`${endPoint('createPad')}&padID=${padID}`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Unable to create new Pad');
})
@ -49,9 +53,11 @@ describe(__filename, function () {
describe('createAuthor', function () {
this.timeout(100);
it('Creates an author with a name set', function (done) {
api.get(endPoint('createAuthor'))
agent.get(endPoint('createAuthor'))
.expect((res) => {
if (res.body.code !== 0 || !res.body.data.authorID) throw new Error('Unable to create author');
if (res.body.code !== 0 || !res.body.data.authorID) {
throw new Error('Unable to create author');
}
authorID = res.body.data.authorID; // we will be this author for the rest of the tests
})
.expect('Content-Type', /json/)
@ -62,7 +68,8 @@ describe(__filename, function () {
describe('appendChatMessage', function () {
this.timeout(100);
it('Adds a chat message to the pad', function (done) {
api.get(`${endPoint('appendChatMessage')}&padID=${padID}&text=blalblalbha&authorID=${authorID}&time=${timestamp}`)
agent.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');
})
@ -75,7 +82,7 @@ describe(__filename, function () {
describe('getChatHead', function () {
this.timeout(100);
it('Gets the head of chat', function (done) {
api.get(`${endPoint('getChatHead')}&padID=${padID}`)
agent.get(`${endPoint('getChatHead')}&padID=${padID}`)
.expect((res) => {
if (res.body.data.chatHead !== 0) throw new Error('Chat Head Length is wrong');
@ -89,9 +96,11 @@ describe(__filename, function () {
describe('getChatHistory', function () {
this.timeout(40);
it('Gets Chat History of a Pad', function (done) {
api.get(`${endPoint('getChatHistory')}&padID=${padID}`)
agent.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.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/)
@ -100,10 +109,6 @@ describe(__filename, function () {
});
});
var endPoint = function (point) {
return `/api/${apiVersion}/${point}?apikey=${apiKey}`;
};
function makeid() {
let text = '';
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

View File

@ -7,13 +7,13 @@
*/
const common = require('../../common');
const settings = require('../../../container/loadSettings.js').loadSettings();
const supertest = require('supertest');
const api = supertest(`http://${settings.ip}:${settings.port}`);
let agent;
const apiKey = common.apiKey;
const apiVersion = 1;
const endPoint = (point, version) => `/api/${version || apiVersion}/${point}?apikey=${apiKey}`;
const testImports = {
'malformed': {
input: '<html><body><li>wtf</ul></body></html>',
@ -226,6 +226,8 @@ const testImports = {
};
describe(__filename, function () {
before(async function () { agent = await common.init(); });
Object.keys(testImports).forEach((testName) => {
describe(testName, function () {
const testPadId = makeid();
@ -237,7 +239,7 @@ describe(__filename, function () {
}
it('createPad', function (done) {
this.timeout(200);
api.get(`${endPoint('createPad')}&padID=${testPadId}`)
agent.get(`${endPoint('createPad')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Unable to create new Pad');
})
@ -247,7 +249,8 @@ describe(__filename, function () {
it('setHTML', function (done) {
this.timeout(150);
api.get(`${endPoint('setHTML')}&padID=${testPadId}&html=${encodeURIComponent(test.input)}`)
agent.get(`${endPoint('setHTML')}&padID=${testPadId}` +
`&html=${encodeURIComponent(test.input)}`)
.expect((res) => {
if (res.body.code !== 0) throw new Error(`Error:${testName}`);
})
@ -257,7 +260,7 @@ describe(__filename, function () {
it('getHTML', function (done) {
this.timeout(150);
api.get(`${endPoint('getHTML')}&padID=${testPadId}`)
agent.get(`${endPoint('getHTML')}&padID=${testPadId}`)
.expect((res) => {
const gotHtml = res.body.data.html;
if (gotHtml !== test.wantHTML) {
@ -281,7 +284,7 @@ describe(__filename, function () {
it('getText', function (done) {
this.timeout(100);
api.get(`${endPoint('getText')}&padID=${testPadId}`)
agent.get(`${endPoint('getText')}&padID=${testPadId}`)
.expect((res) => {
const gotText = res.body.data.text;
if (gotText !== test.wantText) {
@ -306,12 +309,6 @@ describe(__filename, function () {
});
});
function endPoint(point, version) {
version = version || apiVersion;
return `/api/${version}/${point}?apikey=${apiKey}`;
}
function makeid() {
let text = '';
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

View File

@ -127,7 +127,7 @@ describe(__filename, function () {
describe('Import/Export tests requiring AbiWord/LibreOffice', function () {
this.timeout(60000);
this.timeout(10000);
before(async function () {
if ((!settings.abiword || settings.abiword.indexOf('/') === -1) &&
@ -139,7 +139,6 @@ 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 () {
this.timeout(10000);
await agent.post(`/p/${testPadId}/import`)
.attach('file', wordDoc, {filename: '/test.doc', contentType: 'application/msword'})
.expect(200)
@ -152,7 +151,6 @@ describe(__filename, function () {
});
it('exports DOC', async function () {
this.timeout(3000);
await agent.get(`/p/${testPadId}/export/doc`)
.buffer(true).parse(superagent.parse['application/octet-stream'])
.expect(200)
@ -160,7 +158,6 @@ describe(__filename, function () {
});
it('Tries to import .docx that uses soffice or abiword', async function () {
this.timeout(3000);
await agent.post(`/p/${testPadId}/import`)
.attach('file', wordXDoc, {
filename: '/test.docx',
@ -177,7 +174,6 @@ describe(__filename, function () {
});
it('exports DOC from imported DOCX', async function () {
this.timeout(3000);
await agent.get(`/p/${testPadId}/export/doc`)
.buffer(true).parse(superagent.parse['application/octet-stream'])
.expect(200)
@ -185,7 +181,6 @@ describe(__filename, function () {
});
it('Tries to import .pdf that uses soffice or abiword', async function () {
this.timeout(3000);
await agent.post(`/p/${testPadId}/import`)
.attach('file', pdfDoc, {filename: '/test.pdf', contentType: 'application/pdf'})
.expect(200)
@ -198,7 +193,6 @@ describe(__filename, function () {
});
it('exports PDF', async function () {
this.timeout(3000);
await agent.get(`/p/${testPadId}/export/pdf`)
.buffer(true).parse(superagent.parse['application/octet-stream'])
.expect(200)
@ -206,7 +200,6 @@ describe(__filename, function () {
});
it('Tries to import .odt that uses soffice or abiword', async function () {
this.timeout(3000);
await agent.post(`/p/${testPadId}/import`)
.attach('file', odtDoc, {filename: '/test.odt', contentType: 'application/odt'})
.expect(200)
@ -219,7 +212,6 @@ describe(__filename, function () {
});
it('exports ODT', async function () {
this.timeout(3000);
await agent.get(`/p/${testPadId}/export/odt`)
.buffer(true).parse(superagent.parse['application/octet-stream'])
.expect(200)

View File

@ -1,22 +1,25 @@
'use strict';
/*
* Tests for the instance-level APIs
*
* Section "GLOBAL FUNCTIONS" in src/node/db/API.js
*/
const common = require('../../common');
const settings = require('../../../../node/utils/Settings');
const supertest = require('supertest');
const api = supertest(`http://${settings.ip}:${settings.port}`);
let agent;
const apiKey = common.apiKey;
const apiVersion = '1.2.14';
const endPoint = (point, version) => `/api/${version || apiVersion}/${point}?apikey=${apiKey}`;
describe(__filename, function () {
before(async function () { agent = await common.init(); });
describe('Connectivity for instance-level API tests', function () {
it('can connect', function (done) {
this.timeout(150);
api.get('/api/')
agent.get('/api/')
.expect('Content-Type', /json/)
.expect(200, done);
});
@ -25,20 +28,25 @@ describe(__filename, function () {
describe('getStats', function () {
it('Gets the stats of a running instance', function (done) {
this.timeout(100);
api.get(endPoint('getStats'))
agent.get(endPoint('getStats'))
.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)}`);
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 (!('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)}`);
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/)
@ -46,8 +54,3 @@ describe(__filename, function () {
});
});
});
var endPoint = function (point, version) {
version = version || apiVersion;
return `/api/${version}/${point}?apikey=${apiKey}`;
};

View File

@ -1,3 +1,5 @@
'use strict';
/*
* ACHTUNG: there is a copied & modified version of this file in
* <basedir>/src/tests/container/specs/api/pad.js
@ -5,19 +7,19 @@
* TODO: unify those two files, and merge in a single one.
*/
const assert = require('assert').strict;
const async = require('async');
const common = require('../../common');
const settings = require('../../../../node/utils/Settings');
const supertest = require('supertest');
const api = supertest(`http://${settings.ip}:${settings.port}`);
let agent;
const apiKey = common.apiKey;
let apiVersion = 1;
const testPadId = makeid();
let lastEdited = '';
const text = generateLongText();
const endPoint = (point, version) => `/api/${version || apiVersion}/${point}?apikey=${apiKey}`;
/*
* Html document with nested lists of different types, to test its import and
* verify it is exported back correctly
@ -45,10 +47,12 @@ const ulSpaceHtml = '<!doctype html><html><body><ul class="bullet"> <li>one</li>
const expectedSpaceHtml = '<!doctype html><html><body><ul class="bullet"><li>one</ul></body></html>';
describe(__filename, function () {
before(async function () { agent = await common.init(); });
describe('Connectivity', function () {
it('can connect', function (done) {
this.timeout(200);
api.get('/api/')
agent.get('/api/')
.expect('Content-Type', /json/)
.expect(200, done);
});
@ -57,7 +61,7 @@ describe(__filename, function () {
describe('API Versioning', function () {
it('finds the version tag', function (done) {
this.timeout(150);
api.get('/api/')
agent.get('/api/')
.expect((res) => {
apiVersion = res.body.currentVersion;
if (!res.body.currentVersion) throw new Error('No version set in API');
@ -73,7 +77,7 @@ describe(__filename, function () {
// 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
const permErrorURL = `/api/${apiVersion}/createPad?apikey=password&padID=test`;
api.get(permErrorURL)
agent.get(permErrorURL)
.expect(401, done);
});
});
@ -123,7 +127,7 @@ describe(__filename, function () {
describe('deletePad', function () {
it('deletes a Pad', function (done) {
this.timeout(150);
api.get(`${endPoint('deletePad')}&padID=${testPadId}`)
agent.get(`${endPoint('deletePad')}&padID=${testPadId}`)
.expect('Content-Type', /json/)
.expect(200, done); // @TODO: we shouldn't expect 200 here since the pad may not exist
});
@ -132,7 +136,7 @@ describe(__filename, function () {
describe('createPad', function () {
it('creates a new Pad', function (done) {
this.timeout(150);
api.get(`${endPoint('createPad')}&padID=${testPadId}`)
agent.get(`${endPoint('createPad')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Unable to create new Pad');
})
@ -144,7 +148,7 @@ describe(__filename, function () {
describe('getRevisionsCount', function () {
it('gets revision count of Pad', function (done) {
this.timeout(150);
api.get(`${endPoint('getRevisionsCount')}&padID=${testPadId}`)
agent.get(`${endPoint('getRevisionsCount')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Unable to get Revision Count');
if (res.body.data.revisions !== 0) throw new Error('Incorrect Revision Count');
@ -157,10 +161,12 @@ describe(__filename, function () {
describe('getSavedRevisionsCount', function () {
it('gets saved revisions count of Pad', function (done) {
this.timeout(150);
api.get(`${endPoint('getSavedRevisionsCount')}&padID=${testPadId}`)
agent.get(`${endPoint('getSavedRevisionsCount')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Unable to get Saved Revisions Count');
if (res.body.data.savedRevisions !== 0) throw new Error('Incorrect Saved Revisions Count');
if (res.body.data.savedRevisions !== 0) {
throw new Error('Incorrect Saved Revisions Count');
}
})
.expect('Content-Type', /json/)
.expect(200, done);
@ -170,10 +176,10 @@ describe(__filename, function () {
describe('listSavedRevisions', function () {
it('gets saved revision list of Pad', function (done) {
this.timeout(150);
api.get(`${endPoint('listSavedRevisions')}&padID=${testPadId}`)
agent.get(`${endPoint('listSavedRevisions')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Unable to get Saved Revisions List');
if (!res.body.data.savedRevisions.equals([])) throw new Error('Incorrect Saved Revisions List');
assert.deepEqual(res.body.data.savedRevisions, []);
})
.expect('Content-Type', /json/)
.expect(200, done);
@ -183,7 +189,7 @@ describe(__filename, function () {
describe('getHTML', function () {
it('get the HTML of Pad', function (done) {
this.timeout(150);
api.get(`${endPoint('getHTML')}&padID=${testPadId}`)
agent.get(`${endPoint('getHTML')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.data.html.length <= 1) throw new Error('Unable to get the HTML');
})
@ -195,7 +201,7 @@ describe(__filename, function () {
describe('listAllPads', function () {
it('list all pads', function (done) {
this.timeout(150);
api.get(endPoint('listAllPads'))
agent.get(endPoint('listAllPads'))
.expect((res) => {
if (res.body.data.padIDs.includes(testPadId) !== true) {
throw new Error('Unable to find pad in pad list');
@ -209,7 +215,7 @@ describe(__filename, function () {
describe('deletePad', function () {
it('deletes a Pad', function (done) {
this.timeout(150);
api.get(`${endPoint('deletePad')}&padID=${testPadId}`)
agent.get(`${endPoint('deletePad')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Pad Deletion failed');
})
@ -221,7 +227,7 @@ describe(__filename, function () {
describe('listAllPads', function () {
it('list all pads', function (done) {
this.timeout(150);
api.get(endPoint('listAllPads'))
agent.get(endPoint('listAllPads'))
.expect((res) => {
if (res.body.data.padIDs.includes(testPadId) !== false) {
throw new Error('Test pad should not be in pads list');
@ -235,7 +241,7 @@ describe(__filename, function () {
describe('getHTML', function () {
it('get the HTML of a Pad -- Should return a failure', function (done) {
this.timeout(150);
api.get(`${endPoint('getHTML')}&padID=${testPadId}`)
agent.get(`${endPoint('getHTML')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.code !== 1) throw new Error('Pad deletion failed');
})
@ -247,7 +253,7 @@ describe(__filename, function () {
describe('createPad', function () {
it('creates a new Pad with text', function (done) {
this.timeout(200);
api.get(`${endPoint('createPad')}&padID=${testPadId}&text=testText`)
agent.get(`${endPoint('createPad')}&padID=${testPadId}&text=testText`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Pad Creation failed');
})
@ -259,7 +265,7 @@ describe(__filename, function () {
describe('getText', function () {
it('gets the Pad text and expect it to be testText with \n which is a line break', function (done) {
this.timeout(150);
api.get(`${endPoint('getText')}&padID=${testPadId}`)
agent.get(`${endPoint('getText')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.data.text !== 'testText\n') throw new Error('Pad Creation with text');
})
@ -271,7 +277,7 @@ describe(__filename, function () {
describe('setText', function () {
it('creates a new Pad with text', function (done) {
this.timeout(200);
api.post(endPoint('setText'))
agent.post(endPoint('setText'))
.send({
padID: testPadId,
text: 'testTextTwo',
@ -287,7 +293,7 @@ describe(__filename, function () {
describe('getText', function () {
it('gets the Pad text', function (done) {
this.timeout(150);
api.get(`${endPoint('getText')}&padID=${testPadId}`)
agent.get(`${endPoint('getText')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.data.text !== 'testTextTwo\n') throw new Error('Setting Text');
})
@ -299,7 +305,7 @@ describe(__filename, function () {
describe('getRevisionsCount', function () {
it('gets Revision Count of a Pad', function (done) {
this.timeout(150);
api.get(`${endPoint('getRevisionsCount')}&padID=${testPadId}`)
agent.get(`${endPoint('getRevisionsCount')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.data.revisions !== 1) throw new Error('Unable to get text revision count');
})
@ -311,7 +317,7 @@ describe(__filename, function () {
describe('saveRevision', function () {
it('saves Revision', function (done) {
this.timeout(150);
api.get(`${endPoint('saveRevision')}&padID=${testPadId}`)
agent.get(`${endPoint('saveRevision')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Unable to save Revision');
})
@ -323,10 +329,12 @@ describe(__filename, function () {
describe('getSavedRevisionsCount', function () {
it('gets saved revisions count of Pad', function (done) {
this.timeout(150);
api.get(`${endPoint('getSavedRevisionsCount')}&padID=${testPadId}`)
agent.get(`${endPoint('getSavedRevisionsCount')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Unable to get Saved Revisions Count');
if (res.body.data.savedRevisions !== 1) throw new Error('Incorrect Saved Revisions Count');
if (res.body.data.savedRevisions !== 1) {
throw new Error('Incorrect Saved Revisions Count');
}
})
.expect('Content-Type', /json/)
.expect(200, done);
@ -336,10 +344,10 @@ describe(__filename, function () {
describe('listSavedRevisions', function () {
it('gets saved revision list of Pad', function (done) {
this.timeout(150);
api.get(`${endPoint('listSavedRevisions')}&padID=${testPadId}`)
agent.get(`${endPoint('listSavedRevisions')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Unable to get Saved Revisions List');
if (!res.body.data.savedRevisions.equals([1])) throw new Error('Incorrect Saved Revisions List');
assert.deepEqual(res.body.data.savedRevisions, [1]);
})
.expect('Content-Type', /json/)
.expect(200, done);
@ -348,7 +356,7 @@ describe(__filename, function () {
describe('padUsersCount', function () {
it('gets User Count of a Pad', function (done) {
this.timeout(150);
api.get(`${endPoint('padUsersCount')}&padID=${testPadId}`)
agent.get(`${endPoint('padUsersCount')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.data.padUsersCount !== 0) throw new Error('Incorrect Pad User count');
})
@ -360,7 +368,7 @@ describe(__filename, function () {
describe('getReadOnlyID', function () {
it('Gets the Read Only ID of a Pad', function (done) {
this.timeout(150);
api.get(`${endPoint('getReadOnlyID')}&padID=${testPadId}`)
agent.get(`${endPoint('getReadOnlyID')}&padID=${testPadId}`)
.expect((res) => {
if (!res.body.data.readOnlyID) throw new Error('No Read Only ID for Pad');
})
@ -372,9 +380,11 @@ describe(__filename, function () {
describe('listAuthorsOfPad', function () {
it('Get Authors of the Pad', function (done) {
this.timeout(150);
api.get(`${endPoint('listAuthorsOfPad')}&padID=${testPadId}`)
agent.get(`${endPoint('listAuthorsOfPad')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.data.authorIDs.length !== 0) throw new Error('# of Authors of pad is not 0');
if (res.body.data.authorIDs.length !== 0) {
throw new Error('# of Authors of pad is not 0');
}
})
.expect('Content-Type', /json/)
.expect(200, done);
@ -384,7 +394,7 @@ describe(__filename, function () {
describe('getLastEdited', function () {
it('Get When Pad was left Edited', function (done) {
this.timeout(150);
api.get(`${endPoint('getLastEdited')}&padID=${testPadId}`)
agent.get(`${endPoint('getLastEdited')}&padID=${testPadId}`)
.expect((res) => {
if (!res.body.data.lastEdited) {
throw new Error('# of Authors of pad is not 0');
@ -400,7 +410,7 @@ describe(__filename, function () {
describe('setText', function () {
it('creates a new Pad with text', function (done) {
this.timeout(200);
api.post(endPoint('setText'))
agent.post(endPoint('setText'))
.send({
padID: testPadId,
text: 'testTextTwo',
@ -416,7 +426,7 @@ describe(__filename, function () {
describe('getLastEdited', function () {
it('Get When Pad was left Edited', function (done) {
this.timeout(150);
api.get(`${endPoint('getLastEdited')}&padID=${testPadId}`)
agent.get(`${endPoint('getLastEdited')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.data.lastEdited <= lastEdited) {
throw new Error('Editing A Pad is not updating when it was last edited');
@ -430,7 +440,7 @@ describe(__filename, function () {
describe('padUsers', function () {
it('gets User Count of a Pad', function (done) {
this.timeout(150);
api.get(`${endPoint('padUsers')}&padID=${testPadId}`)
agent.get(`${endPoint('padUsers')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.data.padUsers.length !== 0) throw new Error('Incorrect Pad Users');
})
@ -442,7 +452,7 @@ describe(__filename, function () {
describe('deletePad', function () {
it('deletes a Pad', function (done) {
this.timeout(150);
api.get(`${endPoint('deletePad')}&padID=${testPadId}`)
agent.get(`${endPoint('deletePad')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Pad Deletion failed');
})
@ -451,14 +461,13 @@ describe(__filename, function () {
});
});
const originalPadId = testPadId;
const newPadId = makeid();
const copiedPadId = makeid();
describe('createPad', function () {
it('creates a new Pad with text', function (done) {
this.timeout(200);
api.get(`${endPoint('createPad')}&padID=${testPadId}`)
agent.get(`${endPoint('createPad')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Pad Creation failed');
})
@ -470,7 +479,7 @@ describe(__filename, function () {
describe('setText', function () {
it('Sets text on a pad Id', function (done) {
this.timeout(150);
api.post(`${endPoint('setText')}&padID=${testPadId}`)
agent.post(`${endPoint('setText')}&padID=${testPadId}`)
.field({text})
.expect((res) => {
if (res.body.code !== 0) throw new Error('Pad Set Text failed');
@ -483,7 +492,7 @@ describe(__filename, function () {
describe('getText', function () {
it('Gets text on a pad Id', function (done) {
this.timeout(200);
api.get(`${endPoint('getText')}&padID=${testPadId}`)
agent.get(`${endPoint('getText')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Pad Get Text failed');
if (res.body.data.text !== `${text}\n`) throw new Error('Pad Text not set properly');
@ -496,7 +505,7 @@ describe(__filename, function () {
describe('setText', function () {
it('Sets text on a pad Id including an explicit newline', function (done) {
this.timeout(200);
api.post(`${endPoint('setText')}&padID=${testPadId}`)
agent.post(`${endPoint('setText')}&padID=${testPadId}`)
.field({text: `${text}\n`})
.expect((res) => {
if (res.body.code !== 0) throw new Error('Pad Set Text failed');
@ -509,7 +518,7 @@ describe(__filename, function () {
describe('getText', function () {
it("Gets text on a pad Id and doesn't have an excess newline", function (done) {
this.timeout(150);
api.get(`${endPoint('getText')}&padID=${testPadId}`)
agent.get(`${endPoint('getText')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Pad Get Text failed');
if (res.body.data.text !== `${text}\n`) throw new Error('Pad Text not set properly');
@ -522,7 +531,7 @@ describe(__filename, function () {
describe('getLastEdited', function () {
it('Gets when pad was last edited', function (done) {
this.timeout(150);
api.get(`${endPoint('getLastEdited')}&padID=${testPadId}`)
agent.get(`${endPoint('getLastEdited')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.lastEdited === 0) throw new Error('Get Last Edited Failed');
})
@ -534,7 +543,7 @@ describe(__filename, function () {
describe('movePad', function () {
it('Move a Pad to a different Pad ID', function (done) {
this.timeout(200);
api.get(`${endPoint('movePad')}&sourceID=${testPadId}&destinationID=${newPadId}&force=true`)
agent.get(`${endPoint('movePad')}&sourceID=${testPadId}&destinationID=${newPadId}&force=true`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Moving Pad Failed');
})
@ -546,7 +555,7 @@ describe(__filename, function () {
describe('getText', function () {
it('Gets text on a pad Id', function (done) {
this.timeout(150);
api.get(`${endPoint('getText')}&padID=${newPadId}`)
agent.get(`${endPoint('getText')}&padID=${newPadId}`)
.expect((res) => {
if (res.body.data.text !== `${text}\n`) throw new Error('Pad Get Text failed');
})
@ -558,7 +567,8 @@ describe(__filename, function () {
describe('movePad', function () {
it('Move a Pad to a different Pad ID', function (done) {
this.timeout(200);
api.get(`${endPoint('movePad')}&sourceID=${newPadId}&destinationID=${testPadId}&force=false`)
agent.get(`${endPoint('movePad')}&sourceID=${newPadId}&destinationID=${testPadId}` +
'&force=false')
.expect((res) => {
if (res.body.code !== 0) throw new Error('Moving Pad Failed');
})
@ -570,7 +580,7 @@ describe(__filename, function () {
describe('getText', function () {
it('Gets text on a pad Id', function (done) {
this.timeout(150);
api.get(`${endPoint('getText')}&padID=${testPadId}`)
agent.get(`${endPoint('getText')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.data.text !== `${text}\n`) throw new Error('Pad Get Text failed');
})
@ -582,7 +592,7 @@ describe(__filename, function () {
describe('getLastEdited', function () {
it('Gets when pad was last edited', function (done) {
this.timeout(150);
api.get(`${endPoint('getLastEdited')}&padID=${testPadId}`)
agent.get(`${endPoint('getLastEdited')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.lastEdited === 0) throw new Error('Get Last Edited Failed');
})
@ -594,7 +604,7 @@ describe(__filename, function () {
describe('appendText', function () {
it('Append text to a pad Id', function (done) {
this.timeout(150);
api.get(`${endPoint('appendText', '1.2.13')}&padID=${testPadId}&text=hello`)
agent.get(`${endPoint('appendText', '1.2.13')}&padID=${testPadId}&text=hello`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Pad Append Text failed');
})
@ -606,10 +616,12 @@ describe(__filename, function () {
describe('getText', function () {
it('Gets text on a pad Id', function (done) {
this.timeout(150);
api.get(`${endPoint('getText')}&padID=${testPadId}`)
agent.get(`${endPoint('getText')}&padID=${testPadId}`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Pad Get Text failed');
if (res.body.data.text !== `${text}hello\n`) throw new Error('Pad Text not set properly');
if (res.body.data.text !== `${text}hello\n`) {
throw new Error('Pad Text not set properly');
}
})
.expect('Content-Type', /json/)
.expect(200, done);
@ -621,13 +633,15 @@ describe(__filename, function () {
it('Sets the HTML of a Pad attempting to pass ugly HTML', function (done) {
this.timeout(200);
const html = '<div><b>Hello HTML</title></head></div>';
api.post(endPoint('setHTML'))
agent.post(endPoint('setHTML'))
.send({
padID: testPadId,
html,
})
.expect((res) => {
if (res.body.code !== 0) throw new Error("Crappy HTML Can't be Imported[we weren't able to sanitize it']");
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);
@ -637,7 +651,7 @@ describe(__filename, function () {
describe('setHTML', function () {
it('Sets the HTML of a Pad with complex nested lists of different types', function (done) {
this.timeout(200);
api.post(endPoint('setHTML'))
agent.post(endPoint('setHTML'))
.send({
padID: testPadId,
html: ulHtml,
@ -653,7 +667,7 @@ describe(__filename, function () {
describe('getHTML', function () {
it('Gets back the HTML of a Pad with complex nested lists of different types', function (done) {
this.timeout(150);
api.get(`${endPoint('getHTML')}&padID=${testPadId}`)
agent.get(`${endPoint('getHTML')}&padID=${testPadId}`)
.expect((res) => {
const receivedHtml = res.body.data.html.replace('<br></body>', '</body>').toLowerCase();
@ -677,7 +691,7 @@ describe(__filename, function () {
describe('setHTML', function () {
it('Sets the HTML of a Pad with white space between list items', function (done) {
this.timeout(200);
api.get(`${endPoint('setHTML')}&padID=${testPadId}&html=${ulSpaceHtml}`)
agent.get(`${endPoint('setHTML')}&padID=${testPadId}&html=${ulSpaceHtml}`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('List HTML cant be imported');
})
@ -689,7 +703,7 @@ describe(__filename, function () {
describe('getHTML', function () {
it('Gets back the HTML of a Pad with complex nested lists of different types', function (done) {
this.timeout(150);
api.get(`${endPoint('getHTML')}&padID=${testPadId}`)
agent.get(`${endPoint('getHTML')}&padID=${testPadId}`)
.expect((res) => {
const receivedHtml = res.body.data.html.replace('<br></body>', '</body>').toLowerCase();
if (receivedHtml !== expectedSpaceHtml) {
@ -716,7 +730,7 @@ describe(__filename, function () {
async.map(
badUrlChars,
(badUrlChar, cb) => {
api.get(`${endPoint('createPad')}&padID=${badUrlChar}`)
agent.get(`${endPoint('createPad')}&padID=${badUrlChar}`)
.expect((res) => {
if (res.body.code !== 1) throw new Error('Pad with bad characters was created');
})
@ -730,7 +744,8 @@ describe(__filename, function () {
describe('copyPad', function () {
it('copies the content of a existent pad', function (done) {
this.timeout(200);
api.get(`${endPoint('copyPad')}&sourceID=${testPadId}&destinationID=${copiedPadId}&force=true`)
agent.get(`${endPoint('copyPad')}&sourceID=${testPadId}&destinationID=${copiedPadId}` +
'&force=true')
.expect((res) => {
if (res.body.code !== 0) throw new Error('Copy Pad Failed');
})
@ -747,13 +762,14 @@ describe(__filename, function () {
createNewPadWithHtml(sourcePadId, ulHtml, done);
});
beforeEach(function () {
beforeEach(async function () {
newPad = makeid();
});
it('returns a successful response', function (done) {
this.timeout(200);
api.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}&destinationID=${newPad}&force=false`)
agent.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}` +
`&destinationID=${newPad}&force=false`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Copy Pad Without History Failed');
})
@ -764,14 +780,16 @@ describe(__filename, function () {
// this test validates if the source pad's text and attributes are kept
it('creates a new pad with the same content as the source pad', function (done) {
this.timeout(200);
api.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}&destinationID=${newPad}&force=false`)
agent.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}` +
`&destinationID=${newPad}&force=false`)
.expect((res) => {
if (res.body.code !== 0) throw new Error('Copy Pad Without History Failed');
})
.end(() => {
api.get(`${endPoint('getHTML')}&padID=${newPad}`)
agent.get(`${endPoint('getHTML')}&padID=${newPad}`)
.expect((res) => {
const receivedHtml = res.body.data.html.replace('<br><br></body>', '</body>').toLowerCase();
const receivedHtml =
res.body.data.html.replace('<br><br></body>', '</body>').toLowerCase();
if (receivedHtml !== expectedHtml) {
throw new Error(`HTML received from export is not the one we were expecting.
@ -794,7 +812,8 @@ describe(__filename, function () {
const padWithNonExistentGroup = `notExistentGroup$${padId}`;
it('throws an error', function (done) {
this.timeout(150);
api.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}&destinationID=${padWithNonExistentGroup}&force=true`)
agent.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}&` +
`destinationID=${padWithNonExistentGroup}&force=true`)
.expect((res) => {
// code 1, it means an error has happened
if (res.body.code !== 1) throw new Error('It should report an error');
@ -813,7 +832,8 @@ describe(__filename, function () {
context('and force is false', function () {
it('throws an error', function (done) {
this.timeout(150);
api.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}&destinationID=${padIdExistent}&force=false`)
agent.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}` +
`&destinationID=${padIdExistent}&force=false`)
.expect((res) => {
// code 1, it means an error has happened
if (res.body.code !== 1) throw new Error('It should report an error');
@ -825,10 +845,13 @@ describe(__filename, function () {
context('and force is true', function () {
it('returns a successful response', function (done) {
this.timeout(200);
api.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}&destinationID=${padIdExistent}&force=true`)
agent.get(`${endPoint('copyPadWithoutHistory')}&sourceID=${sourcePadId}` +
`&destinationID=${padIdExistent}&force=true`)
.expect((res) => {
// code 1, it means an error has happened
if (res.body.code !== 0) throw new Error('Copy pad without history with force true failed');
if (res.body.code !== 0) {
throw new Error('Copy pad without history with force true failed');
}
})
.expect(200, done);
});
@ -842,10 +865,10 @@ describe(__filename, function () {
*/
var createNewPadWithHtml = function (padId, html, cb) {
api.get(`${endPoint('createPad')}&padID=${padId}`)
const createNewPadWithHtml = (padId, html, cb) => {
agent.get(`${endPoint('createPad')}&padID=${padId}`)
.end(() => {
api.post(endPoint('setHTML'))
agent.post(endPoint('setHTML'))
.send({
padID: padId,
html,
@ -854,11 +877,6 @@ var createNewPadWithHtml = function (padId, html, cb) {
});
};
var endPoint = function (point, version) {
version = version || apiVersion;
return `/api/${version}/${point}?apikey=${apiKey}`;
};
function makeid() {
let text = '';
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
@ -878,22 +896,3 @@ function generateLongText() {
}
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 (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;
};

View File

@ -1,10 +1,9 @@
'use strict';
const assert = require('assert').strict;
const common = require('../../common');
const settings = require('../../../../node/utils/Settings');
const supertest = require('supertest');
const api = supertest(`http://${settings.ip}:${settings.port}`);
let agent;
const apiKey = common.apiKey;
let apiVersion = 1;
let groupID = '';
@ -12,11 +11,15 @@ let authorID = '';
let sessionID = '';
let padID = makeid();
const endPoint = (point) => `/api/${apiVersion}/${point}?apikey=${apiKey}`;
describe(__filename, function () {
before(async function () { agent = await common.init(); });
describe('API Versioning', function () {
it('errors if can not connect', async function () {
this.timeout(200);
await api.get('/api/')
await agent.get('/api/')
.expect(200)
.expect((res) => {
assert(res.body.currentVersion);
@ -58,7 +61,7 @@ describe(__filename, function () {
describe('API: Group creation and deletion', function () {
it('createGroup', async function () {
this.timeout(100);
await api.get(endPoint('createGroup'))
await agent.get(endPoint('createGroup'))
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -70,7 +73,7 @@ describe(__filename, function () {
it('listSessionsOfGroup for empty group', async function () {
this.timeout(100);
await api.get(`${endPoint('listSessionsOfGroup')}&groupID=${groupID}`)
await agent.get(`${endPoint('listSessionsOfGroup')}&groupID=${groupID}`)
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -81,7 +84,7 @@ describe(__filename, function () {
it('deleteGroup', async function () {
this.timeout(100);
await api.get(`${endPoint('deleteGroup')}&groupID=${groupID}`)
await agent.get(`${endPoint('deleteGroup')}&groupID=${groupID}`)
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -91,7 +94,7 @@ describe(__filename, function () {
it('createGroupIfNotExistsFor', async function () {
this.timeout(100);
await api.get(`${endPoint('createGroupIfNotExistsFor')}&groupMapper=management`)
await agent.get(`${endPoint('createGroupIfNotExistsFor')}&groupMapper=management`)
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -104,7 +107,7 @@ describe(__filename, function () {
// Creates a group, creates 2 sessions, 2 pads and then deletes the group.
it('createGroup', async function () {
this.timeout(100);
await api.get(endPoint('createGroup'))
await agent.get(endPoint('createGroup'))
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -116,7 +119,7 @@ describe(__filename, function () {
it('createAuthor', async function () {
this.timeout(100);
await api.get(endPoint('createAuthor'))
await agent.get(endPoint('createAuthor'))
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -128,8 +131,8 @@ describe(__filename, function () {
it('createSession', async function () {
this.timeout(100);
await api.get(`${endPoint('createSession')
}&authorID=${authorID}&groupID=${groupID}&validUntil=999999999999`)
await agent.get(`${endPoint('createSession')}&authorID=${authorID}&groupID=${groupID}` +
'&validUntil=999999999999')
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -141,8 +144,8 @@ describe(__filename, function () {
it('createSession', async function () {
this.timeout(100);
await api.get(`${endPoint('createSession')
}&authorID=${authorID}&groupID=${groupID}&validUntil=999999999999`)
await agent.get(`${endPoint('createSession')}&authorID=${authorID}&groupID=${groupID}` +
'&validUntil=999999999999')
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -154,7 +157,7 @@ describe(__filename, function () {
it('createGroupPad', async function () {
this.timeout(100);
await api.get(`${endPoint('createGroupPad')}&groupID=${groupID}&padName=x1234567`)
await agent.get(`${endPoint('createGroupPad')}&groupID=${groupID}&padName=x1234567`)
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -164,7 +167,7 @@ describe(__filename, function () {
it('createGroupPad', async function () {
this.timeout(100);
await api.get(`${endPoint('createGroupPad')}&groupID=${groupID}&padName=x12345678`)
await agent.get(`${endPoint('createGroupPad')}&groupID=${groupID}&padName=x12345678`)
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -174,7 +177,7 @@ describe(__filename, function () {
it('deleteGroup', async function () {
this.timeout(100);
await api.get(`${endPoint('deleteGroup')}&groupID=${groupID}`)
await agent.get(`${endPoint('deleteGroup')}&groupID=${groupID}`)
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -187,7 +190,7 @@ describe(__filename, function () {
describe('API: Author creation', function () {
it('createGroup', async function () {
this.timeout(100);
await api.get(endPoint('createGroup'))
await agent.get(endPoint('createGroup'))
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -199,7 +202,7 @@ describe(__filename, function () {
it('createAuthor', async function () {
this.timeout(100);
await api.get(endPoint('createAuthor'))
await agent.get(endPoint('createAuthor'))
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -210,7 +213,7 @@ describe(__filename, function () {
it('createAuthor with name', async function () {
this.timeout(100);
await api.get(`${endPoint('createAuthor')}&name=john`)
await agent.get(`${endPoint('createAuthor')}&name=john`)
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -222,7 +225,7 @@ describe(__filename, function () {
it('createAuthorIfNotExistsFor', async function () {
this.timeout(100);
await api.get(`${endPoint('createAuthorIfNotExistsFor')}&authorMapper=chris`)
await agent.get(`${endPoint('createAuthorIfNotExistsFor')}&authorMapper=chris`)
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -233,7 +236,7 @@ describe(__filename, function () {
it('getAuthorName', async function () {
this.timeout(100);
await api.get(`${endPoint('getAuthorName')}&authorID=${authorID}`)
await agent.get(`${endPoint('getAuthorName')}&authorID=${authorID}`)
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -246,8 +249,8 @@ describe(__filename, function () {
describe('API: Sessions', function () {
it('createSession', async function () {
this.timeout(100);
await api.get(`${endPoint('createSession')
}&authorID=${authorID}&groupID=${groupID}&validUntil=999999999999`)
await agent.get(`${endPoint('createSession')}&authorID=${authorID}&groupID=${groupID}` +
'&validUntil=999999999999')
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -259,7 +262,7 @@ describe(__filename, function () {
it('getSessionInfo', async function () {
this.timeout(100);
await api.get(`${endPoint('getSessionInfo')}&sessionID=${sessionID}`)
await agent.get(`${endPoint('getSessionInfo')}&sessionID=${sessionID}`)
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -272,7 +275,7 @@ describe(__filename, function () {
it('listSessionsOfGroup', async function () {
this.timeout(100);
await api.get(`${endPoint('listSessionsOfGroup')}&groupID=${groupID}`)
await agent.get(`${endPoint('listSessionsOfGroup')}&groupID=${groupID}`)
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -283,7 +286,7 @@ describe(__filename, function () {
it('deleteSession', async function () {
this.timeout(100);
await api.get(`${endPoint('deleteSession')}&sessionID=${sessionID}`)
await agent.get(`${endPoint('deleteSession')}&sessionID=${sessionID}`)
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -293,7 +296,7 @@ describe(__filename, function () {
it('getSessionInfo of deleted session', async function () {
this.timeout(100);
await api.get(`${endPoint('getSessionInfo')}&sessionID=${sessionID}`)
await agent.get(`${endPoint('getSessionInfo')}&sessionID=${sessionID}`)
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -305,7 +308,7 @@ describe(__filename, function () {
describe('API: Group pad management', function () {
it('listPads', async function () {
this.timeout(100);
await api.get(`${endPoint('listPads')}&groupID=${groupID}`)
await agent.get(`${endPoint('listPads')}&groupID=${groupID}`)
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -316,7 +319,7 @@ describe(__filename, function () {
it('createGroupPad', async function () {
this.timeout(100);
await api.get(`${endPoint('createGroupPad')}&groupID=${groupID}&padName=${padID}`)
await agent.get(`${endPoint('createGroupPad')}&groupID=${groupID}&padName=${padID}`)
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -327,7 +330,7 @@ describe(__filename, function () {
it('listPads after creating a group pad', async function () {
this.timeout(100);
await api.get(`${endPoint('listPads')}&groupID=${groupID}`)
await agent.get(`${endPoint('listPads')}&groupID=${groupID}`)
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -340,7 +343,7 @@ describe(__filename, function () {
describe('API: Pad security', function () {
it('getPublicStatus', async function () {
this.timeout(100);
await api.get(`${endPoint('getPublicStatus')}&padID=${padID}`)
await agent.get(`${endPoint('getPublicStatus')}&padID=${padID}`)
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -351,7 +354,7 @@ describe(__filename, function () {
it('setPublicStatus', async function () {
this.timeout(100);
await api.get(`${endPoint('setPublicStatus')}&padID=${padID}&publicStatus=true`)
await agent.get(`${endPoint('setPublicStatus')}&padID=${padID}&publicStatus=true`)
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -361,7 +364,7 @@ describe(__filename, function () {
it('getPublicStatus after changing public status', async function () {
this.timeout(100);
await api.get(`${endPoint('getPublicStatus')}&padID=${padID}`)
await agent.get(`${endPoint('getPublicStatus')}&padID=${padID}`)
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -378,7 +381,7 @@ describe(__filename, function () {
describe('API: Misc', function () {
it('listPadsOfAuthor', async function () {
this.timeout(100);
await api.get(`${endPoint('listPadsOfAuthor')}&authorID=${authorID}`)
await agent.get(`${endPoint('listPadsOfAuthor')}&authorID=${authorID}`)
.expect(200)
.expect('Content-Type', /json/)
.expect((res) => {
@ -389,11 +392,6 @@ describe(__filename, function () {
});
});
const endPoint = function (point) {
return `/api/${apiVersion}/${point}?apikey=${apiKey}`;
};
function makeid() {
let text = '';
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

View File

@ -1,3 +1,5 @@
'use strict';
/**
* caching_middleware is responsible for serving everything under path `/javascripts/`
* That includes packages as defined in `src/node/utils/tar.json` and probably also plugin code
@ -6,7 +8,6 @@
const common = require('../common');
const assert = require('assert').strict;
const url = require('url');
const queryString = require('querystring');
const settings = require('../../../node/utils/Settings');
@ -23,7 +24,7 @@ let agent;
*/
function isPlaintextResponse(fileContent, resource) {
// callback=require.define&v=1234
const query = url.parse(resource).query;
const query = (new URL(resource, 'http://localhost')).search.slice(1);
// require.define
const jsonp = queryString.parse(query).callback;

View File

@ -44,14 +44,14 @@ describe('select formatting buttons when selection has style applied', function
const testIfFormattingButtonIsDeselected = function (style) {
it(`deselects the ${style} button`, function (done) {
this.timeout(50);
this.timeout(100);
helper.waitFor(() => isButtonSelected(style) === false).done(done);
});
};
const testIfFormattingButtonIsSelected = function (style) {
it(`selects the ${style} button`, function (done) {
this.timeout(50);
this.timeout(100);
helper.waitFor(() => isButtonSelected(style)).done(done);
});
};
@ -131,7 +131,7 @@ describe('select formatting buttons when selection has style applied', function
context('when user applies a style and the selection does not change', function () {
it('selects the style button', async function () {
this.timeout(50);
this.timeout(100);
const style = STYLES[0]; // italic
applyStyleOnLine(style, FIRST_LINE);
await helper.waitForPromise(() => isButtonSelected(style) === true);

View File

@ -14,7 +14,7 @@ MY_DIR=$(try cd "${0%/*}" && try pwd -P) || exit 1
try cd "${MY_DIR}/../../../.."
log "Assuming src/bin/installDeps.sh has already been run"
node node_modules/ep_etherpad-lite/node/server.js --experimental-worker "${@}" &
node src/node/server.js --experimental-worker "${@}" &
ep_pid=$!
log "Waiting for Etherpad to accept connections (http://localhost:9001)..."

View File

@ -14,7 +14,7 @@ MY_DIR=$(try cd "${0%/*}" && try pwd -P) || exit 1
try cd "${MY_DIR}/../../../.."
log "Assuming src/bin/installDeps.sh has already been run"
node node_modules/ep_etherpad-lite/node/server.js --experimental-worker "${@}" &
node src/node/server.js --experimental-worker "${@}" &
ep_pid=$!
log "Waiting for Etherpad to accept connections (http://localhost:9001)..."

View File

@ -17,8 +17,9 @@ s!"max":[^,]*!"max": 100!
s!"points":[^,]*!"points": 1000!
' settings.json.template >settings.json
log "Deprecation notice: runnerBackend.sh - Please use: cd src && npm test"
log "Assuming src/bin/installDeps.sh has already been run"
node node_modules/ep_etherpad-lite/node/server.js "${@}" &
node src/node/server.js "${@}" &
ep_pid=$!
log "Waiting for Etherpad to accept connections (http://localhost:9001)..."

View File

@ -17,7 +17,7 @@ s!"points":[^,]*!"points": 1000!
' settings.json.template >settings.json
log "Assuming src/bin/installDeps.sh has already been run"
node node_modules/ep_etherpad-lite/node/server.js "${@}" >/dev/null &
node src/node/server.js "${@}" >/dev/null &
ep_pid=$!
log "Waiting for Etherpad to accept connections (http://localhost:9001)..."

View File

@ -2,7 +2,7 @@
<system.webServer>
<handlers>
<add name="iisnode" path="node_modules/ep_etherpad-lite/node/server.js" verb="*" modules="iisnode" />
<add name="iisnode" path="src/node/server.js" verb="*" modules="iisnode" />
</handlers>
<rewrite>
@ -10,7 +10,7 @@
<!-- uncomment this section to enable debugging
<rule name="LogFile" patternSyntax="ECMAScript" stopProcessing="true">
<match url="iisnode"/>
<action type="Rewrite" url="node_modules/ep_etherpad-lite/node/iisnode" />
<action type="Rewrite" url="src/node/iisnode" />
</rule>
<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^server.js\/debug[\/]?" />
@ -23,7 +23,7 @@
<conditions>
<add input="{{REQUEST_FILENAME}}" matchType="IsFile" negate="True"/>
</conditions>
<action type="Rewrite" url="node_modules/ep_etherpad-lite/node/server.js" />
<action type="Rewrite" url="src/node/server.js" />
</rule>
</rules>
</rewrite>

View File

@ -1 +1,11 @@
@echo off
REM Windows and symlinks do not get along with each other, so on Windows
REM `node_modules\ep_etherpad-lite` is sometimes a full copy of `src` not a
REM symlink to `src`. If it is a copy, Node.js sees `src\foo.js` and
REM `node_modules\ep_etherpad-lite\foo.js` as two independent modules with
REM independent state, when they should be treated as the same file. To work
REM around this, everything must consistently use either `src` or
REM `node_modules\ep_etherpad-lite` on Windows. Because some plugins access
REM Etherpad internals via `require('ep_etherpad-lite/foo')`,
REM `node_modules\ep_etherpad-lite` is used here.
node node_modules\ep_etherpad-lite\node\server.js