mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-04-21 07:56:16 -04:00
restructure: move bin/ and tests/ to src/
Also add symlinks from the old `bin/` and `tests/` locations to avoid breaking scripts and other tools. Motivations: * Scripts and tests no longer have to do dubious things like: require('ep_etherpad-lite/node_modules/foo') to access packages installed as dependencies in `src/package.json`. * Plugins can access the backend test helper library in a non-hacky way: require('ep_etherpad-lite/tests/backend/common') * We can delete the top-level `package.json` without breaking our ability to lint the files in `bin/` and `tests/`. Deleting the top-level `package.json` has downsides: It will cause `npm` to print warnings whenever plugins are installed, npm will no longer be able to enforce a plugin's peer dependency on ep_etherpad-lite, and npm will keep deleting the `node_modules/ep_etherpad-lite` symlink that points to `../src`. But there are significant upsides to deleting the top-level `package.json`: It will drastically speed up plugin installation because `npm` doesn't have to recursively walk the dependencies in `src/package.json`. Also, deleting the top-level `package.json` avoids npm's horrible dependency hoisting behavior (where it moves stuff from `src/node_modules/` to the top-level `node_modules/` directory). Dependency hoisting causes numerous mysterious problems such as silent failures in `npm outdated` and `npm update`. Dependency hoisting also breaks plugins that do: require('ep_etherpad-lite/node_modules/foo')
This commit is contained in:
parent
efde0b787a
commit
2ea8ea1275
146 changed files with 191 additions and 1161 deletions
184
src/tests/frontend/runner.js
Normal file
184
src/tests/frontend/runner.js
Normal file
|
@ -0,0 +1,184 @@
|
|||
'use strict';
|
||||
|
||||
/* global specs_list */
|
||||
|
||||
$(() => {
|
||||
const stringifyException = (exception) => {
|
||||
let err = exception.stack || exception.toString();
|
||||
|
||||
// FF / Opera do not add the message
|
||||
if (!~err.indexOf(exception.message)) {
|
||||
err = `${exception.message}\n${err}`;
|
||||
}
|
||||
|
||||
// <=IE7 stringifies to [Object Error]. Since it can be overloaded, we
|
||||
// check for the result of the stringifying.
|
||||
if (err === '[object Error]') err = exception.message;
|
||||
|
||||
// Safari doesn't give you a stack. Let's at least provide a source line.
|
||||
if (!exception.stack && exception.sourceURL && exception.line !== undefined) {
|
||||
err += `\n(${exception.sourceURL}:${exception.line})`;
|
||||
}
|
||||
|
||||
return err;
|
||||
};
|
||||
|
||||
const customRunner = (runner) => {
|
||||
const stats = {suites: 0, tests: 0, passes: 0, pending: 0, failures: 0};
|
||||
let level = 0;
|
||||
|
||||
if (!runner) return;
|
||||
|
||||
runner.on('start', () => {
|
||||
stats.start = new Date();
|
||||
});
|
||||
|
||||
runner.on('suite', (suite) => {
|
||||
suite.root || stats.suites++;
|
||||
if (suite.root) return;
|
||||
append(suite.title);
|
||||
level++;
|
||||
});
|
||||
|
||||
runner.on('suite end', (suite) => {
|
||||
if (suite.root) return;
|
||||
level--;
|
||||
|
||||
if (level === 0) {
|
||||
append('');
|
||||
}
|
||||
});
|
||||
|
||||
// Scroll down test display after each test
|
||||
const mochaEl = $('#mocha')[0];
|
||||
runner.on('test', () => {
|
||||
mochaEl.scrollTop = mochaEl.scrollHeight;
|
||||
});
|
||||
|
||||
// max time a test is allowed to run
|
||||
// TODO this should be lowered once timeslider_revision.js is faster
|
||||
let killTimeout;
|
||||
runner.on('test end', () => {
|
||||
stats.tests++;
|
||||
});
|
||||
|
||||
runner.on('pass', (test) => {
|
||||
if (killTimeout) clearTimeout(killTimeout);
|
||||
killTimeout = setTimeout(() => {
|
||||
append('FINISHED - [red]no test started since 3 minutes, tests stopped[clear]');
|
||||
}, 60000 * 3);
|
||||
|
||||
const medium = test.slow() / 2;
|
||||
test.speed = test.duration > test.slow()
|
||||
? 'slow'
|
||||
: test.duration > medium
|
||||
? 'medium'
|
||||
: 'fast';
|
||||
|
||||
stats.passes++;
|
||||
append(`-> [green]PASSED[clear] : ${test.title} ${test.duration} ms`);
|
||||
});
|
||||
|
||||
runner.on('fail', (test, err) => {
|
||||
if (killTimeout) clearTimeout(killTimeout);
|
||||
killTimeout = setTimeout(() => {
|
||||
append('FINISHED - [red]no test started since 3 minutes, tests stopped[clear]');
|
||||
}, 60000 * 3);
|
||||
|
||||
stats.failures++;
|
||||
test.err = err;
|
||||
append(`-> [red]FAILED[clear] : ${test.title} ${stringifyException(test.err)}`);
|
||||
});
|
||||
|
||||
runner.on('pending', (test) => {
|
||||
if (killTimeout) clearTimeout(killTimeout);
|
||||
killTimeout = setTimeout(() => {
|
||||
append('FINISHED - [red]no test started since 3 minutes, tests stopped[clear]');
|
||||
}, 60000 * 3);
|
||||
|
||||
stats.pending++;
|
||||
append(`-> [yellow]PENDING[clear]: ${test.title}`);
|
||||
});
|
||||
|
||||
const $console = $('#console');
|
||||
const append = (text) => {
|
||||
const oldText = $console.text();
|
||||
|
||||
let space = '';
|
||||
for (let i = 0; i < level * 2; i++) {
|
||||
space += ' ';
|
||||
}
|
||||
|
||||
let splitedText = '';
|
||||
_(text.split('\n')).each((line) => {
|
||||
while (line.length > 0) {
|
||||
const split = line.substr(0, 100);
|
||||
line = line.substr(100);
|
||||
if (splitedText.length > 0) splitedText += '\n';
|
||||
splitedText += split;
|
||||
}
|
||||
});
|
||||
|
||||
// indent all lines with the given amount of space
|
||||
const newText = _(splitedText.split('\n')).map((line) => space + line).join('\\n');
|
||||
|
||||
$console.text(`${oldText + newText}\\n`);
|
||||
};
|
||||
|
||||
const total = runner.total;
|
||||
runner.on('end', () => {
|
||||
stats.end = new Date();
|
||||
stats.duration = stats.end - stats.start;
|
||||
const minutes = Math.floor(stats.duration / 1000 / 60);
|
||||
// chrome < 57 does not like this .toString().padStart('2', '0');
|
||||
const seconds = Math.round((stats.duration / 1000) % 60);
|
||||
if (stats.tests === total) {
|
||||
append(`FINISHED - ${stats.passes} tests passed, ${stats.failures} tests failed, ` +
|
||||
`${stats.pending} pending, duration: ${minutes}:${seconds}`);
|
||||
} else if (stats.tests > total) {
|
||||
append(`FINISHED - but more tests than planned returned ${stats.passes} tests passed, ` +
|
||||
`${stats.failures} tests failed, ${stats.pending} pending, ` +
|
||||
`duration: ${minutes}:${seconds}`);
|
||||
append(`${total} tests, but ${stats.tests} returned. ` +
|
||||
'There is probably a problem with your async code or error handling, ' +
|
||||
'see https://github.com/mochajs/mocha/issues/1327');
|
||||
} else {
|
||||
append(`FINISHED - but not all tests returned ${stats.passes} tests passed, ` +
|
||||
`${stats.failures} tests failed, ${stats.pending} tests pending, ` +
|
||||
`duration: ${minutes}:${seconds}`);
|
||||
append(`${total} tests, but only ${stats.tests} returned. ` +
|
||||
'Check for failed before/beforeEach-hooks (no `test end` is called for them ' +
|
||||
'and subsequent tests of the same suite are skipped), ' +
|
||||
'see https://github.com/mochajs/mocha/pull/1043');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getURLParameter = (name) => (new URLSearchParams(location.search)).get(name);
|
||||
|
||||
// get the list of specs and filter it if requested
|
||||
const specs = specs_list.slice();
|
||||
|
||||
// inject spec scripts into the dom
|
||||
const $body = $('body');
|
||||
$.each(specs, (i, spec) => {
|
||||
// if the spec isn't a plugin spec which means the spec file might be in a different subfolder
|
||||
if (!spec.startsWith('/')) {
|
||||
$body.append(`<script src="specs/${spec}"></script>`);
|
||||
} else {
|
||||
$body.append(`<script src="${spec}"></script>`);
|
||||
}
|
||||
});
|
||||
|
||||
// initialize the test helper
|
||||
helper.init(() => {
|
||||
// configure and start the test framework
|
||||
const grep = getURLParameter('grep');
|
||||
if (grep != null) {
|
||||
mocha.grep(grep);
|
||||
}
|
||||
|
||||
const runner = mocha.run();
|
||||
customRunner(runner);
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue