'use strict';
/*
* While importexport tests target the `setHTML` API endpoint, which is nearly identical to what
* happens when a user manually imports a document via the UI, the contentcollector tests here don't
* use rehype to process the document. Rehype removes spaces and newĺines were applicable, so the
* expected results here can differ from importexport.js.
*
* If you add tests here, please also add them to importexport.js
*/
const AttributePool = require('ep_etherpad-lite/static/js/AttributePool');
const assert = require('assert').strict;
const cheerio = require('ep_etherpad-lite/node_modules/cheerio');
const contentcollector = require('ep_etherpad-lite/static/js/contentcollector');
const tests = {
nestedLi: {
description: 'Complex nested Li',
html: '
- one
- 1.1
- two
',
wantLineAttribs: [
'*0*1*2*3+1+3', '*0*4*2*5+1+3', '*0*1*2*5+1+3',
],
wantText: [
'*one', '*1.1', '*two',
],
},
complexNest: {
description: 'Complex list of different types',
html: '- item
- item1
- item2
',
wantLineAttribs: [
'*0*1*2+1+3',
'*0*1*2+1+3',
'*0*1*2+1+1',
'*0*1*2+1+1',
'*0*1*2+1+1',
'*0*3*2+1+1',
'*0*3*2+1+1',
'*0*4*2*5+1+4',
'*0*6*2*7+1+5',
'*0*6*2*7+1+5',
],
wantText: [
'*one',
'*two',
'*0',
'*1',
'*2',
'*3',
'*4',
'*item',
'*item1',
'*item2',
],
},
ul: {
description: 'Tests if uls properly get attributes',
html: 'div
foo
',
wantLineAttribs: ['*0*1*2+1+1', '*0*1*2+1+1', '+3', '+3'],
wantText: ['*a', '*b', 'div', 'foo'],
},
ulIndented: {
description: 'Tests if indented uls properly get attributes',
html: 'foo
',
wantLineAttribs: ['*0*1*2+1+1', '*0*3*2+1+1', '*0*1*2+1+1', '+3'],
wantText: ['*a', '*b', '*a', 'foo'],
},
ol: {
description: 'Tests if ols properly get line numbers when in a normal OL',
html: '- a
- b
- c
test
',
wantLineAttribs: ['*0*1*2*3+1+1', '*0*1*2*3+1+1', '*0*1*2*3+1+1', '+4'],
wantText: ['*a', '*b', '*c', 'test'],
noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?',
},
lineDoBreakInOl: {
description: 'A single completely empty line break within an ol should reset count if OL is closed off..',
html: '- should be 1
hello
- should be 1
- should be 2
',
wantLineAttribs: ['*0*1*2*3+1+b', '+5', '*0*1*2*4+1+b', '*0*1*2*4+1+b', ''],
wantText: ['*should be 1', 'hello', '*should be 1', '*should be 2', ''],
noteToSelf: "Shouldn't include attribute marker in the line",
},
testP: {
description: 'A single
should create a new line',
html: '',
wantLineAttribs: ['', ''],
wantText: ['', ''],
noteToSelf: 'should create a line break but not break numbering',
},
nestedOl: {
description: 'Tests if ols properly get line numbers when in a normal OL',
html: 'a- b
- c
notlistfoo
',
wantLineAttribs: ['+1', '*0*1*2*3+1+1', '*0*4*2*5+1+1', '+7', '+3'],
wantText: ['a', '*b', '*c', 'notlist', 'foo'],
noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?',
},
nestedOl2: {
description: 'First item being an UL then subsequent being OL will fail',
html: '',
wantLineAttribs: ['+1', '*0*1*2*3+1+1', '*0*4*2*5+1+1'],
wantText: ['a', '*b', '*c'],
noteToSelf: 'Ensure empty P does not induce line attribute marker, wont this break the editor?',
disabled: true,
},
lineDontBreakOL: {
description: 'A single completely empty line break within an ol should NOT reset count',
html: '- should be 1
- should be 2
- should be 3
',
wantLineAttribs: [],
wantText: ['*should be 1', '*should be 2', '*should be 3'],
noteToSelf: "should create a line break but not break numbering -- This is what I can't get working!",
disabled: true,
},
ignoreAnyTagsOutsideBody: {
description: 'Content outside body should be ignored',
html: 'titleempty
',
wantLineAttribs: ['+5'],
wantText: ['empty'],
},
lineWithMultipleSpaces: {
description: 'Multiple spaces should be preserved',
html: 'Text with more than one space.
',
wantLineAttribs: ['+10'],
wantText: ['Text with more than one space.'],
},
lineWithMultipleNonBreakingAndNormalSpaces: {
description: 'non-breaking and normal space should be preserved',
html: 'Text with more than one space.
',
wantLineAttribs: ['+10'],
wantText: ['Text with more than one space.'],
},
multiplenbsp: {
description: 'Multiple nbsp should be preserved',
html: '
',
wantLineAttribs: ['+2'],
wantText: [' '],
},
multipleNonBreakingSpaceBetweenWords: {
description: 'Multiple nbsp between words ',
html: ' word1 word2 word3
',
wantLineAttribs: ['+m'],
wantText: [' word1 word2 word3'],
},
nonBreakingSpacePreceededBySpaceBetweenWords: {
description: 'A non-breaking space preceeded by a normal space',
html: ' word1 word2 word3
',
wantLineAttribs: ['+l'],
wantText: [' word1 word2 word3'],
},
nonBreakingSpaceFollowededBySpaceBetweenWords: {
description: 'A non-breaking space followed by a normal space',
html: ' word1 word2 word3
',
wantLineAttribs: ['+l'],
wantText: [' word1 word2 word3'],
},
spacesAfterNewline: {
description: 'Don\'t collapse spaces that follow a newline',
html: 'something
something
',
wantLineAttribs: ['+9', '+m'],
wantText: ['something', ' something'],
},
spacesAfterNewlineP: {
description: 'Don\'t collapse spaces that follow a empty paragraph',
html: 'something something
',
wantLineAttribs: ['+9', '', '+m'],
wantText: ['something', '', ' something'],
},
spacesAtEndOfLine: {
description: 'Don\'t collapse spaces that preceed/follow a newline',
html: 'something
something
',
wantLineAttribs: ['+l', '+m'],
wantText: ['something ', ' something'],
},
spacesAtEndOfLineP: {
description: 'Don\'t collapse spaces that preceed/follow a empty paragraph',
html: 'something something
',
wantLineAttribs: ['+l', '', '+m'],
wantText: ['something ', '', ' something'],
},
nonBreakingSpacesAfterNewlines: {
description: 'Don\'t collapse non-breaking spaces that follow a newline',
html: 'something
something
',
wantLineAttribs: ['+9', '+c'],
wantText: ['something', ' something'],
},
nonBreakingSpacesAfterNewlinesP: {
description: 'Don\'t collapse non-breaking spaces that follow a paragraph',
html: 'something something
',
wantLineAttribs: ['+9', '', '+c'],
wantText: ['something', '', ' something'],
},
preserveSpacesInsideElements: {
description: 'Preserve all spaces when multiple are present',
html: 'Need more space s !
',
wantLineAttribs: ['+h*0+4+2'],
wantText: ['Need more space s !'],
},
preserveSpacesAcrossNewlines: {
description: 'Newlines and multiple spaces across newlines should be preserved',
html: `
Need
more
space
s
!
`,
wantLineAttribs: ['+19*0+4+b'],
wantText: ['Need more space s !'],
},
multipleNewLinesAtBeginning: {
description: 'Multiple new lines at the beginning should be preserved',
html: '
first line
second line
',
wantLineAttribs: ['', '', '', '', '+a', '', '+b'],
wantText: ['', '', '', '', 'first line', '', 'second line'],
},
multiLineParagraph: {
description: 'A paragraph with multiple lines should not loose spaces when lines are combined',
html: `
а б в г ґ д е є ж з и і ї й к л м н о
п р с т у ф х ц ч ш щ ю я ь
`,
wantLineAttribs: ['+1t'],
wantText: ['а б в г ґ д е є ж з и і ї й к л м н о п р с т у ф х ц ч ш щ ю я ь'],
},
multiLineParagraphWithPre: {
description: 'lines in preformatted text should be kept intact',
html: `
а б в г ґ д е є ж з и і ї й к л м н о
multiple
lines
in
pre
п р с т у ф х ц ч ш щ ю я
ь
`,
wantLineAttribs: ['+11', '+8', '+5', '+2', '+3', '+r'],
wantText: [
'а б в г ґ д е є ж з и і ї й к л м н о',
'multiple',
'lines',
'in',
'pre',
'п р с т у ф х ц ч ш щ ю я ь',
],
},
preIntroducesASpace: {
description: 'pre should be on a new line not preceeded by a space',
html: `
1
preline
`,
wantLineAttribs: ['+6', '+7'],
wantText: [' 1 ', 'preline'],
},
dontDeleteSpaceInsideElements: {
description: 'Preserve spaces on the beginning and end of a element',
html: 'Need more space s !
',
wantLineAttribs: ['+f*0+3+1'],
wantText: ['Need more space s !'],
},
dontDeleteSpaceOutsideElements: {
description: 'Preserve spaces outside elements',
html: 'Need more space s !
',
wantLineAttribs: ['+g*0+1+2'],
wantText: ['Need more space s !'],
},
dontDeleteSpaceAtEndOfElement: {
description: 'Preserve spaces at the end of an element',
html: 'Need more space s !
',
wantLineAttribs: ['+g*0+2+1'],
wantText: ['Need more space s !'],
},
dontDeleteSpaceAtBeginOfElements: {
description: 'Preserve spaces at the start of an element',
html: 'Need more space s !
',
wantLineAttribs: ['+f*0+2+2'],
wantText: ['Need more space s !'],
},
};
describe(__filename, function () {
for (const test of Object.keys(tests)) {
const testObj = tests[test];
describe(test, function () {
if (testObj.disabled) {
return xit('DISABLED:', test, function (done) {
done();
});
}
it(testObj.description, async function () {
const $ = cheerio.load(testObj.html); // Load HTML into Cheerio
const doc = $('body')[0]; // Creates a dom-like representation of HTML
// Create an empty attribute pool
const apool = new AttributePool();
// Convert a dom tree into a list of lines and attribute liens
// using the content collector object
const cc = contentcollector.makeContentCollector(true, null, apool);
cc.collectContent(doc);
const result = cc.finish();
const gotAttributes = result.lineAttribs;
const wantAttributes = testObj.wantLineAttribs;
const gotText = new Array(result.lines);
const wantText = testObj.wantText;
assert.deepEqual(gotText[0], wantText);
assert.deepEqual(gotAttributes, wantAttributes);
});
});
}
});
function arraysEqual(a, b) {
if (a === b) return true;
if (a == null || b == null) return false;
if (a.length !== b.length) return false;
// If you don't care about the order of the elements inside
// the array, you should sort both arrays here.
// Please note that calling sort on an array will modify that array.
// you might want to clone your array first.
for (let i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) return false;
}
return true;
}