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:
John McLear 2021-02-03 12:08:43 +00:00 committed by Richard Hansen
parent efde0b787a
commit 2ea8ea1275
146 changed files with 191 additions and 1161 deletions

View file

@ -1,171 +0,0 @@
'use strict';
// WARNING: drag and drop is only simulated on these tests, manual testing might also be necessary
describe('drag and drop', function () {
before(function (done) {
helper.newPad(() => {
createScriptWithSeveralLines(done);
});
this.timeout(60000);
});
context('when user drags part of one line and drops it far form its original place', function () {
before(function (done) {
selectPartOfSourceLine();
dragSelectedTextAndDropItIntoMiddleOfLine(TARGET_LINE);
// make sure DnD was correctly simulated
helper.waitFor(() => {
const $targetLine = getLine(TARGET_LINE);
const sourceWasMovedToTarget = $targetLine.text() === 'Target line [line 1]';
return sourceWasMovedToTarget;
}).done(done);
});
context('and user triggers UNDO', function () {
before(function () {
const $undoButton = helper.padChrome$('.buttonicon-undo');
$undoButton.click();
});
it('moves text back to its original place', function (done) {
// test text was removed from drop target
const $targetLine = getLine(TARGET_LINE);
expect($targetLine.text()).to.be('Target line []');
// test text was added back to original place
const $firstSourceLine = getLine(FIRST_SOURCE_LINE);
const $lastSourceLine = getLine(FIRST_SOURCE_LINE + 1);
expect($firstSourceLine.text()).to.be('Source line 1.');
expect($lastSourceLine.text()).to.be('Source line 2.');
done();
});
});
});
context('when user drags some lines far form its original place', function () {
before(function (done) {
selectMultipleSourceLines();
dragSelectedTextAndDropItIntoMiddleOfLine(TARGET_LINE);
// make sure DnD was correctly simulated
helper.waitFor(() => {
const $lineAfterTarget = getLine(TARGET_LINE + 1);
const sourceWasMovedToTarget = $lineAfterTarget.text() !== '...';
return sourceWasMovedToTarget;
}).done(done);
});
context('and user triggers UNDO', function () {
before(function () {
const $undoButton = helper.padChrome$('.buttonicon-undo');
$undoButton.click();
});
it('moves text back to its original place', function (done) {
// test text was removed from drop target
const $targetLine = getLine(TARGET_LINE);
expect($targetLine.text()).to.be('Target line []');
// test text was added back to original place
const $firstSourceLine = getLine(FIRST_SOURCE_LINE);
const $lastSourceLine = getLine(FIRST_SOURCE_LINE + 1);
expect($firstSourceLine.text()).to.be('Source line 1.');
expect($lastSourceLine.text()).to.be('Source line 2.');
done();
});
});
});
/* ********************* Helper functions/constants ********************* */
const TARGET_LINE = 2;
const FIRST_SOURCE_LINE = 5;
const getLine = (lineNumber) => {
const $lines = helper.padInner$('div');
return $lines.slice(lineNumber, lineNumber + 1);
};
const createScriptWithSeveralLines = (done) => {
// create some lines to be used on the tests
const $firstLine = helper.padInner$('div').first();
$firstLine.html('...<br>...<br>Target line []<br>...<br>...<br>' +
'Source line 1.<br>Source line 2.<br>');
// wait for lines to be split
helper.waitFor(() => {
const $lastSourceLine = getLine(FIRST_SOURCE_LINE + 1);
return $lastSourceLine.text() === 'Source line 2.';
}).done(done);
};
const selectPartOfSourceLine = () => {
const $sourceLine = getLine(FIRST_SOURCE_LINE);
// select 'line 1' from 'Source line 1.'
const start = 'Source '.length;
const end = start + 'line 1'.length;
helper.selectLines($sourceLine, $sourceLine, start, end);
};
const selectMultipleSourceLines = () => {
const $firstSourceLine = getLine(FIRST_SOURCE_LINE);
const $lastSourceLine = getLine(FIRST_SOURCE_LINE + 1);
helper.selectLines($firstSourceLine, $lastSourceLine);
};
const dragSelectedTextAndDropItIntoMiddleOfLine = (targetLineNumber) => {
// dragstart: start dragging content
triggerEvent('dragstart');
// drop: get HTML data from selected text
const draggedHtml = getHtmlFromSelectedText();
triggerEvent('drop');
// dragend: remove original content + insert HTML data into target
moveSelectionIntoTarget(draggedHtml, targetLineNumber);
triggerEvent('dragend');
};
const getHtmlFromSelectedText = () => {
const innerDocument = helper.padInner$.document;
const range = innerDocument.getSelection().getRangeAt(0);
const clonedSelection = range.cloneContents();
const span = innerDocument.createElement('span');
span.id = 'buffer';
span.appendChild(clonedSelection);
const draggedHtml = span.outerHTML;
return draggedHtml;
};
const triggerEvent = (eventName) => {
const event = new helper.padInner$.Event(eventName);
helper.padInner$('#innerdocbody').trigger(event);
};
const moveSelectionIntoTarget = (draggedHtml, targetLineNumber) => {
const innerDocument = helper.padInner$.document;
// delete original content
innerDocument.execCommand('delete');
// set position to insert content on target line
const $target = getLine(targetLineNumber);
$target.sendkeys('{selectall}{rightarrow}{leftarrow}');
// Insert content.
// Based on http://stackoverflow.com/a/6691294, to be IE-compatible
const range = innerDocument.getSelection().getRangeAt(0);
const frag = innerDocument.createDocumentFragment();
const el = innerDocument.createElement('div');
el.innerHTML = draggedHtml;
while (el.firstChild) {
frag.appendChild(el.firstChild);
}
range.insertNode(frag);
};
});