| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679 |
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8">
- <title>Markdown Unit Tests</title>
- <link rel="icon" href="data:;base64,iVBORw0KGgo=">
- <style type="text/css">
- :root {
- font-family: sans-serif;
- --color-passed: #090;
- --color-failed: #a00;
- --color-errored: #a80;
- --color-untested: #888;
- }
- .testclass {
- border: 1px solid black;
- padding: 0.5em 1em;
- margin-bottom: 1em;
- max-width: 50em;
- }
- .testclassname {
- font-weight: bold;
- font-size: 1.25rem;
- padding-bottom: 0.25em;
- }
- .testclassstatus {
-
- }
- .testclassstatus.passed { color: var(--color-passed); }
- .testclassstatus.failed { color: var(--color-failed); }
- .testclassstatus.errored { color: var(--color-errored); }
- .testclassstatus.untested { color: var(--color-untested); }
- .testcase {
- clear: both;
- padding: 0.2em 0;
- margin-left: 2em;
- }
- .testcase {
- border-top: 1px solid #888;
- }
- .testcasename {
- font-family: monospace;
- }
- .testcasestatus {
- font-weight: bold;
- font-size: 80%;
- float: left;
- }
- .testcasetiming {
- float: right;
- color: #888;
- font-size: 80%;
- }
- .testcaseresult {
- height: 1em;
- }
- .testcasemessage {
- clear: both;
- }
- .result-untested {
- color: #888;
- }
- .result-testing {
- color: black;
- }
- .result-passed {
- color: #090;
- }
- .result-failed {
- color: #a00;
- }
- .result-errored {
- color: #a80;
- }
- </style>
- <script src="js/markdown.js"></script>
- <script src="js/spreadsheet.js"></script>
- <!-- Testing infrastructure -->
- <script>
- /**
- * @param {String} text
- * @returns {String}
- */
- function escapeHTML(text) {
- return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br/>\n');
- }
-
- class ResultType {
- static untested = new ResultType('untested');
- static testing = new ResultType('testing');
- static passed = new ResultType('passed');
- static failed = new ResultType('failed');
- static errored = new ResultType('errored');
-
- #name;
-
- constructor(name) {
- this.#name = name;
- }
-
- toString() {
- return `${this.constructor.name}.${this.#name}`;
- }
- }
-
- class FailureError extends Error {
- constructor(message=null) {
- super(message);
- }
- }
-
- class BaseTest {
- setUp() {}
- tearDown() {}
- /** @var {TestCaseRunner|null} */
- currentRunner = null;
- fail(failMessage=null) {
- throw new FailureError(failMessage || 'failed');
- }
- assertTrue(test, failMessage=null) {
- if (!test) this.fail(failMessage || `expected true, got ${test}`);
- }
- assertFalse(test, failMessage=null) {
- if (test) this.fail(failMessage || `expected false, got ${test}`);
- }
- assertEqual(a, b, floatDifferenceRatio=0.000001, failMessage=null) {
- if (MDUtils.equal(a, b, floatDifferenceRatio)) return;
- const aVal = (typeof a == 'string') ? `"${a}"` : `${a}`;
- const bVal = (typeof b == 'string') ? `"${b}"` : `${b}`;
- if (aVal.length > 20 || bVal.length > 20) {
- this.fail(failMessage || `equality failed:\n${aVal}\n!=\n${bVal}`);
- } else {
- this.fail(failMessage || `equality failed: ${aVal} != ${bVal}`);
- }
- }
- expectError(e=true) {
- if (this.currentRunner) this.currentRunner.expectedError = e;
- }
- /**
- * @param {number} maxTime maximum time in seconds
- * @param {function} timedCode code to run and time
- */
- profile(maxTime, timedCode) {
- const startTime = performance.now();
- const result = timedCode();
- const endTime = performance.now();
- const seconds = (endTime - startTime) / 1000.0;
- if (seconds > maxTime) {
- this.fail(`Expected <= ${maxTime}s execution time, actual ${seconds}s.`);
- }
- return result;
- }
- }
-
- /**
- * Manages the running and results of a single test method on a test
- * class.
- */
- class TestCaseRunner {
- /** @var {number} */
- static #nextUniqueId = 1;
- /** @var {number} */
- uniqueId = 0;
- /** @var {BaseTest} */
- #objectUnderTest;
- /** @var {method} */
- #method;
- /** @var {ResultType} */
- result = ResultType.untested;
- /** @var {String|null} */
- message = null;
- expectedError = null;
- duration = null;
- /** @var {String} */
- get className() { return this.#objectUnderTest.constructor.name; }
- /** @var {String} */
- get methodName() { return this.#method.name; }
- /**
- * @param {BaseTest} objectUnderTest
- * @param {method} method
- */
- constructor(objectUnderTest, method) {
- this.#objectUnderTest = objectUnderTest;
- this.#method = method;
- this.uniqueId = TestCaseRunner.#nextUniqueId++;
- }
- run() {
- var start;
- this.expectedError = null;
- this.#objectUnderTest.currentRunner = this;
- try {
- this.#objectUnderTest.setUp();
- } catch (e) {
- console.error(`Failed to run ${this.className}.setUp() - ${e.message}`);
- this.result = ResultType.errored;
- this.message = e.message;
- return;
- }
- try {
- start = performance.now();
- this.#method.bind(this.#objectUnderTest)();
- this.duration = performance.now() - start;
- this.result = ResultType.passed;
- this.message = null;
- } catch (e) {
- this.duration = performance.now() - start;
- if (e instanceof FailureError) {
- this.result = ResultType.failed;
- this.message = e.message;
- } else if (this.#isExpectedError(e)) {
- this.result = ResultType.passed;
- this.message = null;
- } else {
- this.result = ResultType.errored;
- this.message = e.message;
- if (e.stack !== undefined) {
- this.message += "\n" + e.stack;
- }
- }
- } finally {
- this.expectedError = null;
- try {
- this.#objectUnderTest.tearDown();
- this.#objectUnderTest.currentRunner = null;
- } catch (e0) {
- console.error(`Failed to run ${this.className}.tearDown() - ${e0.message}`);
- this.result = ResultType.errored;
- this.message = e0.message;
- }
- }
- }
- get #cssId() { return `testcase${this.uniqueId}`; }
- #isExpectedError(e) {
- if (this.expectedError === null) return false;
- if (this.expectedError === true) return true;
- // TODO: Have a way to specify details about what kind of error is expected. Maybe a prototype instance and/or a testing lambda.
- return false;
- }
- toHTML() {
- var html = `<div class="testcase" id="${this.#cssId}">`;
- html += `<div class="testcasename"><span class="testcasemethod">${this.methodName}</span></div>`;
- html += '<div class="testcaseresult">';
- switch (this.result) {
- case ResultType.untested:
- html += '<div class="testcasestatus result-untested">Waiting to test</div>';
- break;
- case ResultType.testing:
- html += '<div class="testcasestatus result-testing">Testing...</div>';
- break;
- case ResultType.passed:
- html += '<div class="testcasestatus result-passed">Passed</div>';
- break;
- case ResultType.failed:
- html += '<div class="testcasestatus result-failed">Failed</div>';
- break;
- case ResultType.errored:
- html += '<div class="testcasestatus result-errored">Errored</div>';
- break;
- }
- if (this.duration !== null) {
- html += `<div class="testcasetiming">${Number(this.duration / 1000.0).toFixed(3)}s</div>`;
- }
- html += '</div>';
- if (this.message !== null) {
- html += `<div class="testcasemessage">${escapeHTML(this.message)}</div>`;
- }
- html += `</div>`;
- return html;
- }
- /**
- * Updates the HTML node in-place with the current status, or
- * adds it if it does not exist yet.
- */
- updateHTML() {
- let existing = document.getElementById(this.#cssId);
- if (existing) {
- existing.outerHTML = this.toHTML();
- } else {
- document.getElementById('results').innerHTML += this.toHTML();
- }
- }
-
- /**
- * @param {object} objectUnderTest
- * @returns {TestCaseRunner[]}
- */
- static findTestCases(objectUnderTest) {
- if (!(objectUnderTest instanceof BaseTest)) return [];
- var members = [];
- var obj = objectUnderTest;
- do {
- members.push(...Object.getOwnPropertyNames(obj));
- } while (obj = Object.getPrototypeOf(obj));
- return members.sort().filter((e, i, arr) => {
- if (e != arr[i + 1] && typeof objectUnderTest[e] == 'function' && e.startsWith('test')) return true;
- }).map((name) => {
- return new TestCaseRunner(objectUnderTest, objectUnderTest[name]);
- });
- }
- }
-
- class TestClassRunner {
- static #nextUniqueId = 1;
-
- #uniqueId = 0;
- #theClass;
- #instance;
- #testCases;
-
- get testCases() { return this.#testCases; }
-
- constructor(theClass) {
- this.#theClass = theClass;
- this.#instance = new theClass();
- this.#testCases = TestCaseRunner.findTestCases(this.#instance);
- this.#uniqueId = TestClassRunner.#nextUniqueId++;
- }
- get #cssId() { return `testclass${this.#uniqueId}`; }
- #summaryHTML() {
- var anyTesting = false;
- var anyFailed = false;
- var anyErrored = false;
- var anyUntested = false;
- var anyPassed = false;
- var allPassed = true;
- for (const test of this.testCases) {
- switch (test.result) {
- case ResultType.untested:
- anyUntested = true;
- allPassed = false;
- break;
- case ResultType.testing:
- anyTesting = true;
- allPassed = false;
- break;
- case ResultType.passed:
- anyPassed = true;
- break;
- case ResultType.failed:
- anyFailed = true;
- allPassed = false;
- break;
- case ResultType.errored:
- anyErrored = true;
- allPassed = false;
- break;
- }
- }
- var html = '';
- html += `<summary class="testclasssummary" id="${this.#cssId}summary">`;
- html += `<span class="testclassname">${this.#theClass.name}</span> `;
- if (anyTesting || (anyUntested && (anyPassed || anyFailed || anyErrored))) {
- html += '<span class="testclassstatus testing">Testing...</span>';
- } else if (anyErrored) {
- html += '<span class="testclassstatus errored">Errored</span>';
- } else if (anyFailed) {
- html += '<span class="testclassstatus failed">Failed</span>';
- } else if (allPassed) {
- html += '<span class="testclassstatus passed">Passed</span>';
- }
- html += '</summary>';
- return html;
- }
- toHTML() {
- var html = '';
- html += `<div class="testclass" id="${this.#cssId}">`;
- html += `<details id="${this.#cssId}details">`;
- html += this.#summaryHTML();
- for (const testCase of this.#testCases) {
- html += testCase.toHTML();
- }
- html += '</details>';
- html += '</div>';
- return html;
- }
- updateHTML() {
- var existing = document.getElementById(`${this.#cssId}summary`);
- if (!existing) {
- document.getElementById('results').innerHTML += this.toHTML();
- } else {
- existing.outerHTML = this.#summaryHTML();
- var allPassed = true;
- for (const test of this.testCases) {
- if (test.result != ResultType.passed) {
- allPassed = false;
- break;
- }
- }
- document.getElementById(`${this.#cssId}details`).open = !allPassed;
- }
- }
-
- static runAll(testClasses) {
- var tests = []; // tuples of TestClassRunner and TestCaseRunner
- for (const testClass of testClasses) {
- const classRunner = new TestClassRunner(testClass);
- classRunner.updateHTML();
- tests.push(...classRunner.testCases.map(function(test) { return [ classRunner, test ] }));
- }
- var testInterval = setInterval(function() {
- if (tests.length == 0) {
- clearInterval(testInterval);
- testInterval = null;
- return;
- }
- var classRunner;
- var testCase;
- [ classRunner, testCase ] = tests[0];
- if (testCase.result == ResultType.untested) {
- testCase.result = ResultType.testing;
- testCase.updateHTML();
- classRunner.updateHTML();
- } else if (testCase.result == ResultType.testing) {
- tests.splice(0, 1);
- testCase.run();
- testCase.updateHTML();
- classRunner.updateHTML();
- }
- }, 1);
- }
- }
- </script>
- <!-- Tests -->
- <script>
- function onLoad() {
- let testClasses = [
- // FIXME: Reenable these! Disabled temporarily to test spreadsheets faster.
- // TokenTests,
- // UtilsTests,
- // InlineTests,
- // BlockTests,
- CellValueTests,
- CellAddressRangeTests,
- ExpressionSetTests,
- ];
- TestClassRunner.runAll(testClasses);
- }
- document.addEventListener('DOMContentLoaded', onLoad);
-
- function normalizeWhitespace(str) {
- return str.replace(/\s+/g, ' ').replace(/(?:^\s+|\s+$)/g, '');
- }
-
- class TokenTests extends BaseTest {
- test_findFirstTokens() {
- const tokens = [
- new MDToken('Lorem', MDTokenType.Text),
- new MDToken(' ', MDTokenType.Whitespace),
- new MDToken('_', MDTokenType.Underscore),
- new MDToken('ipsum', MDTokenType.Text),
- new MDToken('_', MDTokenType.Underscore),
- new MDToken(' ', MDTokenType.Whitespace),
- new MDToken('dolor', MDTokenType.Text),
- new MDToken('_', MDTokenType.Underscore),
- new MDToken('sit', MDTokenType.Text),
- new MDToken('_', MDTokenType.Underscore),
- ];
- const pattern = [
- MDTokenType.Underscore,
- ];
- const result = MDToken.findFirstTokens(tokens, pattern);
- const expected = {
- tokens: [ tokens[2] ],
- index: 2,
- };
- this.assertEqual(result, expected);
- }
-
- test_findFirstTokens_optionalWhitespace1() {
- const tokens = [
- new MDToken('Lorem', MDTokenType.Text),
- new MDToken(' ', MDTokenType.Whitespace),
- new MDToken('[ipsum]', MDTokenType.Label, 'ipsum'),
- new MDToken('(link.html)', MDTokenType.URL, 'link.html'),
- new MDToken(' ', MDTokenType.Whitespace),
- new MDToken('dolor', MDTokenType.Text),
- ];
- const pattern = [
- MDTokenType.Label,
- MDTokenType.META_OptionalWhitespace,
- MDTokenType.URL,
- ];
- const result = MDToken.findFirstTokens(tokens, pattern);
- const expected = {
- tokens: [ tokens[2], tokens[3] ],
- index: 2,
- };
- this.assertEqual(result, expected);
- }
-
- test_findFirstTokens_optionalWhitespace2() {
- const tokens = [
- new MDToken('Lorem', MDTokenType.Text),
- new MDToken(' ', MDTokenType.Whitespace),
- new MDToken('[ipsum]', MDTokenType.Label, 'ipsum'),
- new MDToken(' ', MDTokenType.Whitespace),
- new MDToken('(link.html)', MDTokenType.URL, 'link.html'),
- new MDToken(' ', MDTokenType.Whitespace),
- new MDToken('dolor', MDTokenType.Text),
- ];
- const pattern = [
- MDTokenType.Label,
- MDTokenType.META_OptionalWhitespace,
- MDTokenType.URL,
- ];
- const result = MDToken.findFirstTokens(tokens, pattern);
- const expected = {
- tokens: [ tokens[2], tokens[3], tokens[4] ],
- index: 2,
- };
- this.assertEqual(result, expected);
- }
-
- test_findPairedTokens() {
- const tokens = [
- new MDToken('Lorem', MDTokenType.Text),
- new MDToken(' ', MDTokenType.Whitespace),
- new MDToken('_', MDTokenType.Underscore),
- new MDToken('ipsum', MDTokenType.Text),
- new MDToken('_', MDTokenType.Underscore),
- new MDToken(' ', MDTokenType.Whitespace),
- new MDToken('dolor', MDTokenType.Text),
- new MDToken('_', MDTokenType.Underscore),
- new MDToken('sit', MDTokenType.Text),
- new MDToken('_', MDTokenType.Underscore),
- ];
- const pattern = [
- MDTokenType.Underscore,
- ];
- const result = MDToken.findPairedTokens(tokens, pattern, pattern);
- const expected = {
- startTokens: [ tokens[2] ],
- contentTokens: [ tokens[3] ],
- endTokens: [ tokens[4] ],
- startIndex: 2,
- contentIndex: 3,
- endIndex: 4,
- totalLength: 3,
- }
- this.assertEqual(result, expected);
- }
- }
-
- class UtilsTests extends BaseTest {
- test_stripIndent() {
- this.assertEqual(MDUtils.stripIndent(''), '');
- this.assertEqual(MDUtils.stripIndent(' '), '');
- this.assertEqual(MDUtils.stripIndent('foo'), 'foo');
- this.assertEqual(MDUtils.stripIndent(' foo'), 'foo');
- this.assertEqual(MDUtils.stripIndent(' foo'), 'foo');
- this.assertEqual(MDUtils.stripIndent(' foo'), 'foo');
- this.assertEqual(MDUtils.stripIndent(' foo'), 'foo');
- this.assertEqual(MDUtils.stripIndent(' foo'), ' foo');
- this.assertEqual(MDUtils.stripIndent('\tfoo'), 'foo');
- this.assertEqual(MDUtils.stripIndent('\t\tfoo'), '\tfoo');
- this.assertEqual(MDUtils.stripIndent('\t\tfoo', 2), 'foo');
- this.assertEqual(MDUtils.stripIndent(' foo', 2), 'foo');
- }
-
- test_countIndents() {
- this.assertEqual(MDUtils.countIndents(''), 0);
- this.assertEqual(MDUtils.countIndents(' '), 1);
- this.assertEqual(MDUtils.countIndents(' '), 1);
- this.assertEqual(MDUtils.countIndents('foo'), 0);
- this.assertEqual(MDUtils.countIndents('foo'), 0);
- this.assertEqual(MDUtils.countIndents(' foo'), 1);
- this.assertEqual(MDUtils.countIndents(' foo'), 1);
- this.assertEqual(MDUtils.countIndents(' foo'), 1);
- this.assertEqual(MDUtils.countIndents(' foo'), 1);
- this.assertEqual(MDUtils.countIndents(' foo'), 2);
- this.assertEqual(MDUtils.countIndents('\tfoo'), 1);
- this.assertEqual(MDUtils.countIndents('\t\tfoo'), 2);
-
- this.assertEqual(MDUtils.countIndents('', true), 0);
- this.assertEqual(MDUtils.countIndents(' ', true), 0);
- this.assertEqual(MDUtils.countIndents(' ', true), 1);
- this.assertEqual(MDUtils.countIndents('foo', true), 0);
- this.assertEqual(MDUtils.countIndents(' foo', true), 0);
- this.assertEqual(MDUtils.countIndents(' foo', true), 0);
- this.assertEqual(MDUtils.countIndents(' foo', true), 0);
- this.assertEqual(MDUtils.countIndents(' foo', true), 1);
- this.assertEqual(MDUtils.countIndents(' foo', true), 1);
- this.assertEqual(MDUtils.countIndents('\tfoo', true), 1);
- this.assertEqual(MDUtils.countIndents('\t\tfoo', true), 2);
- }
-
- test_tokenizeLabel() {
- // Escapes are preserved
- this.assertEqual(MDUtils.tokenizeLabel('[foo] bar'), [ '[foo]', 'foo' ]);
- this.assertEqual(MDUtils.tokenizeLabel('[foo\\[] bar'), [ '[foo\\[]', 'foo\\[' ]);
- this.assertEqual(MDUtils.tokenizeLabel('[foo\\]] bar'), [ '[foo\\]]', 'foo\\]' ]);
- this.assertEqual(MDUtils.tokenizeLabel('[foo[]] bar'), [ '[foo[]]', 'foo[]' ]);
- this.assertEqual(MDUtils.tokenizeLabel('[foo\\(] bar'), [ '[foo\\(]', 'foo\\(' ]);
- this.assertEqual(MDUtils.tokenizeLabel('[foo\\)] bar'), [ '[foo\\)]', 'foo\\)' ]);
- this.assertEqual(MDUtils.tokenizeLabel('[foo()] bar'), [ '[foo()]', 'foo()' ]);
-
- this.assertEqual(MDUtils.tokenizeLabel('foo bar'), null);
- this.assertEqual(MDUtils.tokenizeLabel('[foo\\] bar'), null);
- this.assertEqual(MDUtils.tokenizeLabel('[foo bar'), null);
- this.assertEqual(MDUtils.tokenizeLabel('[foo[] bar'), null);
- }
-
- test_tokenizeURL() {
- this.assertEqual(MDUtils.tokenizeURL('(page.html) foo'), [ '(page.html)', 'page.html', null ]);
- this.assertEqual(MDUtils.tokenizeURL('(page.html "link title") foo'), [ '(page.html "link title")', 'page.html', 'link title' ]);
- this.assertEqual(MDUtils.tokenizeURL('(https://example.com/path/page.html?query=foo&bar=baz#fragment) foo'), [ '(https://example.com/path/page.html?query=foo&bar=baz#fragment)', 'https://example.com/path/page.html?query=foo&bar=baz#fragment', null ]);
-
- this.assertEqual(MDUtils.tokenizeURL('page.html foo'), null);
- this.assertEqual(MDUtils.tokenizeURL('(page.html foo'), null);
- this.assertEqual(MDUtils.tokenizeURL('page.html) foo'), null);
- this.assertEqual(MDUtils.tokenizeURL('(page.html "title) foo'), null);
- this.assertEqual(MDUtils.tokenizeURL('(page .html) foo'), null);
- this.assertEqual(MDUtils.tokenizeURL('(user@example.com) foo'), null);
- this.assertEqual(MDUtils.tokenizeURL('(user@example.com "title") foo'), null);
- }
-
- test_tokenizeEmail() {
- this.assertEqual(MDUtils.tokenizeEmail('(user@example.com)'), [ '(user@example.com)', 'user@example.com', null ]);
- this.assertEqual(MDUtils.tokenizeEmail('(user@example.com "link title")'), [ '(user@example.com "link title")', 'user@example.com', 'link title' ]);
-
- this.assertEqual(MDUtils.tokenizeEmail('(https://example.com) foo'), null);
- this.assertEqual(MDUtils.tokenizeEmail('(https://example.com "link title") foo'), null);
- this.assertEqual(MDUtils.tokenizeEmail('(user@example.com "link title) foo'), null);
- this.assertEqual(MDUtils.tokenizeEmail('(user@example.com foo'), null);
- this.assertEqual(MDUtils.tokenizeEmail('user@example.com) foo'), null);
- }
- }
-
- class InlineTests extends BaseTest {
- /** @type {Markdown} */
- parser;
- md(markdown) {
- return normalizeWhitespace(this.parser.toHTML(markdown));
- }
-
- setUp() {
- this.parser = Markdown.completeParser;
- }
-
- test_simpleText() {
- let markdown = 'Lorem ipsum';
- let expected = 'Lorem ipsum';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_strong() {
- let markdown = 'Lorem **ipsum** dolor **sit**';
- let expected = 'Lorem <strong>ipsum</strong> dolor <strong>sit</strong>';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_emphasis() {
- let markdown = 'Lorem _ipsum_ dolor _sit_';
- let expected = 'Lorem <em>ipsum</em> dolor <em>sit</em>';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_strongEmphasis_cleanNesting1() {
- let markdown = 'Lorem **ipsum *dolor* sit** amet';
- let expected = 'Lorem <strong>ipsum <em>dolor</em> sit</strong> amet';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_strongEmphasis_cleanNesting2() {
- let markdown = 'Lorem *ipsum **dolor** sit* amet';
- let expected = 'Lorem <em>ipsum <strong>dolor</strong> sit</em> amet';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_strongEmphasis_tightNesting() {
- let markdown = 'Lorem ***ipsum*** dolor';
- let expected1 = 'Lorem <strong><em>ipsum</em></strong> dolor';
- let expected2 = 'Lorem <em><strong>ipsum</strong></em> dolor';
- let actual = this.md(markdown);
- this.assertTrue(actual == expected1 || actual == expected2);
- }
-
- test_strongEmphasis_lopsidedNesting1() {
- let markdown = 'Lorem ***ipsum* dolor** sit';
- let expected = 'Lorem <strong><em>ipsum</em> dolor</strong> sit';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_strongEmphasis_lopsidedNesting2() {
- let markdown = 'Lorem ***ipsum** dolor* sit';
- let expected = 'Lorem <em><strong>ipsum</strong> dolor</em> sit';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_strongEmphasis_lopsidedNesting3() {
- let markdown = 'Lorem **ipsum *dolor*** sit';
- let expected = 'Lorem <strong>ipsum <em>dolor</em></strong> sit';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_strongEmphasis_lopsidedNesting4() {
- let markdown = 'Lorem *ipsum **dolor*** sit';
- let expected = 'Lorem <em>ipsum <strong>dolor</strong></em> sit';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_inlineCode() {
- let markdown = 'Lorem `ipsum` dolor';
- let expected = 'Lorem <code>ipsum</code> dolor';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_inlineCode_withInnerBacktick() {
- let markdown = 'Lorem ``ip`su`m`` dolor';
- let expected = 'Lorem <code>ip`su`m</code> dolor';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_strikethrough_single() {
- let markdown = 'Lorem ~ipsum~ dolor';
- let expected = 'Lorem <strike>ipsum</strike> dolor';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_strikethrough_double() {
- let markdown = 'Lorem ~~ipsum~~ dolor';
- let expected = 'Lorem <strike>ipsum</strike> dolor';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_link_fullyQualified() {
- let markdown = 'Lorem [ipsum](https://example.com/path/page.html) dolor';
- let expected = 'Lorem <a href="https://example.com/path/page.html">ipsum</a> dolor';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_link_relative() {
- let markdown = 'Lorem [ipsum](page.html) dolor';
- let expected = 'Lorem <a href="page.html">ipsum</a> dolor';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_link_title() {
- let markdown = 'Lorem [ipsum](page.html "link title") dolor';
- let expected = 'Lorem <a href="page.html" title="link title">ipsum</a> dolor';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_link_literal() {
- let markdown = 'Lorem <https://example.com> dolor';
- let expected = 'Lorem <a href="https://example.com">https://example.com</a> dolor';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_link_ref() {
- let markdown = "Lorem [ipsum][ref] dolor\n\n[ref]: https://example.com";
- let expected = '<p>Lorem <a href="https://example.com">ipsum</a> dolor</p>';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_link_email() {
- let markdown = 'Lorem [ipsum](user@example.com) dolor';
- let expected = 'Lorem <a href="mailto:user@example.com">ipsum</a> dolor';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_link_email_withTitle() {
- let markdown = 'Lorem [ipsum](user@example.com "title") dolor';
- let expected = 'Lorem <a href="mailto:user@example.com" title="title">ipsum</a> dolor';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_link_literalEmail() {
- let markdown = 'Lorem <user@example.com> dolor';
- let expected = 'Lorem <a href="mailto:user@example.com">user@example.com</a> dolor';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_link_image() {
- let markdown = 'Lorem [](page.html) ipsum';
- let expected = 'Lorem <a href="page.html"><img src="image.jpg" alt="alt"></a> ipsum';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_link_image_complex() {
- let markdown = 'Lorem [![alt] (image.jpg "image title")] (page.html "link title") ipsum';
- let expected = 'Lorem <a href="page.html" title="link title"><img src="image.jpg" alt="alt" title="image title"></a> ipsum';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_image() {
- let markdown = 'Lorem  dolor';
- let expected = 'Lorem <img src="image.jpg" alt="alt text"> dolor';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_image_noAlt() {
- let markdown = 'Lorem  dolor';
- let expected = 'Lorem <img src="image.jpg"> dolor';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_image_withTitle() {
- let markdown = 'Lorem  dolor';
- let expected = 'Lorem <img src="image.jpg" alt="alt text" title="image title"> dolor';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_image_ref() {
- let markdown = 'Lorem ![alt text][ref] dolor\n\n' +
- '[ref]: image.jpg "image title"';
- let expected = '<p>Lorem <img src="image.jpg" alt="alt text" title="image title"> dolor</p>';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_htmlTags() {
- let markdown = 'Lorem <strong title="value" foo=\'with " quote\' bar="with \' apostrophe" attr=unquoted checked>ipsum</strong> dolor';
- let expected = markdown;
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
- }
-
- class BlockTests extends BaseTest {
- /** @type {Markdown} */
- parser;
- md(markdown) {
- return normalizeWhitespace(this.parser.toHTML(markdown));
- }
-
- setUp() {
- this.parser = Markdown.completeParser;
- }
-
- test_paragraphs() {
- let markdown = "Lorem ipsum\n\nDolor sit amet";
- let expected = "<p>Lorem ipsum</p> <p>Dolor sit amet</p>";
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_paragraph_lineGrouping() {
- let markdown = "Lorem ipsum\ndolor sit amet";
- let expected = "Lorem ipsum dolor sit amet";
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_header_underlineH1() {
- let markdown = "Header 1\n===\n\nLorem ipsum";
- let expected = "<h1>Header 1</h1> <p>Lorem ipsum</p>";
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_header_underlineH2() {
- let markdown = "Header 2\n---\n\nLorem ipsum";
- let expected = "<h2>Header 2</h2> <p>Lorem ipsum</p>";
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_header_hash() {
- let markdown = "# Header 1\n## Header 2\n### Header 3\n#### Header 4\n##### Header 5\n###### Header 6\n";
- let expected = '<h1>Header 1</h1> <h2>Header 2</h2> <h3>Header 3</h3> <h4>Header 4</h4> <h5>Header 5</h5> <h6>Header 6</h6>';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_header_hash_trailing() {
- let markdown = "# Header 1 #\n## Header 2 ##\n### Header 3 ######";
- let expected = '<h1>Header 1</h1> <h2>Header 2</h2> <h3>Header 3</h3>';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_unorderedList() {
- let markdown = "* Lorem\n* Ipsum\n* Dolor";
- let expected = '<ul> <li>Lorem</li> <li>Ipsum</li> <li>Dolor</li> </ul>';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_unorderedList_nested() {
- let markdown = "* Lorem\n + Ipsum\n* Dolor";
- let expected = '<ul> <li>Lorem <ul> <li>Ipsum</li> </ul></li> <li>Dolor</li> </ul>';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_unorderedList_hitch() {
- // This incomplete bulleted list locked up the browser at one
- // point, not forever but a REALLY long time
- this.profile(1.0, () => {
- let markdown = "Testing\n\n* ";
- let expected = '<p>Testing</p> <ul> <li></li> </ul>';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- });
- }
-
- test_orderedList() {
- let markdown = "1. Lorem\n1. Ipsum\n5. Dolor";
- let expected = '<ol> <li>Lorem</li> <li>Ipsum</li> <li>Dolor</li> </ol>';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_orderedList_numbering() {
- let markdown = "4. Lorem\n1. Ipsum\n9. Dolor";
- let expected = '<ol start="4"> <li>Lorem</li> <li>Ipsum</li> <li>Dolor</li> </ol>';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_orderedList_nested1() {
- let markdown = "1. Lorem\n 1. Ipsum\n1. Dolor";
- let expected = '<ol> <li>Lorem <ol> <li>Ipsum</li> </ol></li> <li>Dolor</li> </ol>';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_orderedList_nested2() {
- let markdown = "1. Lorem\n 1. Ipsum\n 1. Dolor\n 1. Sit\n1. Amet";
- let expected = '<ol> <li>Lorem <ol> <li>Ipsum <ol> <li>Dolor</li> </ol></li> <li>Sit</li> </ol></li> <li>Amet</li> </ol>';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_blockquote() {
- let markdown = '> Lorem ipsum dolor';
- let expected = '<blockquote> Lorem ipsum dolor </blockquote>';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_blockquote_paragraphs() {
- let markdown = '> Lorem ipsum dolor\n>\n>Sit amet';
- let expected = '<blockquote> <p>Lorem ipsum dolor</p> <p>Sit amet</p> </blockquote>';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_blockquote_list() {
- let markdown = '> 1. Lorem\n> 2. Ipsum';
- let expected = '<blockquote> <ol> <li>Lorem</li> <li>Ipsum</li> </ol> </blockquote>';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_codeBlock_indented() {
- let markdown = "Code\n\n function foo() {\n return 'bar';\n }\n\nend";
- let expected = "<p>Code</p>\n\n<pre><code>function foo() {\n return 'bar';\n}</code></pre>\n<p>end</p>\n";
- let actual = this.parser.toHTML(markdown); // don't normalize whitespace
- this.assertEqual(actual.replace(/ /g, '⎵'), expected.replace(/ /g, '⎵'));
- }
-
- test_codeBlock_fenced() {
- let markdown = "Code\n\n```\nfunction foo() {\n return 'bar';\n}\n```\n\nend";
- let expected = "<p>Code</p>\n\n<pre><code>function foo() {\n return 'bar';\n}</code></pre>\n<p>end</p>\n";
- let actual = this.parser.toHTML(markdown); // don't normalize whitespace
- this.assertEqual(actual.replace(/ /g, '⎵'), expected.replace(/ /g, '⎵'));
- }
-
- test_horizontalRule() {
- let markdown = "Before\n\n---\n\n- - -\n\n***\n\n* * * * * * *\n\nafter";
- let expected = "<p>Before</p> <hr> <hr> <hr> <hr> <p>after</p>";
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_table_unfenced() {
- let markdown = "Column A | Column B | Column C\n--- | --- | ---\n1 | 2 | 3\n4 | 5 | 6";
- let expected = "<table> <thead> <tr> <th>Column A</th> <th>Column B</th> <th>Column C</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>2</td> <td>3</td> </tr> <tr> <td>4</td> <td>5</td> <td>6</td> </tr> </tbody> </table>";
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_table_fenced() {
- let markdown = "| Column A | Column B | Column C |\n| --- | --- | --- |\n| 1 | 2 | 3\n4 | 5 | 6 |";
- let expected = "<table> <thead> <tr> <th>Column A</th> <th>Column B</th> <th>Column C</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>2</td> <td>3</td> </tr> <tr> <td>4</td> <td>5</td> <td>6</td> </tr> </tbody> </table>";
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_table_alignment() {
- let markdown = 'Column A | Column B | Column C\n' +
- ':--- | :---: | ---:\n' +
- '1 | 2 | 3\n' +
- '4 | 5 | 6';
- let expected = '<table> ' +
- '<thead> ' +
- '<tr> ' +
- '<th align="left">Column A</th> ' +
- '<th align="center">Column B</th> ' +
- '<th align="right">Column C</th> ' +
- '</tr> ' +
- '</thead> ' +
- '<tbody> ' +
- '<tr> ' +
- '<td align="left">1</td> ' +
- '<td align="center">2</td> ' +
- '<td align="right">3</td> ' +
- '</tr> ' +
- '<tr> ' +
- '<td align="left">4</td> ' +
- '<td align="center">5</td> ' +
- '<td align="right">6</td> ' +
- '</tr> ' +
- '</tbody> ' +
- '</table>';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_table_holes() {
- let markdown = 'Column A||Column C\n' +
- '---|---|---\n' +
- '|1|2||\n' +
- '|4||6|\n' +
- '||8|9|';
- let expected = '<table> ' +
- '<thead> ' +
- '<tr> ' +
- '<th>Column A</th> ' +
- '<th></th> ' +
- '<th>Column C</th> ' +
- '</tr> ' +
- '</thead> ' +
- '<tbody> ' +
- '<tr> ' +
- '<td>1</td> ' +
- '<td>2</td> ' +
- '<td></td> ' +
- '</tr> ' +
- '<tr> ' +
- '<td>4</td> ' +
- '<td></td> ' +
- '<td>6</td> ' +
- '</tr> ' +
- '<tr> ' +
- '<td></td> ' +
- '<td>8</td> ' +
- '<td>9</td> ' +
- '</tr> ' +
- '</tbody> ' +
- '</table>';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_definitionList() {
- let markdown = 'term\n' +
- ': definition\n' +
- 'another' +
- ' term\n' +
- ': def 1\n' +
- ' broken on next line\n' +
- ': def 2';
- let expected = '<dl> ' +
- '<dt>term</dt> ' +
- '<dd>definition</dd> ' +
- '<dt>another term</dt> ' +
- '<dd>def 1 broken on next line</dd> ' +
- '<dd>def 2</dd> ' +
- '</dl>';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_footnotes() {
- let markdown = 'Lorem ipsum[^1] dolor[^2] sit[^1] amet\n\n[^1]: A footnote\n[^2]: Another footnote';
- let expected = '<p>Lorem ipsum<sup id="footnoteref_1"><a href="#footnote_1">1</a></sup> ' +
- 'dolor<sup id="footnoteref_2"><a href="#footnote_2">2</a></sup> ' +
- 'sit<sup id="footnoteref_3"><a href="#footnote_1">1</a></sup> amet</p> ' +
- '<div class="footnotes">' +
- '<hr/>' +
- '<ol>' +
- '<li value="1" id="footnote_1">A footnote <a href="#footnoteref_1" class="footnote-backref">↩︎</a> <a href="#footnoteref_3" class="footnote-backref">↩︎</a></li> ' +
- '<li value="2" id="footnote_2">Another footnote <a href="#footnoteref_2" class="footnote-backref">↩︎</a></li> ' +
- '</ol>' +
- '</div>';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
-
- test_abbreviations() {
- let markdown = 'Lorem ipsum HTML dolor HTML sit\n' +
- '\n' +
- '*[HTML]: Hypertext Markup Language';
- let expected = '<p>Lorem ipsum <abbr title="Hypertext Markup Language">HTML</abbr> dolor <abbr title="Hypertext Markup Language">HTML</abbr> sit</p>';
- let actual = this.md(markdown);
- this.assertEqual(actual, expected);
- }
- }
-
- class CellValueTests extends BaseTest {
- test_fromCellString_blank() {
- var value;
- value = CellValue.fromCellString('');
- this.assertEqual(value.type, CellValue.TYPE_BLANK);
- this.assertEqual(value.formattedValue, '');
- this.assertEqual(value.value, null);
- value = CellValue.fromCellString(' ');
- this.assertEqual(value.type, CellValue.TYPE_BLANK);
- this.assertEqual(value.formattedValue, '');
- this.assertEqual(value.value, null);
- }
-
- test_fromCellString_number() {
- var value;
- value = CellValue.fromCellString('123');
- this.assertEqual(value.type, CellValue.TYPE_NUMBER);
- this.assertEqual(value.formattedValue, '123');
- this.assertEqual(value.value, 123);
- this.assertEqual(value.decimals, 0);
- value = CellValue.fromCellString('-0');
- this.assertEqual(value.type, CellValue.TYPE_NUMBER);
- this.assertEqual(value.formattedValue, '-0');
- this.assertEqual(value.value, 0);
- this.assertEqual(value.decimals, 0);
- value = CellValue.fromCellString('1,234');
- this.assertEqual(value.type, CellValue.TYPE_NUMBER);
- this.assertEqual(value.formattedValue, '1,234');
- this.assertEqual(value.value, 1234);
- this.assertEqual(value.decimals, 0);
- value = CellValue.fromCellString('-1,234,567.89');
- this.assertEqual(value.type, CellValue.TYPE_NUMBER);
- this.assertEqual(value.formattedValue, '-1,234,567.89');
- this.assertEqual(value.value, -1234567.89);
- this.assertEqual(value.decimals, 2);
- }
-
- test_fromCellString_percent() {
- var value;
- value = CellValue.fromCellString('123%');
- this.assertEqual(value.type, CellValue.TYPE_PERCENT);
- this.assertEqual(value.formattedValue, '123%');
- this.assertEqual(value.value, 1.23, 0.0001);
- this.assertEqual(value.decimals, 0);
- value = CellValue.fromCellString('-12.3%');
- this.assertEqual(value.type, CellValue.TYPE_PERCENT);
- this.assertEqual(value.formattedValue, '-12.3%');
- this.assertEqual(value.value, -0.123, 0.0001);
- this.assertEqual(value.decimals, 1);
- }
-
- test_fromCellString_currency() {
- var value;
- value = CellValue.fromCellString('$123');
- this.assertEqual(value.type, CellValue.TYPE_CURRENCY);
- this.assertEqual(value.formattedValue, '$123');
- this.assertEqual(value.value, 123);
- this.assertEqual(value.decimals, 0);
- value = CellValue.fromCellString('-$12.34');
- this.assertEqual(value.type, CellValue.TYPE_CURRENCY);
- this.assertEqual(value.formattedValue, '-$12.34');
- this.assertEqual(value.value, -12.34, 0.0001);
- this.assertEqual(value.decimals, 2);
- }
-
- test_fromCellString_boolean() {
- var value;
- value = CellValue.fromCellString('true');
- this.assertEqual(value.type, CellValue.TYPE_BOOLEAN);
- this.assertEqual(value.formattedValue, 'TRUE');
- this.assertEqual(value.value, true);
- value = CellValue.fromCellString('false');
- this.assertEqual(value.type, CellValue.TYPE_BOOLEAN);
- this.assertEqual(value.formattedValue, 'FALSE');
- this.assertEqual(value.value, false);
- }
-
- test_fromCellString_string() {
- var value;
- value = CellValue.fromCellString('some text');
- this.assertEqual(value.type, CellValue.TYPE_STRING);
- this.assertEqual(value.formattedValue, 'some text');
- this.assertEqual(value.value, 'some text');
- value = CellValue.fromCellString("'123");
- this.assertEqual(value.type, CellValue.TYPE_STRING);
- this.assertEqual(value.formattedValue, '123');
- this.assertEqual(value.value, '123');
- }
-
- test_fromCellString_formula() {
- var value;
- value = CellValue.fromCellString('=A*B');
- this.assertEqual(value.type, CellValue.TYPE_FORMULA);
- this.assertEqual(value.formattedValue, '=A*B');
- this.assertEqual(value.value, '=A*B');
- value = CellValue.fromCellString('=MAX(A, 3)');
- this.assertEqual(value.type, CellValue.TYPE_FORMULA);
- this.assertEqual(value.formattedValue, '=MAX(A, 3)');
- this.assertEqual(value.value, '=MAX(A, 3)');
- }
-
- test_fromValue_null() {
- var value;
- value = CellValue.fromValue(null);
- this.assertEqual(value.type, CellValue.TYPE_BLANK);
- }
-
- test_fromValue_number() {
- var value;
- value = CellValue.fromValue(123);
- this.assertEqual(value.type, CellValue.TYPE_NUMBER);
- this.assertEqual(value.formattedValue, '123');
- value = CellValue.fromValue(3.141592);
- this.assertEqual(value.type, CellValue.TYPE_NUMBER);
- this.assertEqual(value.formattedValue, '3.141592');
- value = CellValue.fromValue(123456789);
- this.assertEqual(value.type, CellValue.TYPE_NUMBER);
- this.assertEqual(value.formattedValue, '123,456,789');
- }
-
- test_fromValue_boolean() {
- var value;
- value = CellValue.fromValue(true);
- this.assertEqual(value.type, CellValue.TYPE_BOOLEAN);
- this.assertEqual(value.formattedValue, 'TRUE');
- value = CellValue.fromValue(false);
- this.assertEqual(value.type, CellValue.TYPE_BOOLEAN);
- this.assertEqual(value.formattedValue, 'FALSE');
- }
-
- test_fromValue_string() {
- var value;
- value = CellValue.fromValue('foo');
- this.assertEqual(value.type, CellValue.TYPE_STRING);
- this.assertEqual(value.formattedValue, 'foo');
- value = CellValue.fromValue('123');
- this.assertEqual(value.type, CellValue.TYPE_STRING);
- this.assertEqual(value.formattedValue, '123');
- }
-
- test_fromValue_formula() {
- var value;
- value = CellValue.fromValue('=A*B');
- this.assertEqual(value.type, CellValue.TYPE_FORMULA);
- this.assertEqual(value.formattedValue, '=A*B');
- }
-
- test_operation_add() {
- var a, b, result, expected;
- a = CellValue.fromValue(3);
- b = CellValue.fromValue(4);
- result = a.add(b)
- expected = new CellValue('7', 7, CellValue.TYPE_NUMBER, 0);
- this.assertEqual(result, expected);
-
- a = CellValue.fromCellString('100%');
- b = CellValue.fromCellString('50%');
- result = a.add(b);
- expected = new CellValue('150%', 1.5, CellValue.TYPE_PERCENT, 0);
- this.assertEqual(result, expected);
-
- a = CellValue.fromCellString('$123');
- b = CellValue.fromCellString('$321');
- result = a.add(b);
- expected = new CellValue('$444.00', 444, CellValue.TYPE_CURRENCY, 2);
- this.assertEqual(result, expected);
- }
-
- test_operation_subtract() {
- var a, b, result, expected;
- a = CellValue.fromValue(9);
- b = CellValue.fromValue(4);
- result = a.subtract(b)
- expected = new CellValue('5', 5, CellValue.TYPE_NUMBER, 0);
- this.assertEqual(result, expected);
-
- a = CellValue.fromCellString('100%');
- b = CellValue.fromCellString('50%');
- result = a.subtract(b);
- expected = new CellValue('50%', 0.5, CellValue.TYPE_PERCENT, 0);
- this.assertEqual(result, expected);
-
- a = CellValue.fromCellString('$321');
- b = CellValue.fromCellString('$123');
- result = a.subtract(b);
- expected = new CellValue('$198.00', 198, CellValue.TYPE_CURRENCY, 2);
- this.assertEqual(result, expected);
- }
-
- test_operation_multiply() {
- var a, b, result, expected;
- a = CellValue.fromValue(3);
- b = CellValue.fromValue(4);
- result = a.multiply(b)
- expected = new CellValue('12', 12, CellValue.TYPE_NUMBER, 0);
- this.assertEqual(result, expected);
-
- a = CellValue.fromCellString('150%');
- b = CellValue.fromCellString('50%');
- result = a.multiply(b);
- expected = new CellValue('75%', 0.75, CellValue.TYPE_PERCENT, 0);
- this.assertEqual(result, expected);
-
- a = CellValue.fromCellString('$321');
- b = CellValue.fromCellString('50%');
- result = a.multiply(b);
- expected = new CellValue('$160.50', 160.50, CellValue.TYPE_CURRENCY, 2);
- this.assertEqual(result, expected);
- }
-
- test_operation_divide() {
- var a, b, result, expected;
- a = CellValue.fromValue(12);
- b = CellValue.fromValue(4);
- result = a.divide(b)
- expected = new CellValue('3', 3, CellValue.TYPE_NUMBER, 0);
- this.assertEqual(result, expected);
-
- a = CellValue.fromCellString('150%');
- b = CellValue.fromCellString('50%');
- result = a.divide(b);
- expected = new CellValue('300%', 3.0, CellValue.TYPE_PERCENT, 0);
- this.assertEqual(result, expected);
-
- a = CellValue.fromCellString('$321');
- b = CellValue.fromCellString('200%');
- result = a.divide(b);
- expected = new CellValue('$160.50', 160.50, CellValue.TYPE_CURRENCY, 2);
- this.assertEqual(result, expected);
- }
-
- test_operation_modulo() {
- var a, b, result, expected;
- a = CellValue.fromValue(7);
- b = CellValue.fromValue(4);
- result = a.modulo(b)
- expected = new CellValue('3', 3, CellValue.TYPE_NUMBER, 0);
- this.assertEqual(result, expected);
-
- a = CellValue.fromCellString('175%');
- b = CellValue.fromCellString('50%');
- result = a.modulo(b);
- expected = new CellValue('25%', 0.25, CellValue.TYPE_PERCENT, 0);
- this.assertEqual(result, expected);
-
- a = CellValue.fromCellString('$327');
- b = CellValue.fromCellString('$20');
- result = a.modulo(b);
- expected = new CellValue('$7.00', 7.00, CellValue.TYPE_CURRENCY, 2);
- this.assertEqual(result, expected);
- }
-
- test_operation_unaryNot() {
- this.assertEqual(CellValue.fromValue(true).not(), CellValue.fromValue(false));
- this.assertEqual(CellValue.fromValue(false).not(), CellValue.fromValue(true));
- }
-
- test_operation_comparators() {
- const a = CellValue.fromValue(3);
- const b = CellValue.fromValue(4);
- const t = CellValue.fromValue(true);
- const f = CellValue.fromValue(false);
-
- this.assertEqual(a.lt(b), t);
- this.assertEqual(a.lte(b), t);
- this.assertEqual(a.gt(b), f);
- this.assertEqual(a.gte(b), f);
- this.assertEqual(a.eq(b), f);
- this.assertEqual(a.neq(b), t);
-
- this.assertEqual(b.lt(a), f);
- this.assertEqual(b.lte(a), f);
- this.assertEqual(b.gt(a), t);
- this.assertEqual(b.gte(a), t);
- this.assertEqual(b.eq(a), f);
- this.assertEqual(b.neq(a), t);
-
- this.assertEqual(a.lt(a), f);
- this.assertEqual(a.lte(a), t);
- this.assertEqual(a.gt(a), f);
- this.assertEqual(a.gte(a), t);
- this.assertEqual(a.eq(a), t);
- this.assertEqual(a.neq(a), f);
- }
-
- test_operation_concatenate() {
- const a = CellValue.fromValue('abc');
- const b = CellValue.fromValue('xyz');
- const result = a.concatenate(b);
- const expected = CellValue.fromValue('abcxyz');
- this.assertEqual(result, expected);
- }
- }
-
- class CellAddressRangeTests extends BaseTest {
- test_iterator() {
- const grid = new SpreadsheetGrid(3, 4);
- let range = new CellAddressRange(new CellAddress(0, 0), new CellAddress(2, 3));
- var visited = [];
- var sanity = 100;
- for (const address of range.cellsIn(grid)) {
- visited.push(address.name);
- if (sanity-- < 0) break;
- }
- const result = visited.join(',');
- const expected = 'A1,A2,A3,A4,B1,B2,B3,B4,C1,C2,C3,C4';
- this.assertEqual(result, expected);
- }
-
- test_iterator_column() {
- const grid = new SpreadsheetGrid(3, 4);
- let range = new CellAddressRange(new CellAddress(1, -1), new CellAddress(2, -1));
- var visited = [];
- var sanity = 100;
- for (const address of range.cellsIn(grid)) {
- visited.push(address.name);
- if (sanity-- < 0) break;
- }
- const result = visited.join(',');
- const expected = 'B1,B2,B3,B4,C1,C2,C3,C4';
- this.assertEqual(result, expected);
- }
-
- test_iterator_beyondBounds() {
- const grid = new SpreadsheetGrid(3, 4);
- let range = new CellAddressRange(new CellAddress(0, 1), new CellAddress(9, 9));
- var visited = [];
- var sanity = 100;
- for (const address of range.cellsIn(grid)) {
- visited.push(address.name);
- if (sanity-- < 0) break;
- }
- const result = visited.join(',');
- const expected = 'A2,A3,A4,B2,B3,B4,C2,C3,C4';
- this.assertEqual(result, expected);
- }
-
- test_iterator_outOfBounds() {
- const grid = new SpreadsheetGrid(3, 4);
- let range = new CellAddressRange(new CellAddress(5, 0), new CellAddress(5, 9));
- var visited = [];
- var sanity = 100;
- for (const address of range.cellsIn(grid)) {
- visited.push(address.name);
- if (sanity-- < 0) break;
- }
- const result = visited.join(',');
- const expected = '';
- this.assertEqual(result, expected);
- }
- }
-
- class ExpressionSetTests extends BaseTest {
- test_simpleMath() {
- const grid = new SpreadsheetGrid(1, 1);
- grid.cells[0][0].originalValue = CellValue.fromCellString('=7*3');
- const expressionSet = new CellExpressionSet(grid);
- expressionSet.calculateCells();
- const expected = CellValue.fromValue(21);
- this.assertEqual(grid.cells[0][0].outputValue, expected);
- }
-
- test_reference() {
- const grid = new SpreadsheetGrid(3, 1);
- grid.cells[0][0].originalValue = CellValue.fromValue(123);
- grid.cells[1][0].originalValue = CellValue.fromValue(3);
- grid.cells[2][0].originalValue = CellValue.fromCellString('=A1*B1');
- const expressionSet = new CellExpressionSet(grid);
- expressionSet.calculateCells();
- const expected = CellValue.fromValue(369);
- this.assertEqual(grid.cells[2][0].outputValue, expected);
- }
-
- test_infixPriority() {
- const grid = new SpreadsheetGrid(1, 1);
- grid.cells[0][0].originalValue = CellValue.fromCellString('=4*9/3+7-4');
- const expressionSet = new CellExpressionSet(grid);
- expressionSet.calculateCells();
- const expected = CellValue.fromValue(15);
- this.assertEqual(grid.cells[0][0].outputValue, expected);
- }
-
- test_filledFormula() {
- const grid = new SpreadsheetGrid(3, 3);
- grid.cells[0][0].originalValue = CellValue.fromValue(4);
- grid.cells[1][0].originalValue = CellValue.fromValue(5);
- grid.cells[2][0].originalValue = CellValue.fromCellString('=A1*B1 FILL');
- grid.cells[0][1].originalValue = CellValue.fromValue(6);
- grid.cells[1][1].originalValue = CellValue.fromValue(7);
- grid.cells[0][2].originalValue = CellValue.fromValue(8);
- grid.cells[1][2].originalValue = CellValue.fromValue(9);
- const expressionSet = new CellExpressionSet(grid);
- expressionSet.calculateCells();
- this.assertEqual(grid.cells[2][0].outputValue, CellValue.fromValue(20));
- this.assertEqual(grid.cells[2][1].outputValue, CellValue.fromValue(42));
- this.assertEqual(grid.cells[2][2].outputValue, CellValue.fromValue(72));
- }
-
- test_dependencies() {
- const grid = new SpreadsheetGrid(2, 4);
- grid.cells[0][0].originalValue = CellValue.fromValue(1);
- grid.cells[1][0].originalValue = CellValue.fromCellString('=A1+B2');
- grid.cells[0][1].originalValue = CellValue.fromValue(2);
- grid.cells[1][1].originalValue = CellValue.fromCellString('=A2+B3');
- grid.cells[0][2].originalValue = CellValue.fromValue(3);
- grid.cells[1][2].originalValue = CellValue.fromCellString('=A3+B4');
- grid.cells[0][3].originalValue = CellValue.fromValue(4);
- grid.cells[1][3].originalValue = CellValue.fromCellString('=A4');
- const expressionSet = new CellExpressionSet(grid);
- expressionSet.calculateCells();
- this.assertEqual(grid.cells[1][0].outputValue, CellValue.fromValue(10));
- this.assertEqual(grid.cells[1][1].outputValue, CellValue.fromValue(9));
- this.assertEqual(grid.cells[1][2].outputValue, CellValue.fromValue(7));
- this.assertEqual(grid.cells[1][3].outputValue, CellValue.fromValue(4));
- }
-
- _test_simple_formula(formula, expected) {
- const grid = new SpreadsheetGrid(1, 1);
- grid.cells[0][0].originalValue = CellValue.fromCellString(formula);
- const expressionSet = new CellExpressionSet(grid);
- expressionSet.calculateCells();
- const result = grid.cells[0][0].outputValue;
- const exp = (expected instanceof CellValue) ? expected : CellValue.fromValue(expected);
- this.assertEqual(result.type, exp.type);
- this.assertEqual(result.decimals, exp.decimals);
- this.assertEqual(result.formattedValue, exp.formattedValue);
- this.assertEqual(result.value, exp.value);
- }
-
- test_func_abs() {
- this._test_simple_formula('=ABS(-3)', 3);
- this._test_simple_formula('=ABS(4)', 4);
- }
-
- test_func_and() {
- this._test_simple_formula('=AND(FALSE, FALSE)', false);
- this._test_simple_formula('=AND(FALSE, TRUE)', false);
- this._test_simple_formula('=AND(TRUE, FALSE)', false);
- this._test_simple_formula('=AND(TRUE, TRUE)', true);
- }
-
- test_func_average() {
- this._test_simple_formula('=AVERAGE(4, 6, 2, 4)', 4);
- }
-
- test_func_ceiling() {
- this._test_simple_formula('=CEILING(3.1)', 4);
- this._test_simple_formula('=CEILING(3)', 3);
- this._test_simple_formula('=CEILING(-3.1)', -3);
- }
-
- test_func_exp() {
- this._test_simple_formula('=EXP(1)', 2.718281828459045);
- }
-
- test_func_floor() {
- this._test_simple_formula('=FLOOR(3.1)', 3);
- this._test_simple_formula('=FLOOR(3)', 3);
- this._test_simple_formula('=FLOOR(-3.1)', -4);
- }
-
- test_func_if() {
- this._test_simple_formula('=IF(FALSE, 4, 6)', 6);
- this._test_simple_formula('=IF(TRUE, 4, 6)', 4);
- }
-
- test_func_ifs() {
- this._test_simple_formula('=IFS(TRUE, 1, FALSE, 2, FALSE, 3, FALSE, 4, 5)', 1);
- this._test_simple_formula('=IFS(FALSE, 1, TRUE, 2, FALSE, 3, FALSE, 4, 5)', 2);
- this._test_simple_formula('=IFS(FALSE, 1, FALSE, 2, TRUE, 3, FALSE, 4, 5)', 3);
- this._test_simple_formula('=IFS(FALSE, 1, FALSE, 2, FALSE, 3, TRUE, 4, 5)', 4);
- this._test_simple_formula('=IFS(FALSE, 1, FALSE, 2, FALSE, 3, FALSE, 4, 5)', 5);
- }
-
- test_func_ln() {
- this._test_simple_formula('=LN(2.718281828459045)', 1);
- }
-
- test_func_log() {
- this._test_simple_formula('=LOG(1000, 10)', 3);
- }
-
- test_func_lower() {
- this._test_simple_formula('=LOWER("MiXeD")', 'mixed');
- }
-
- test_func_max() {
- this._test_simple_formula('=MAX(4, 8, 5, 2)', 8);
- }
-
- test_func_min() {
- this._test_simple_formula('=MIN(4, 8, 5, 2)', 2);
- }
-
- test_func_mod() {
- this._test_simple_formula('=MOD(37, 4)', 1);
- }
-
- test_func_not() {
- this._test_simple_formula('=NOT(TRUE)', false);
- this._test_simple_formula('=NOT(FALSE)', true);
- }
-
- test_func_or() {
- this._test_simple_formula('=OR(FALSE, FALSE)', false);
- this._test_simple_formula('=OR(FALSE, TRUE)', true);
- this._test_simple_formula('=OR(TRUE, FALSE)', true);
- this._test_simple_formula('=OR(TRUE, TRUE)', true);
- this._test_simple_formula('=OR(FALSE, FALSE, FALSE, TRUE)', true);
- }
-
- test_func_power() {
- this._test_simple_formula('=POWER(2, 3)', 8);
- }
-
- test_func_round() {
- this._test_simple_formula('=ROUND(3.1)', 3);
- this._test_simple_formula('=ROUND(3.5)', 4);
- this._test_simple_formula('=ROUND(4)', 4);
- this._test_simple_formula('=ROUND(-3.1)', -3);
- this._test_simple_formula('=ROUND(-3.5)', -3);
- this._test_simple_formula('=ROUND(-3.9)', -4);
- }
-
- test_func_sqrt() {
- this._test_simple_formula('=SQRT(16)', 4);
- }
-
- test_func_substitute() {
- this._test_simple_formula('=SUBSTITUTE("cat sat on the mat", "at", "ot")', 'cot sot on the mot');
- }
-
- test_func_sum() {
- this._test_simple_formula('=SUM(1, 2, 3, 4, 5)', 15);
- }
-
- test_func_upper() {
- this._test_simple_formula('=UPPER("mIxEd")', 'MIXED');
- }
-
- test_func_xor() {
- this._test_simple_formula('=XOR(FALSE, FALSE)', false);
- this._test_simple_formula('=XOR(FALSE, TRUE)', true);
- this._test_simple_formula('=XOR(TRUE, FALSE)', true);
- this._test_simple_formula('=XOR(TRUE, TRUE)', false);
- this._test_simple_formula('=XOR(FALSE, FALSE, TRUE)', true);
- this._test_simple_formula('=XOR(TRUE, FALSE, TRUE)', false);
- }
-
- test_format() {
- this._test_simple_formula('=2.718281828459045 ; number 3', new CellValue('2.718', 2.718281828459045, 'number', 3));
- this._test_simple_formula('=2.718281828459045 ; percent 2', new CellValue('271.83%', 2.718281828459045, 'percent', 2));
- this._test_simple_formula('=2.718281828459045 ; currency 2', new CellValue('$2.72', 2.718281828459045, 'currency', 2));
- }
- }
- </script>
- </head>
- <body>
- <div id="results"></div>
- </body>
- </html>
|