'use strict'; describe('importexport.js', function () { const testCases = [ { name: 'text with newlines', inputText: [ 'imported text\n', 'newline', ].join(''), wantPadLines: [ 'imported text', 'newline', ], wantExportHtmlBody: [ 'imported text
', 'newline
', ].join(''), wantExportText: [ 'imported text\n', 'newline\n', ].join(''), }, { name: 'HTML with newlines', inputHtmlBody: [ 'htmltext
', 'newline', ].join(''), wantPadLines: [ 'htmltext', 'newline', '
', ], wantExportHtmlBody: [ 'htmltext
', 'newline
', '
', ].join(''), wantExportText: [ 'htmltext\n', 'newline\n', '\n', ].join(''), }, { name: 'HTML with attributes', inputHtmlBody: [ 'htmltext
', 'newline', ].join(''), wantPadLines: [ 'htmltext', 'newline', '
', ], wantExportHtmlBody: [ 'htmltext
', 'newline
', '
', ].join(''), wantExportText: [ 'htmltext\n', 'newline\n', '\n', ].join(''), }, { name: 'HTML with bullets', inputHtmlBody: [ '', ].join(''), wantPadLines: [ '', '', '', '', '
', ], wantExportHtmlBody: [ '', '
', ].map((l) => l.replace(/^\s+/, '')).join(''), wantExportText: [ '\t* bullet line 1\n', '\t* bullet line 2\n', '\t\t* bullet2 line 1\n', '\t\t* bullet2 line 2\n', '\n', ].join(''), }, { name: 'HTML with bullets and newlines', inputHtmlBody: [ '', '
', '', '
', '', ].join(''), wantPadLines: [ '', '
', '', '', '
', '', '
', ], wantExportHtmlBody: [ '', '
', '', '
', '', '
', ].map((l) => l.replace(/^\s+/, '')).join(''), wantExportText: [ '\t* bullet line 1\n', '\n', '\t* bullet line 2\n', '\t\t* bullet2 line 1\n', '\n', '\t\t* bullet2 line 2\n', '\n', ].join(''), }, { name: 'HTML with bullets, newlines, and attributes', inputHtmlBody: [ '', '
', '', '
', '', ].join(''), wantPadLines: [ '', '
', '', '', '
', '', '', '', '
', ], wantExportHtmlBody: [ '', '
', '', '
', '', '
', ].map((l) => l.replace(/^\s+/, '')).join(''), wantExportText: [ '\t* bullet line 1\n', '\n', '\t* bullet line 2\n', '\t\t* bullet2 line 1\n', '\n', '\t\t\t\t* bullet4 line 2 bisu\n', '\t\t\t\t* bullet4 line 2 bs\n', '\t\t\t\t* bullet4 line 2 uuis\n', '\n', ].join(''), }, { name: 'HTML with nested bullets', inputHtmlBody: [ '', '', '', ].join(''), wantPadLines: [ '', '', '', '', '', '', '', '
', ], wantExportHtmlBody: [ '', '
', ].map((l) => l.replace(/^\s+/, '')).join(''), wantExportText: [ '\t* bullet line 1\n', '\t* bullet line 2\n', '\t\t* bullet2 line 1\n', '\t\t\t\t* bullet4 line 2\n', '\t\t\t\t* bullet4 line 2\n', '\t\t\t\t* bullet4 line 2\n', '\t\t\t* bullet3 line 1\n', '\n', ].join(''), }, { name: 'HTML with 8 levels of bullets, newlines, and attributes', inputHtmlBody: [ '', '
', '', '
', '', ].join(''), wantPadLines: [ '', '
', '', '', '
', '', '', '', '', '', '', '
', ], wantExportHtmlBody: [ '', '
', '', '
', '', '
', ].map((l) => l.replace(/^\s+/, '')).join(''), wantExportText: [ '\t* bullet line 1\n', '\n', '\t* bullet line 2\n', '\t\t* bullet2 line 1\n', '\n', '\t\t\t\t* bullet4 line 2 bisu\n', '\t\t\t\t* bullet4 line 2 bs\n', '\t\t\t\t* bullet4 line 2 uuis\n', '\t\t\t\t\t\t\t\t* foo\n', '\t\t\t\t\t\t\t\t* foobar bs\n', '\t\t\t\t\t* foobar\n', '\n', ].join(''), }, { name: 'HTML with ordered lists', inputHtmlBody: [ '
  1. number 1 line 1
', '
  1. number 2 line 2
', ].join(''), wantPadLines: [ '
  1. number 1 line 1
', '
  1. number 2 line 2
', '
', ], wantExportHtmlBody: [ '
    ', '
  1. number 1 line 1
  2. ', '
  3. number 2 line 2
  4. ', '
', '
', ].map((l) => l.replace(/^\s+/, '')).join(''), wantExportText: [ '\t1. number 1 line 1\n', '\t2. number 2 line 2\n', '\n', ].join(''), }, ]; let confirm; before(async function () { this.timeout(60000); await new Promise( (resolve, reject) => helper.newPad((err) => err != null ? reject(err) : resolve())); confirm = helper.padChrome$.window.confirm; helper.padChrome$.window.confirm = () => true; // As of 2021-02-22 a mutable FileList cannot be directly created so DataTransfer is used as a // hack to access a mutable FileList for testing the '' element. DataTransfer // itself is quite new so support for it is tested here. See: // * https://github.com/whatwg/html/issues/3269 // * https://stackoverflow.com/q/47119426 try { const dt = new DataTransfer(); dt.items.add(new File(['testing'], 'file.txt', {type: 'text/plain'})); // Supposedly all modern browsers support a settable HTMLInputElement.files property, but // Firefox 52 complains. helper.padChrome$('#importform input[type=file]')[0].files = dt.files; } catch (err) { return this.skip(); } }); after(async function () { helper.padChrome$.window.confirm = confirm; }); beforeEach(async function () { const popup = helper.padChrome$('#import_export'); const isVisible = () => popup.hasClass('popup-show'); if (isVisible()) return; const button = helper.padChrome$('button[data-l10n-id="pad.toolbar.import_export.title"]'); button.click(); await helper.waitForPromise(isVisible); }); const docToHtml = (() => { const s = new XMLSerializer(); return (doc) => s.serializeToString(doc); })(); const htmlToDoc = (() => { const p = new DOMParser(); return (html) => p.parseFromString(html, 'text/html'); })(); const htmlBodyToDoc = (htmlBody) => { const doc = document.implementation.createHTMLDocument(); $('body', doc).html(htmlBody); return doc; }; for (const tc of testCases) { describe(tc.name, function () { it('import', async function () { const ext = tc.inputHtmlBody ? 'html' : 'txt'; const contents = ext === 'html' ? docToHtml(htmlBodyToDoc(tc.inputHtmlBody)) : tc.inputText; // DataTransfer is used as a hacky way to get a mutable FileList. For details, see: // https://stackoverflow.com/q/47119426 const dt = new DataTransfer(); dt.items.add(new File([contents], `file.${ext}`, {type: 'text/plain'})); const form = helper.padChrome$('#importform'); form.find('input[type=file]')[0].files = dt.files; form.find('#importsubmitinput').submit(); try { await helper.waitForPromise(() => { const got = helper.linesDiv(); if (got.length !== tc.wantPadLines.length) return false; for (let i = 0; i < got.length; i++) { const gotDiv = $('
').html(got[i].html()); const wantDiv = $('
').html(tc.wantPadLines[i]); if (!gotDiv[0].isEqualNode(wantDiv[0])) return false; } return true; }); } catch (err) { const formatLine = (l) => ` ${JSON.stringify(l)}`; const g = helper.linesDiv().map((div) => formatLine(div.html())).join('\n'); const w = tc.wantPadLines.map(formatLine).join('\n'); throw new Error(`Import failed. Got pad lines:\n${g}\nWant pad lines:\n${w}`); } }); it('export to HTML', async function () { const link = helper.padChrome$('#exporthtmla').attr('href'); const url = new URL(link, helper.padChrome$.window.location.href).href; const gotHtml = await $.ajax({url, dataType: 'html'}); const gotBody = $('body', htmlToDoc(gotHtml)); gotBody.html(gotBody.html().replace(/^\s+|\s+$/g, '')); const wantBody = $('body', htmlBodyToDoc(tc.wantExportHtmlBody)); if (!gotBody[0].isEqualNode(wantBody[0])) { throw new Error(`Got exported HTML body:\n ${JSON.stringify(gotBody.html())}\n` + `Want HTML body:\n ${JSON.stringify(wantBody.html())}`); } }); it('export to text', async function () { const link = helper.padChrome$('#exportplaina').attr('href'); const url = new URL(link, helper.padChrome$.window.location.href).href; const got = await $.ajax({url, dataType: 'text'}); expect(got).to.be(tc.wantExportText); }); }); } });