diff options
Diffstat (limited to 'polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html')
-rw-r--r-- | polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html | 542 |
1 files changed, 458 insertions, 84 deletions
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html index f9e465e28d..3238cbc16d 100644 --- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html +++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html @@ -1,5 +1,6 @@ <!DOCTYPE html> <!-- +@license Copyright (C) 2016 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); @@ -58,10 +59,13 @@ limitations under the License. suite('gr-diff-builder tests', () => { let element; let builder; + let createThreadGroupFn; let sandbox; + const LINE_FEED_HTML = '<span class="style-scope gr-diff br"></span>'; setup(() => { sandbox = sinon.sandbox.create(); + element = fixture('basic'); stub('gr-rest-api-interface', { getLoggedIn() { return Promise.resolve(false); }, getProjectConfig() { return Promise.resolve({}); }, @@ -71,13 +75,182 @@ limitations under the License. show_tabs: true, tab_size: 4, }; - const projectName = 'my-project'; + createThreadGroupFn = sinon.spy(() => ({ + setAttribute: sinon.spy(), + })); builder = new GrDiffBuilder( - {content: []}, {left: [], right: []}, prefs, projectName); + {content: []}, {left: [], right: []}, createThreadGroupFn, prefs); }); teardown(() => { sandbox.restore(); }); + test('_getThreads', () => { + const patchForNewThreads = 3; + const comments = [ + { + id: 'sallys_confession', + message: 'i like you, jack', + updated: '2015-12-23 15:00:20.396000000', + __commentSide: 'left', + }, { + id: 'jacks_reply', + message: 'i like you, too', + updated: '2015-12-24 15:01:20.396000000', + __commentSide: 'left', + in_reply_to: 'sallys_confession', + }, + { + id: 'new_draft', + message: 'i do not like either of you', + __commentSide: 'left', + __draft: true, + updated: '2015-12-20 15:01:20.396000000', + }, + ]; + + let expectedThreadGroups = [ + { + start_datetime: '2015-12-23 15:00:20.396000000', + commentSide: 'left', + comments: [{ + id: 'sallys_confession', + message: 'i like you, jack', + updated: '2015-12-23 15:00:20.396000000', + __commentSide: 'left', + }, { + id: 'jacks_reply', + message: 'i like you, too', + updated: '2015-12-24 15:01:20.396000000', + __commentSide: 'left', + in_reply_to: 'sallys_confession', + }], + rootId: 'sallys_confession', + patchNum: 3, + }, + { + start_datetime: '2015-12-20 15:01:20.396000000', + commentSide: 'left', + comments: [ + { + id: 'new_draft', + message: 'i do not like either of you', + __commentSide: 'left', + __draft: true, + updated: '2015-12-20 15:01:20.396000000', + }, + ], + rootId: 'new_draft', + patchNum: 3, + }, + ]; + + assert.deepEqual( + builder._getThreads(comments, patchForNewThreads), + expectedThreadGroups); + + // Patch num should get inherited from comment rather + comments.push({ + id: 'betsys_confession', + message: 'i like you, jack', + updated: '2015-12-24 15:00:10.396000000', + range: { + start_line: 1, + start_character: 1, + end_line: 1, + end_character: 2, + }, + __commentSide: 'left', + }); + + expectedThreadGroups = [ + { + start_datetime: '2015-12-23 15:00:20.396000000', + commentSide: 'left', + comments: [{ + id: 'sallys_confession', + message: 'i like you, jack', + updated: '2015-12-23 15:00:20.396000000', + __commentSide: 'left', + }, { + id: 'jacks_reply', + in_reply_to: 'sallys_confession', + message: 'i like you, too', + updated: '2015-12-24 15:01:20.396000000', + __commentSide: 'left', + }], + patchNum: 3, + rootId: 'sallys_confession', + }, + { + start_datetime: '2015-12-24 15:00:10.396000000', + commentSide: 'left', + comments: [{ + id: 'betsys_confession', + message: 'i like you, jack', + updated: '2015-12-24 15:00:10.396000000', + range: { + start_line: 1, + start_character: 1, + end_line: 1, + end_character: 2, + }, + __commentSide: 'left', + }], + patchNum: 3, + rootId: 'betsys_confession', + range: { + start_line: 1, + start_character: 1, + end_line: 1, + end_character: 2, + }, + }, + { + start_datetime: '2015-12-20 15:01:20.396000000', + commentSide: 'left', + comments: [ + { + id: 'new_draft', + message: 'i do not like either of you', + __commentSide: 'left', + __draft: true, + updated: '2015-12-20 15:01:20.396000000', + }, + ], + rootId: 'new_draft', + patchNum: 3, + }, + ]; + + assert.deepEqual( + builder._getThreads(comments, patchForNewThreads), + expectedThreadGroups); + }); + + test('multiple comments at same location but not threaded', () => { + const comments = [ + { + id: 'sallys_confession', + message: 'i like you, jack', + updated: '2015-12-23 15:00:20.396000000', + __commentSide: 'left', + }, { + id: 'jacks_reply', + message: 'i like you, too', + updated: '2015-12-24 15:01:20.396000000', + __commentSide: 'left', + }, + ]; + assert.equal(builder._getThreads(comments, '3').length, 2); + }); + + test('_createElement classStr applies all classes', () => { + const node = builder._createElement('div', 'test classes'); + assert.isTrue(node.classList.contains('gr-diff')); + assert.isTrue(node.classList.contains('test')); + assert.isTrue(node.classList.contains('classes')); + }); + test('context control buttons', () => { const section = {}; const line = {contextGroup: {lines: []}}; @@ -92,7 +265,7 @@ limitations under the License. let buttons = td.querySelectorAll('gr-button.showContext'); assert.equal(buttons.length, 1); - assert.equal(buttons[0].textContent, 'Show 10 common lines'); + assert.equal(Polymer.dom(buttons[0]).textContent, 'Show 10 common lines'); // Add another line. line.contextGroup.lines.push('lorem upsum'); @@ -102,111 +275,146 @@ limitations under the License. buttons = td.querySelectorAll('gr-button.showContext'); assert.equal(buttons.length, 3); - assert.equal(buttons[0].textContent, '+10โ'); - assert.equal(buttons[1].textContent, 'Show 11 common lines'); - assert.equal(buttons[2].textContent, '+10โ'); + assert.equal(Polymer.dom(buttons[0]).textContent, '+10โ'); + assert.equal(Polymer.dom(buttons[1]).textContent, 'Show 11 common lines'); + assert.equal(Polymer.dom(buttons[2]).textContent, '+10โ'); }); test('newlines 1', () => { let text = 'abcdef'; - assert.equal(builder._addNewlines(text, text), text); + + assert.equal(builder._formatText(text, 4, 10).innerHTML, text); text = 'a'.repeat(20); - assert.equal(builder._addNewlines(text, text), + assert.equal(builder._formatText(text, 4, 10).innerHTML, 'a'.repeat(10) + - GrDiffBuilder.LINE_FEED_HTML + + LINE_FEED_HTML + 'a'.repeat(10)); }); test('newlines 2', () => { const text = '<span class="thumbsup">๐</span>'; - const html = - '<span class="thumbsup">๐</span>'; - assert.equal(builder._addNewlines(text, html), + assert.equal(builder._formatText(text, 4, 10).innerHTML, '<span clas' + - GrDiffBuilder.LINE_FEED_HTML + - 's="thumbsu' + - GrDiffBuilder.LINE_FEED_HTML + - 'p">๐</spa' + - GrDiffBuilder.LINE_FEED_HTML + - 'n>'); + LINE_FEED_HTML + + 's="thumbsu' + + LINE_FEED_HTML + + 'p">๐</span' + + LINE_FEED_HTML + + '>'); }); test('newlines 3', () => { const text = '01234\t56789'; - const html = '01234<span>\t</span>56789'; - assert.equal(builder._addNewlines(text, html), - '01234<span>\t</span>5' + - GrDiffBuilder.LINE_FEED_HTML + - '6789'); + assert.equal(builder._formatText(text, 4, 10).innerHTML, + '01234' + builder._getTabWrapper(3).outerHTML + '56' + + LINE_FEED_HTML + + '789'); }); - test('_addNewlines not called if line_wrapping is true', done => { + test('newlines 4', () => { + const text = '๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐'; + assert.equal(builder._formatText(text, 4, 20).innerHTML, + '๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐' + + LINE_FEED_HTML + + '๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐' + + LINE_FEED_HTML + + '๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐'); + }); + + + test('line_length ignored if line_wrapping is true', () => { builder._prefs = {line_wrapping: true, tab_size: 4, line_length: 50}; - const text = (new Array(52)).join('a'); + const text = 'a'.repeat(51); const line = {text, highlights: []}; - const newLineStub = sandbox.stub(builder, '_addNewlines'); - builder._createTextEl(line); - flush(() => { - assert.isFalse(newLineStub.called); - done(); - }); + const result = builder._createTextEl(line).firstChild.innerHTML; + assert.equal(result, text); }); - test('_addNewlines called if line_wrapping is true and meets other ' + - 'conditions', done => { + test('line_length applied if line_wrapping is false', () => { builder._prefs = {line_wrapping: false, tab_size: 4, line_length: 50}; - const text = (new Array(52)).join('a'); + const text = 'a'.repeat(51); const line = {text, highlights: []}; - const newLineStub = sandbox.stub(builder, '_addNewlines'); - builder._createTextEl(line); - - flush(() => { - assert.isTrue(newLineStub.called); - done(); - }); + const expected = 'a'.repeat(50) + LINE_FEED_HTML + 'a'; + const result = builder._createTextEl(line).firstChild.innerHTML; + assert.equal(result, expected); }); test('_createTextEl linewrap with tabs', () => { - const text = _.times(7, _.constant('\t')).join('') + '!'; + const text = '\t'.repeat(7) + '!'; const line = {text, highlights: []}; const el = builder._createTextEl(line); - const tabEl = el.querySelector('.contentText > .br'); - assert.isOk(tabEl); + assert.equal(el.innerText, text); + // With line length 10 and tab size 2, there should be a line break + // after every two tabs. + const newlineEl = el.querySelector('.contentText > .br'); + assert.isOk(newlineEl); assert.equal( el.querySelector('.contentText .tab:nth-child(2)').nextSibling, - tabEl); + newlineEl); }); test('text length with tabs and unicode', () => { - assert.equal(builder._textLength('12345', 4), 5); - assert.equal(builder._textLength('\t\t12', 4), 10); - assert.equal(builder._textLength('abc๐ข123', 4), 7); - - assert.equal(builder._textLength('abc\t', 8), 8); - assert.equal(builder._textLength('abc\t\t', 10), 20); - assert.equal(builder._textLength('', 10), 0); - assert.equal(builder._textLength('', 10), 0); - assert.equal(builder._textLength('abc\tde', 10), 12); - assert.equal(builder._textLength('abc\tde\t', 10), 20); - assert.equal(builder._textLength('\t\t\t\t\t', 20), 100); + function expectTextLength(text, tabSize, expected) { + // Formatting to |expected| columns should not introduce line breaks. + const result = builder._formatText(text, tabSize, expected); + assert.isNotOk(result.querySelector('.contentText > .br'), + ` Expected the result of: \n` + + ` _formatText(${text}', ${tabSize}, ${expected})\n` + + ` to not contain a br. But the actual result HTML was:\n` + + ` '${result.innerHTML}'\nwhereupon`); + + // Increasing the line limit should produce the same markup. + assert.equal(builder._formatText(text, tabSize, Infinity).innerHTML, + result.innerHTML); + assert.equal(builder._formatText(text, tabSize, expected + 1).innerHTML, + result.innerHTML); + + // Decreasing the line limit should introduce line breaks. + if (expected > 0) { + const tooSmall = builder._formatText(text, tabSize, expected - 1); + assert.isOk(tooSmall.querySelector('.contentText > .br'), + ` Expected the result of: \n` + + ` _formatText(${text}', ${tabSize}, ${expected - 1})\n` + + ` to contain a br. But the actual result HTML was:\n` + + ` '${tooSmall.innerHTML}'\nwhereupon`); + } + } + expectTextLength('12345', 4, 5); + expectTextLength('\t\t12', 4, 10); + expectTextLength('abc๐ข123', 4, 7); + expectTextLength('abc\t', 8, 8); + expectTextLength('abc\t\t', 10, 20); + expectTextLength('', 10, 0); + expectTextLength('', 10, 0); + // 17 Thai combining chars. + expectTextLength('เธเนเนเนเนเนเนเนเนเนเนเนเนเนเนเนเน', 4, 17); + expectTextLength('abc\tde', 10, 12); + expectTextLength('abc\tde\t', 10, 20); + expectTextLength('\t\t\t\t\t', 20, 100); }); test('tab wrapper insertion', () => { const html = 'abc\tdef'; - const wrapper = builder._getTabWrapper( - builder._prefs.tab_size - 3, - builder._prefs.show_tabs); + const tabSize = builder._prefs.tab_size; + const wrapper = builder._getTabWrapper(tabSize - 3); assert.ok(wrapper); - assert.isAbove(wrapper.length, 0); - assert.equal(builder._addTabWrappers(html, builder._prefs.tab_size), - 'abc' + wrapper + 'def'); - assert.throws(builder._getTabWrapper.bind( - builder, - // using \x3c instead of < in string so gjslint can parse - '">\x3cimg src="/" onerror="alert(1);">\x3cspan class="', - true)); + assert.equal(wrapper.innerText, '\t'); + assert.equal( + builder._formatText(html, tabSize, Infinity).innerHTML, + 'abc' + wrapper.outerHTML + 'def'); + }); + + test('tab wrapper style', () => { + const pattern = new RegExp('^<span class="style-scope gr-diff tab" ' + + 'style="(?:-moz-)?tab-size: (\\d+);">\\t<\\/span>$'); + + for (const size of [1, 3, 8, 55]) { + const html = builder._getTabWrapper(size).outerHTML; + expect(html).to.match(pattern); + assert.equal(html.match(pattern)[1], size); + } }); test('comments', () => { @@ -264,41 +472,56 @@ limitations under the License. right: [r5], }; + function threadForComment(c, patchNum) { + return { + commentSide: c.__commentSide, + comments: [c], + patchNum, + rootId: c.id, + start_datetime: c.updated, + }; + } + function checkThreadGroupProps(threadGroupEl, patchNum, isOnParent, comments) { - assert.equal(threadGroupEl.changeNum, '42'); - assert.equal(threadGroupEl.patchForNewThreads, patchNum); - assert.equal(threadGroupEl.path, '/path/to/foo'); - assert.equal(threadGroupEl.isOnParent, isOnParent); - assert.deepEqual(threadGroupEl.projectName, 'my-project'); - assert.deepEqual(threadGroupEl.comments, comments); + assert.equal(createThreadGroupFn.lastCall.args[0], patchNum); + assert.equal(createThreadGroupFn.lastCall.args[1], isOnParent); + assert.deepEqual( + threadGroupEl.threads, + comments.map(c => threadForComment(c, patchNum))); } let line = new GrDiffLine(GrDiffLine.Type.BOTH); line.beforeNumber = 5; line.afterNumber = 5; let threadGroupEl = builder._commentThreadGroupForLine(line); + assert.isTrue(createThreadGroupFn.calledOnce); checkThreadGroupProps(threadGroupEl, '3', false, [l5, r5]); threadGroupEl = builder._commentThreadGroupForLine(line, GrDiffBuilder.Side.RIGHT); + assert.isTrue(createThreadGroupFn.calledTwice); checkThreadGroupProps(threadGroupEl, '3', false, [r5]); threadGroupEl = builder._commentThreadGroupForLine(line, GrDiffBuilder.Side.LEFT); + assert.isTrue(createThreadGroupFn.calledThrice); checkThreadGroupProps(threadGroupEl, '3', true, [l5]); builder._comments.meta.patchRange.basePatchNum = '1'; threadGroupEl = builder._commentThreadGroupForLine(line); + assert.equal(createThreadGroupFn.callCount, 4); checkThreadGroupProps(threadGroupEl, '3', false, [l5, r5]); threadEl = builder._commentThreadGroupForLine(line, GrDiffBuilder.Side.LEFT); + assert.equal(createThreadGroupFn.callCount, 5); checkThreadGroupProps(threadEl, '1', false, [l5]); threadGroupEl = builder._commentThreadGroupForLine(line, GrDiffBuilder.Side.RIGHT); + assert.equal(createThreadGroupFn.callCount, 6); checkThreadGroupProps(threadGroupEl, '3', false, [r5]); builder._comments.meta.patchRange.basePatchNum = 'PARENT'; @@ -307,15 +530,60 @@ limitations under the License. line.beforeNumber = 5; line.afterNumber = 5; threadGroupEl = builder._commentThreadGroupForLine(line); + assert.equal(createThreadGroupFn.callCount, 7); checkThreadGroupProps(threadGroupEl, '3', true, [l5, r5]); line = new GrDiffLine(GrDiffLine.Type.ADD); line.beforeNumber = 3; line.afterNumber = 5; threadGroupEl = builder._commentThreadGroupForLine(line); + assert.equal(createThreadGroupFn.callCount, 8); checkThreadGroupProps(threadGroupEl, '3', false, [l3, r5]); }); + + test('_handlePreferenceError called with invalid preference', () => { + sandbox.stub(element, '_handlePreferenceError'); + const prefs = {tab_size: 0}; + element._getDiffBuilder(element.diff, element.comments, prefs); + assert.isTrue(element._handlePreferenceError.lastCall + .calledWithExactly('tab size')); + }); + + test('_handlePreferenceError triggers alert and javascript error', () => { + const errorStub = sinon.stub(); + element.addEventListener('show-alert', errorStub); + assert.throws(element._handlePreferenceError.bind(element, 'tab size')); + assert.equal(errorStub.lastCall.args[0].detail.message, + `The value of the 'tab size' user preference is invalid. ` + + `Fix in diff preferences`); + }); + + test('_getKeyLocations', () => { + assert.deepEqual(element._getKeyLocations({left: [], right: []}, null), + {left: {}, right: {}}); + const comments = { + left: [{line: 123}, {}], + right: [{line: 456}], + }; + assert.deepEqual(element._getKeyLocations(comments, null), { + left: {FILE: true, 123: true}, + right: {456: true}, + }); + + const lineOfInterest = {number: 789, leftSide: true}; + assert.deepEqual(element._getKeyLocations(comments, lineOfInterest), { + left: {FILE: true, 123: true, 789: true}, + right: {456: true}, + }); + + delete lineOfInterest.leftSide; + assert.deepEqual(element._getKeyLocations(comments, lineOfInterest), { + left: {FILE: true, 123: true}, + right: {456: true, 789: true}, + }); + }); + suite('_isTotal', () => { test('is total for add', () => { const group = new GrDiffGroup(GrDiffGroup.Type.DELTA); @@ -621,6 +889,33 @@ limitations under the License. }); }); + suite('layers from plugins', () => { + let element; + let initialLayersCount; + + setup(() => { + element = fixture('basic'); + element._showTrailingWhitespace = true; + initialLayersCount = element._layers.length; + }); + + test('no plugin layers', () => { + const getDiffLayersStub = sinon.stub(element.$.jsAPI, 'getDiffLayers') + .returns([]); + element.attached(); + assert.isTrue(getDiffLayersStub.called); + assert.equal(element._layers.length, initialLayersCount); + }); + + test('with plugin layers', () => { + const getDiffLayersStub = sinon.stub(element.$.jsAPI, 'getDiffLayers') + .returns([{}, {}]); + element.attached(); + assert.isTrue(getDiffLayersStub.called); + assert.equal(element._layers.length, initialLayersCount+2); + }); + }); + suite('trailing whitespace', () => { let element; let layer; @@ -716,6 +1011,63 @@ limitations under the License. }); }); + suite('rendering text, images and binary files', () => { + let processStub; + let comments; + let prefs; + let content; + + setup(() => { + element = fixture('basic'); + element.viewMode = 'SIDE_BY_SIDE'; + processStub = sandbox.stub(element.$.processor, 'process') + .returns(Promise.resolve()); + sandbox.stub(element, '_anyLineTooLong').returns(true); + comments = {left: [], right: []}; + prefs = { + line_length: 10, + show_tabs: true, + tab_size: 4, + context: -1, + syntax_highlighting: true, + }; + content = [{ + a: ['all work and no play make andybons a dull boy'], + b: ['elgoog elgoog elgoog'], + }, { + ab: [ + 'Non eram nescius, Brute, cum, quae summis ingeniis ', + 'exquisitaque doctrina philosophi Graeco sermone tractavissent', + ], + }]; + }); + + test('text', () => { + element.diff = {content}; + return element.render(comments, prefs).then(() => { + assert.isTrue(processStub.calledOnce); + assert.isFalse(processStub.lastCall.args[1]); + }); + }); + + test('image', () => { + element.diff = {content, binary: true}; + element.isImageDiff = true; + return element.render(comments, prefs).then(() => { + assert.isTrue(processStub.calledOnce); + assert.isTrue(processStub.lastCall.args[1]); + }); + }); + + test('binary', () => { + element.diff = {content, binary: true}; + return element.render(comments, prefs).then(() => { + assert.isTrue(processStub.calledOnce); + assert.isTrue(processStub.lastCall.args[1]); + }); + }); + }); + suite('rendering', () => { let content; let outputEl; @@ -748,7 +1100,7 @@ limitations under the License. outputEl = element.queryEffectiveChildren('#diffTable'); sandbox.stub(element, '_getDiffBuilder', () => { const builder = new GrDiffBuilder( - {content}, {left: [], right: []}, prefs, 'my-project', outputEl); + {content}, {left: [], right: []}, null, prefs, outputEl); sandbox.stub(builder, 'addColumns'); builder.buildSectionElement = function(group) { const section = document.createElement('stub'); @@ -872,6 +1224,10 @@ limitations under the License. }); }); + test('getDiffLength', () => { + assert.equal(element.getDiffLength(diff), 52); + }); + test('getContentByLine', () => { let actual; @@ -922,6 +1278,29 @@ limitations under the License. }); }); + test('_renderContentByRange notexistent elements', () => { + const spy = sandbox.spy(builder, '_createTextEl'); + + sandbox.stub(builder, 'findLinesByRange', + (s, e, d, lines, elements) => { + // Add a line and a corresponding element. + lines.push(new GrDiffLine(GrDiffLine.Type.BOTH)); + const parEl = document.createElement('div'); + const el = document.createElement('div'); + parEl.appendChild(el); + elements.push(el); + + // Add 2 lines without corresponding elements. + lines.push(new GrDiffLine(GrDiffLine.Type.BOTH)); + lines.push(new GrDiffLine(GrDiffLine.Type.BOTH)); + }); + + builder._renderContentByRange(1, 10, 'left'); + // Should be called only once because only one line had a corresponding + // element. + assert.equal(spy.callCount, 1); + }); + test('_getNextContentOnSide side-by-side left', () => { const startElem = builder.getContentByLine(5, 'left', element.$.diffTable); @@ -986,20 +1365,15 @@ limitations under the License. }); }); - test('_escapeHTML', () => { + test('escaping HTML', () => { let input = '<script>alert("XSS");<' + '/script>'; - let expected = '<script>alert("XSS");' + - '</script>'; - let result = GrDiffBuilder.prototype._escapeHTML(input); + let expected = '<script>alert("XSS");</script>'; + let result = builder._formatText(input, 1, Infinity).innerHTML; assert.equal(result, expected); input = '& < > " \' / `'; - - // \u0026 is an ampersand. This is being used here instead of & - // because of the gjslinter. - expected = '\u0026amp; \u0026lt; \u0026gt; \u0026quot;' + - ' \u0026#39; \u0026#x2F; \u0026#96;'; - result = GrDiffBuilder.prototype._escapeHTML(input); + expected = '& < > " \' / `'; + result = builder._formatText(input, 1, Infinity).innerHTML; assert.equal(result, expected); }); }); |