const contentcollector = require('../../../src/static/js/contentcollector');
const AttributePool = require('../../../src/static/js/AttributePool');
const cheerio = require('../../../src/node_modules/cheerio');
/**
* The html of these tests is intentionally collapsed into a single line, except in special cases.
* All tests are duplicated in ./tests/backend/specs/api/importexport/ where they are properly indented.
*
* 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 don't use rehype to process
* the document. Rehype remove spaces and newĺines were applicable.
*
* Note the final
in every of the tests except when the body ends in a list.
*/
const tests = {
nestedOrderedLi: {
description: 'Complex nested Li',
html: '
- one
- 1.1
- two
',
expectedLineAttribs: [
'*0*1*2*3+1+3', '*0*4*2*5+1+3', '*0*1*2*5+1+3',
],
expectedText: [
'*one', '*1.1', '*two',
],
},
complexNest: {
description: 'Complex list of different types',
html: '- item
- item1
- item2
',
expectedLineAttribs: [
'*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',
],
expectedText: [
'*one',
'*two',
'*0',
'*1',
'*2',
'*3',
'*4',
'*item',
'*item1',
'*item2',
],
},
ul: {
description: 'Tests if uls properly get attributes',
html: 'div
foo
',
expectedLineAttribs: ['*0*1*2+1+1', '*0*1*2+1+1', '+3', '+3'],
expectedText: ['*a', '*b', 'div', 'foo'],
},
ulIndented: {
description: 'Tests if indented uls properly get attributes',
html: 'foo
',
expectedLineAttribs: ['*0*1*2+1+1', '*0*3*2+1+1', '*0*1*2+1+1', '+3'],
expectedText: ['*a', '*b', '*a', 'foo'],
},
ol: {
description: 'Tests if ols properly get line numbers when in a normal OL',
html: '- a
- b
- c
test
',
expectedLineAttribs: ['*0*1*2*3+1+1', '*0*1*2*3+1+1', '*0*1*2*3+1+1', '+4'],
expectedText: ['*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
',
expectedLineAttribs: ['*0*1*2*3+1+b', '+5', '*0*1*2*4+1+b', '*0*1*2*4+1+b', ''],
expectedText: ['*should be 1', 'hello', '*should be 1', '*should be 2', ''],
noteToSelf: "Shouldn't include attribute marker in the line",
},
bulletListInOL: {
description: 'A bullet within an OL should not change numbering..',
html: '
- should be 1
- should be 2
',
expectedLineAttribs: ['*0*1*2*3+1+b', '*0*4*2*3+1+i', '*0*1*2*5+1+b', ''],
expectedText: ['*should be 1', '*should be a bullet', '*should be 2', ''],
},
testP: {
description: 'A single should create a new line',
html: '',
expectedLineAttribs: ['', ''],
expectedText: ['', ''],
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
',
expectedLineAttribs: ['+1', '*0*1*2*3+1+1', '*0*4*2*5+1+1', '+7', '+3'],
expectedText: ['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: '',
expectedLineAttribs: ['+1', '*0*1*2*3+1+1', '*0*4*2*5+1+1'],
expectedText: ['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
',
expectedLineAttribs: [],
expectedText: ['*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,
},
brDontBreakOL: {
description: 'A single completely empty line break using
within an ol should NOT reset count',
html: '- should be 1
- should be 2
- should be 3
',
expectedLineAttribs: [],
expectedText: ['*should be 1', '*should be 2', '*should be 3'],
disabled: true,
},
normalNewline: {
description: 'A normal newline as
should work',
html: 'line1
line2
',
expectedLineAttribs: ['+5','+5'],
expectedText: ['line1', 'line2'],
},
indents: {
disabled:true,
description: 'Two indentations',
html: '
',
expectedLineAttribs: ['*0*1*2+1+7', '*0*1*2+1+7', '*0*3*2+1+7', '*0*3*2+1+7'],
expectedText: ['*indent1', '*indent1', '*indent2', '*indent2', '*indent1']
},
indentsAndNewlines: {
disabled: true, // does which might not be right? instead:
description: 'Indentations and newlines',
html: '
',
expectedLineAttribs: ['*0*1*2+1+7', '', '*0*1*2+1+7', '*0*3*2+1+7', '', '*0*1*2+1+7', '*0*3*2+1+7'],
expectedText: ['*indent1', '', '*indent1', '*indent2', '', '*indent1', '*indent2']
},
multipleIndentLevelAndStyles: {
disabled:true,
description: '8 levels of indents, newlines and attributes',
html: '
indent4 line 2 bisuindent4 line 2 bs- indent4 line 2 u
uis
',
expectedLineAttribs: [],
expectedText: ['*indent line 1', '*indent line 1']
},
bulletsAndEmptyLines: {
disabled: true,
description: 'Bullet lists that contain empty lines',
html: '
',
expectedLineAttribs: [],
expectedText: ['*bullet line 1', '', '*bullet line 2', '', '*bullet2 line 1', '', '', '*bullet2 line 2', '']
},
indentsAndEmptyLines: {
disabled:true,
description: 'Indented lines that contain empty lines',
html: '
',
expectedLineAttribs: [],
expectedText: ['indented line 1', '', '', 'indented line 2', '', 'indent2 line 1']
},
bulletsAndNonBulletLines: {
disabled:true,
description: 'Bullet lists that contain not bullet lines',
html: 'not bullet',
expectedLineAttribs: [],
expectedText: []
},
indentsAndNonIndentedLines: {
disabled: true,
description: 'Indented lines that contain non indented lines',
html: 'not indent',
expectedLineAttribs: [],
expectedText: ['*indent line 1', 'not indent', 'not indent', '*indent line 2', '', '*indent2 line 1']
},
olWithNonDefaultStart: {
disabled: true,
description: 'An ordered list that does not start with 1',
html: '- 6
- 7
- 4
- 5
- 0.0.9
- 0.0.10
',
expectedLineAttribs: [
],
expectedText: ['*6', '*7', '*4', '*5', '*0.0.9', '*0.0.10'] //0.0. is wrong
//totally wrong after import
/**
1. 6
1. 7
2. 4
3. 5
3.0.1 0.0.9
3.0.1 0.0.10
only backwards key (and probably return key) trigger renumbering, it is not triggered when entering in any of the lines:
1. 6
2. 7
3. 4
4. 5
4.0.1. 0.0.9
4.0.2. 0.0.10
*/
},
stylingWithAttributesAndTags: {
description: 'Styling applied as separate tags and span attributes',
html: 'line
bold strikethrough italic underline no style no style
',
expectedLineAttribs: ['+4', '*0*1*2*3+z+i'],
expectedText: ['line', 'bold strikethrough italic underline no style no style']
},
stylingWithSpanAndTagsWithoutAttributes: {
description: 'Styling applied as separate tags inside a span',
html: 'line
bold strikethrough italic underline no style no style
',
expectedLineAttribs: ['+4', '*0*1*2*3+z+i'],
expectedText: ['line', 'bold strikethrough italic underline no style no style']
},
stylingWithAttributesWithoutTags: {
disabled:true,
description: 'Styling applied as span attributes',
html: 'line
bold strikethrough italic underline no style no style
',
expectedLineAttribs: ['+4', '*0*1*2*3+z+i'],
expectedText: ['line', 'bold strikethrough italic underline no style no style']
},
stylingWithTagsWithoutAttributes: {
description: 'Styling applied as separate tags',
html: 'line
bold strikethrough italic underline no style no style
',
expectedLineAttribs: ['+4', '*0*1*2*3+z+i'],
expectedText: ['line', 'bold strikethrough italic underline no style no style']
},
stylingWithFontAttributes: {
disabled: true,
description: "Styling applied as font-style",
html: 'line
bold strikethrough italic underline no style no style',
expectedLineAttribs: [],
expectedText: ['abc']
},
dontDeleteSpaceInsideElements: { //BEGINNING AAND END
description: 'Preserve spaces on the beginning and end of a element',
html: 'Need more spaces !
',
expectedLineAttribs: ['+f*0+2+1'],
expectedText: ['Need more spaces !']
},
collapseSpacesInsideElements: {
description: 'Preserve only on space when multiple are present',
html: 'Need more space s !
',
expectedLineAttribs: ['+f*0'],
expectedText: ['Need more space s !'],
disabled: true
},
collapseSpacesAcrossNewlines: {
disabled:true,
description: 'Newlines and multiple spaces across newlines should be collapsed',
html: `
Need
more
space
s
!
`,
expectedLineAttribs: [],
expectedText: []
},
multipleNewLinesAtBeginning: {
disabled: true,
description: 'Multiple new lines at the beginning should be preserved',
html: '
first line
second line
',
expectedLineAttribs: [],
expectedText: []
},
spacesAfterNewline: {
description: 'Don\'t collapse spaces that follow a newline',
html: 'something
something
',
expectedLineAttribs: ['+9', '+m'],
expectedText: ['something', ' something']
},
spacesAfterNewlineP: {
description: 'Don\'t collapse spaces that follow a newline',
html: 'something something
',
expectedLineAttribs: ['+9', '', '+m'],
expectedText: ['something', '', ' something']
},
spacesAtEndOfLine: {
description: 'Don\'t collapse spaces that follow a newline',
html: 'something
something
',
expectedLineAttribs: ['+l', '+m'],
expectedText: ['something ', ' something']
},
spacesAtEndOfLineP: {
description: 'Don\'t collapse spaces that follow a newline',
html: 'something something
',
expectedLineAttribs: ['+l', '', '+m'],
expectedText: ['something ', '', ' something']
},
nonBreakingSpacesAfterNewlines: {
description: 'Don\'t collapse non-breaking spaces that follow a newline',
html: 'something
something
',
expectedLineAttribs: ['+9', '+c'],
expectedText: ['something', ' something']
},
nonBreakingSpacesAfterNewlinesP: {
description: 'Don\'t collapse non-breaking spaces that follow a newline',
html: 'something something
',
expectedLineAttribs: ['+9', '', '+c'],
expectedText: ['something', '', ' something']
},
ignoreAnyTagsOutsideBody: {
description: 'Never add non-breaking spaces for elements outside body',
html: 'titleempty
',
expectedLineAttribs: ['+5'],
expectedText: ['empty']
},
linebreakPreceedsOL: {
description: 'A break before an ordered list should not introduce a list item',
html: '
- should be 1
',
//expectedHTML: '
- should be 1
',
expectedLineAttribs: ['', '*0*1*2*3+1+b'],
//expectedText: '\n\t1. should be 1\n\n'
expectedText: ['', '*should be 1']
},
paragraphPreceedsOL:{
description: 'An empty paragraph before an ordered list should not introduce a list item',
html: '- should be 1
',
//expectedHTML: '
- should be 1
',
//expectedText: '\n\t1. should be 1\n\n'
expectedLineAttribs: ['', '*0*1*2*3+1+b'],
expectedText: ['', '*should be 1']
},
lineWithSpacesPreceedsOL:{
disabled:true,
description: 'A single line with a space right before an ordered list should not introduce a list item',
html: '
- should be 1
',
expectedLineAttribs: ['', '*0*1*2*3+1+b'],
expectedText: ['', '', ' ', '*should be 1']
//expectedHTML: '
- should be 1
',
//expectedText: '\n\n\n\t1. should be 1\n\n'
},
multipleParagraphsWithSpacesPreceedsOL: {
description: 'Paragraphs with spaces right before an ordered list should not introduce a list item',
html: '
- should be 1
',
expectedLineAttribs: ['+1', '+1', '+1', '*0*1*2*3+1+b'],
expectedText: [' ', ' ', ' ', '*should be 1']
//expectedHTML: '
- should be 1
',
//expectedText: '\n\n\n\t1. should be 1\n\n'
},
multiLineParagraph:{
description: "A paragraph with multiple lines should not loose spaces when lines are combined",
html: `
а б в г ґ д е є ж з и і ї й к л м н о
п р с т у ф х ц ч ш щ ю я ь
`,
expectedLineAttribs: [ '+1t' ],
expectedText: ["а б в г ґ д е є ж з и і ї й к л м н о п р с т у ф х ц ч ш щ ю я ь"]
},
multiLineParagraphWithPre:{
description: "lines in preformatted text should be kept intact",
html: `
а б в г ґ д е є ж з и і ї й к л м н о
multiple
lines
in
pre
п р с т у ф х ц ч ш щ ю я
ь
`,
expectedLineAttribs: [ '+11', '+8', '+5', '+2', '+3', '+r' ],
expectedText: ['а б в г ґ д е є ж з и і ї й к л м н о', 'multiple', 'lines', 'in', 'pre', 'п р с т у ф х ц ч ш щ ю я ь']
},
preIntroducesASpace: {
description: "pre should be on a new line not preceeded by a space",
html: `
1
preline
`,
expectedLineAttribs: [ '+6', '+7' ],
expectedText: [' 1 ', 'preline']
},
linesWithMoreThanOnceSpace: {
description: 'Multiple spaces should be preserved',
html: 'Text with more than one space.
',
expectedLineAttribs: [ '+10' ],
expectedText: ['Text with more than one space.']
},
'nonelistiteminlist #3620': {
//fails as a new list item is introduced
disabled:true,
description: 'Text content between and - ',
html: '',
expectedLineAttribs: ['*0*2+1+4', '*0*1*2+1+3'],
expectedText: ['test', '*FOO']
},
'whitespaceinUL #3620': {
//fails as a new list item is introduced
disabled:true,
description: 'Only spaces and no text between
and - ',
html: '',
expectedLineAttribs: ['*0*1*2+1+3'],
expectedText: ['*FOO']
},
whitespaceinBulletList: {
//fails as a new list item is introduced
disabled:true,
description: 'Only spaces and no text between
and - in an bullet list',
html: '',
expectedLineAttribs: ['*0*1*2+1+3'],
expectedText: ['*FOO']
},
whitespaceinIntendedList: {
//fails as a new list item is introduced
disabled:true,
description: 'Only spaces and no text between
and - in an indented list',
html: '',
expectedLineAttribs: ['*0*1*2+1+3'],
expectedText: ['*FOO']
},
whitespaceinOrderedList: {
//fails as a new list item is introduced
disabled:true,
description: 'Only spaces and no text between
and - ',
html: '
- FOO
',
expectedLineAttribs: ['*0*1*2*3+1+3'],
expectedText: ['*FOO']
},
prefixcorrectlinenumber: {
description: 'A normal ordered list with two items',
html: '- should be 1
- should be 2
',
expectedLineAttribs: ['*0*1*2*3+1+b', '*0*1*2*3+1+b'],
expectedText: ['*should be 1', '*should be 2']
},
prefixcorrectlinenumbernested: {
description: 'A normal nested ordered list but without the second being inside - ',
html: '
- should be 1
- foo
- should be 2
',
expectedLineAttribs: ['*0*1*2*3+1+b', '*0*4*2*3+1+3', '*0*1*2*5+1+b'],
expectedText: ['*should be 1', '*foo', '*should be 2']
},
prefixcorrectlinenumbernested2: {
description: 'A normal nested ordered list with the second starting in it\'s own - ',
html: '
- should be 1
- foo
- should be 2
',
expectedLineAttribs: ['*0*1*2*3+1+b', '*0*4*2*3+1+3', '*0*1*2*5+1+b'],
expectedText: ['*should be 1', '*foo', '*should be 2']
},
prefixcorrectlinenumbernested3: {
description: 'A normal nested ordered list with ',
html: '- should be 1
- foo
- should be 2
',
expectedLineAttribs: ['*0*1*2*3+1+b', '*0*4*2*3+1+3', '*0*1*2*5+1+b'],
expectedText: ['*should be 1', '*foo', '*should be 2']
},
//
// /*
// "prefixcorrectlinenumber when introduced none list item - currently not supported see #3450":{
// html: '- should be 1
test- should be 2
',
// expectedLineAttribs: '- should be 1
test- should be 2
',
// expectedText: '\t1. should be 1\n\ttest\n\t2. should be 2\n\n'
// }
// ,
// "newlinesshouldntresetlinenumber #2194":{
// html: '- should be 1
test- should be 2
',
// expectedLineAttribs: '- should be 1
test- should be 2
',
// expectedText: '\t1. should be 1\n\ttest\n\t2. should be 2\n\n'
// }
// */
// 'nestedULsfollowedByNestedOLs': {
// html: `
//
//
// - one
// - two
// - 0
// - 1
// - 2
//
//
//
//
// - item
//
// - item1
// - item2
//
//
//
`,
// expectedLineAttribs: '- item
- item1
- item2
',
// expectedText: '\t* one\n\t* two\n\t* 0\n\t* 1\n\t* 2\n\t\t* 3\n\t\t* 4\n\t1. item\n\t\t1.1. item1\n\t\t1.2. item2\n\n'
// }
}
describe(__filename, function () {
for (const test in tests) {
const testObj = tests[test];
describe(test, function () {
if (testObj.disabled) {
return xit('DISABLED:', test, function (done) {
done();
});
}
it(testObj.description, function (done) {
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 recievedAttributes = result.lineAttribs;
const expectedAttributes = testObj.expectedLineAttribs;
// console.warn(result)
// console.warn("the pool:",apool)
const recievedText = new Array(result.lines);
const expectedText = testObj.expectedText;
// Check recieved text matches the expected text
if (arraysEqual(recievedText[0], expectedText)) {
// console.log("PASS: Recieved Text did match Expected Text\nRecieved:", recievedText[0], "\nExpected:", testObj.expectedText)
} else {
console.error('FAIL: Recieved Text did not match Expected Text\nRecieved:', recievedText[0], '\nExpected:', testObj.expectedText);
throw new Error();
}
// Check recieved attributes matches the expected attributes
if (arraysEqual(recievedAttributes, expectedAttributes)) {
// console.log("PASS: Recieved Attributes matched Expected Attributes");
done();
} else {
console.error('FAIL', test, testObj.description);
console.error('FAIL: Recieved Attributes did not match Expected Attributes\nRecieved: ', recievedAttributes, '\nExpected: ', expectedAttributes);
console.error('FAILING HTML', testObj.html);
throw new Error();
}
});
});
}
});
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;
}