123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861 |
- /**
- * Tests for input and output of various types to ensure the editors work as expected
- * and retain data integrity, especially when it comes to special characters.
- *
- * @author n1474335 [n1474335@gmail.com]
- * @copyright Crown Copyright 2023
- * @license Apache-2.0
- */
- const SPECIAL_CHARS = [
- "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000a\u000b\u000c\u000d\u000e\u000f",
- "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f",
- "\u007f",
- "\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f",
- "\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f",
- "\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\u2066\u2067\u2069\ufeff\ufff9\ufffa\ufffb\ufffc"
- ].join("");
- const ALL_BYTES = [
- "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
- "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
- "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f",
- "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f",
- "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
- "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f",
- "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f",
- "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f",
- "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f",
- "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
- "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf",
- "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf",
- "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf",
- "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf",
- "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef",
- "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
- ].join("");
- const PUA_CHARS = "\ue000\ue001\uf8fe\uf8ff";
- const MULTI_LINE_STRING =`"You know," said Arthur, "it's at times like this, when I'm trapped in a Vogon airlock with a man from Betelgeuse, and about to die of asphyxiation in deep space that I really wish I'd listened to what my mother told me when I was young."
- "Why, what did she tell you?"
- "I don't know, I didn't listen."`;
- const SELECTABLE_STRING = `ONE
- two
- ONE
- three
- ONE
- four
- ONE`;
- // Descriptions for named control characters
- const CONTROL_CHAR_NAMES = {
- 0: "null",
- 7: "bell",
- 8: "backspace",
- 10: "line feed",
- 11: "vertical tab",
- 13: "carriage return",
- 27: "escape",
- 8203: "zero width space",
- 8204: "zero width non-joiner",
- 8205: "zero width joiner",
- 8206: "left-to-right mark",
- 8207: "right-to-left mark",
- 8232: "line separator",
- 8237: "left-to-right override",
- 8238: "right-to-left override",
- 8294: "left-to-right isolate",
- 8295: "right-to-left isolate",
- 8297: "pop directional isolate",
- 8233: "paragraph separator",
- 65279: "zero width no-break space",
- 65532: "object replacement"
- };
- module.exports = {
- before: browser => {
- browser
- .resizeWindow(1280, 800)
- .url(browser.launchUrl)
- .useCss()
- .waitForElementNotPresent("#preloader", 10000)
- .click("#auto-bake-label");
- },
- "CodeMirror has loaded correctly": browser => {
- /* Editor has initialised */
- browser
- .useCss()
- // Input
- .waitForElementVisible("#input-text")
- .waitForElementVisible("#input-text .cm-editor")
- .waitForElementVisible("#input-text .cm-editor .cm-scroller")
- .waitForElementVisible("#input-text .cm-editor .cm-scroller .cm-content")
- .waitForElementVisible("#input-text .cm-editor .cm-scroller .cm-content .cm-line")
- // Output
- .waitForElementVisible("#output-text")
- .waitForElementVisible("#output-text .cm-editor")
- .waitForElementVisible("#output-text .cm-editor .cm-scroller")
- .waitForElementVisible("#output-text .cm-editor .cm-scroller .cm-content")
- .waitForElementVisible("#output-text .cm-editor .cm-scroller .cm-content .cm-line");
- /* Status bar is showing and has correct values */
- browser // Input
- .waitForElementVisible("#input-text .cm-status-bar")
- .waitForElementVisible("#input-text .cm-status-bar .stats-length-value")
- .expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("0");
- browser.waitForElementVisible("#input-text .cm-status-bar .stats-lines-value")
- .expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("1");
- browser.waitForElementVisible("#input-text .cm-status-bar .chr-enc-value")
- .expect.element("#input-text .cm-status-bar .chr-enc-value").text.to.equal("Raw Bytes");
- browser.waitForElementVisible("#input-text .cm-status-bar .eol-value")
- .expect.element("#input-text .cm-status-bar .eol-value").text.to.equal("LF");
- browser // Output
- .waitForElementVisible("#output-text .cm-status-bar")
- .waitForElementVisible("#output-text .cm-status-bar .stats-length-value")
- .expect.element("#output-text .cm-status-bar .stats-length-value").text.to.equal("0");
- browser.waitForElementVisible("#output-text .cm-status-bar .stats-lines-value")
- .expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("1");
- browser.waitForElementVisible("#output-text .cm-status-bar .baking-time-info")
- .expect.element("#output-text .cm-status-bar .baking-time-info").text.to.contain("ms");
- browser.waitForElementVisible("#output-text .cm-status-bar .chr-enc-value")
- .expect.element("#output-text .cm-status-bar .chr-enc-value").text.to.equal("Raw Bytes");
- browser.waitForElementVisible("#output-text .cm-status-bar .eol-value")
- .expect.element("#output-text .cm-status-bar .eol-value").text.to.equal("LF");
- },
- "Adding content": browser => {
- /* Status bar updates correctly */
- setInput(browser, MULTI_LINE_STRING);
- browser.expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("301");
- browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("3");
- browser.expect.element("#input-text .cm-status-bar .chr-enc-value").text.to.equal("Raw Bytes");
- browser.expect.element("#input-text .cm-status-bar .eol-value").text.to.equal("LF");
- browser.expect.element("#output-text .cm-status-bar .stats-length-value").text.to.equal("0");
- browser.expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("1");
- browser.expect.element("#output-text .cm-status-bar .baking-time-info").text.to.contain("ms");
- browser.expect.element("#output-text .cm-status-bar .chr-enc-value").text.to.equal("Raw Bytes");
- browser.expect.element("#output-text .cm-status-bar .eol-value").text.to.equal("LF");
- /* Output updates correctly */
- bake(browser);
- browser.expect.element("#output-text .cm-status-bar .stats-length-value").text.to.equal("301");
- browser.expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("3");
- browser.expect.element("#output-text .cm-status-bar .baking-time-info").text.to.contain("ms");
- browser.expect.element("#output-text .cm-status-bar .chr-enc-value").text.to.equal("Raw Bytes");
- browser.expect.element("#output-text .cm-status-bar .eol-value").text.to.equal("LF");
- },
- "Special content": browser => {
- /* Special characters are rendered correctly */
- setInput(browser, SPECIAL_CHARS, false);
- // First line
- for (let i = 0x0; i <= 0x8; i++) {
- browser.expect.element(`#input-text .cm-line:nth-of-type(1) .cm-specialChar:nth-of-type(${i+1})`)
- .to.have.property("title").equals(`Control character ${CONTROL_CHAR_NAMES[i] || "0x" + i.toString(16)}`);
- browser.expect.element(`#input-text .cm-line:nth-of-type(1) .cm-specialChar:nth-of-type(${i+1})`)
- .text.to.equal(String.fromCharCode(0x2400 + i));
- }
- // Tab \u0009
- browser.expect.element(`#input-text .cm-line:nth-of-type(1)`).to.have.property("textContent").match(/\u0009$/);
- // Line feed \u000a
- browser.expect.element(`#input-text .cm-line:nth-of-type(1)`).to.have.property("textContent").match(/^.{10}$/);
- browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("2");
- // Second line
- for (let i = 0x0b; i < SPECIAL_CHARS.length; i++) {
- const index = SPECIAL_CHARS.charCodeAt(i);
- const name = CONTROL_CHAR_NAMES[index] || "0x" + index.toString(16);
- const value = index >= 32 ? "\u2022" : String.fromCharCode(0x2400 + index);
- browser.expect.element(`#input-text .cm-line:nth-of-type(2) .cm-specialChar:nth-of-type(${i-10})`)
- .to.have.property("title").equals(`Control character ${name}`);
- browser.expect.element(`#input-text .cm-line:nth-of-type(2) .cm-specialChar:nth-of-type(${i-10})`)
- .text.to.equal(value);
- }
- /* Output renders correctly */
- setChrEnc(browser, "output", "UTF-8");
- bake(browser);
- // First line
- for (let i = 0x0; i <= 0x8; i++) {
- browser.expect.element(`#output-text .cm-line:nth-of-type(1) .cm-specialChar:nth-of-type(${i+1})`)
- .to.have.property("title").equals(`Control character ${CONTROL_CHAR_NAMES[i] || "0x" + i.toString(16)}`);
- browser.expect.element(`#output-text .cm-line:nth-of-type(1) .cm-specialChar:nth-of-type(${i+1})`)
- .text.to.equal(String.fromCharCode(0x2400 + i));
- }
- // Tab \u0009
- browser.expect.element(`#output-text .cm-line:nth-of-type(1)`).to.have.property("textContent").match(/\u0009$/);
- // Line feed \u000a
- browser.expect.element(`#output-text .cm-line:nth-of-type(1)`).to.have.property("textContent").match(/^.{10}$/);
- browser.expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("2");
- // Second line
- for (let i = 0x0b; i < SPECIAL_CHARS.length; i++) {
- const index = SPECIAL_CHARS.charCodeAt(i);
- const name = CONTROL_CHAR_NAMES[index] || "0x" + index.toString(16);
- const value = index >= 32 ? "\u2022" : String.fromCharCode(0x2400 + index);
- browser.expect.element(`#output-text .cm-content .cm-line:nth-of-type(2) .cm-specialChar:nth-of-type(${i-10})`)
- .to.have.property("title").equals(`Control character ${name}`);
- browser.expect.element(`#output-text .cm-content .cm-line:nth-of-type(2) .cm-specialChar:nth-of-type(${i-10})`)
- .text.to.equal(value);
- }
- /* Bytes are rendered correctly */
- setInput(browser, ALL_BYTES, false);
- // Expect length to be 255, since one character is creating a newline
- browser.expect.element(`#input-text .cm-content`).to.have.property("textContent").match(/^.{255}$/);
- browser.expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("256");
- browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("2");
- /* PUA \ue000-\uf8ff */
- setInput(browser, PUA_CHARS, false);
- setChrEnc(browser, "output", "UTF-8");
- bake(browser);
- // Confirm input and output as expected
- /* In order to render whitespace characters as control character pictures in the output, even
- when they are the designated line separator, CyberChef sometimes chooses to represent them
- internally using the Unicode Private Use Area (https://en.wikipedia.org/wiki/Private_Use_Areas).
- See `Utils.escapeWhitespace()` for an example of this.
- Therefore, PUA characters should be rendered normally in the Input but as control character
- pictures in the output.
- */
- browser.expect.element(`#input-text .cm-content`).to.have.property("textContent").match(/^\ue000\ue001\uf8fe\uf8ff$/);
- browser.expect.element(`#output-text .cm-content`).to.have.property("textContent").match(/^\u2400\u2401\u3cfe\u3cff$/);
- /* Can be copied */
- setInput(browser, SPECIAL_CHARS, false);
- setChrEnc(browser, "output", "UTF-8");
- bake(browser);
- // Manual copy
- browser
- .doubleClick("#output-text .cm-content .cm-line:nth-of-type(1) .cm-specialChar:nth-of-type(1)")
- .waitForElementVisible("#output-text .cm-selectionBackground");
- copy(browser);
- paste(browser, "#search"); // Paste into search box as this won't mess with the values
- // Ensure that the values are as expected
- browser.expect.element("#search").to.have.value.that.equals("\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008");
- browser.clearValue("#search");
- // Raw copy
- browser
- .click("#copy-output")
- .pause(100);
- paste(browser, "#search"); // Paste into search box as this won't mess with the values
- // Ensure that the values are as expected
- browser.expect.element("#search").to.have.value.that.matches(/^\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009/);
- browser.clearValue("#search");
- },
- "HTML output": browser => {
- /* Displays correctly */
- loadRecipe(browser, "Entropy", ALL_BYTES);
- bake(browser);
- browser
- .waitForElementVisible("#output-html")
- .waitForElementVisible("#output-html #chart-area");
- /* Status bar widgets are disabled */
- browser.expect.element("#output-text .cm-status-bar .disabled .stats-length-value").to.be.visible;
- browser.expect.element("#output-text .cm-status-bar .disabled .stats-lines-value").to.be.visible;
- browser.expect.element("#output-text .cm-status-bar .disabled .chr-enc-value").to.be.visible;
- browser.expect.element("#output-text .cm-status-bar .disabled .eol-value").to.be.visible;
- /* Displays special chars correctly */
- loadRecipe(browser, "To Table", ",\u0000\u0001\u0002\u0003\u0004", [",", "\\r\\n", false, "HTML"]);
- bake(browser);
- for (let i = 0x0; i <= 0x4; i++) {
- browser.expect.element(`#output-html .cm-specialChar:nth-of-type(${i+1})`)
- .to.have.property("title").equals(`Control character ${CONTROL_CHAR_NAMES[i] || "0x" + i.toString(16)}`);
- browser.expect.element(`#output-html .cm-specialChar:nth-of-type(${i+1})`)
- .text.to.equal(String.fromCharCode(0x2400 + i));
- }
- /* Can be copied */
- // Raw copy
- browser
- .click("#copy-output")
- .pause(100);
- paste(browser, "#search"); // Paste into search box as this won't mess with the values
- // Ensure that the values are as expected
- browser.expect.element("#search").to.have.value.that.matches(/\u0000\u0001\u0002\u0003\u0004/);
- browser.clearValue("#search");
- },
- "Highlighting": browser => {
- setInput(browser, SELECTABLE_STRING);
- bake(browser);
- /* Selecting input text also selects other instances in input and output */
- browser // Input
- .click("#auto-bake-label")
- .doubleClick("#input-text .cm-content .cm-line:nth-of-type(1)")
- .waitForElementVisible("#input-text .cm-selectionLayer .cm-selectionBackground")
- .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(1) .cm-selectionMatch")
- .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(2) .cm-selectionMatch")
- .waitForElementVisible("#input-text .cm-content .cm-line:nth-of-type(3) .cm-selectionMatch")
- .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(4) .cm-selectionMatch")
- .waitForElementVisible("#input-text .cm-content .cm-line:nth-of-type(5) .cm-selectionMatch")
- .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(6) .cm-selectionMatch")
- .waitForElementVisible("#input-text .cm-content .cm-line:nth-of-type(7) .cm-selectionMatch");
- browser // Output
- .waitForElementVisible("#output-text .cm-selectionLayer .cm-selectionBackground")
- .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(1) .cm-selectionMatch")
- .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(2) .cm-selectionMatch")
- .waitForElementVisible("#output-text .cm-content .cm-line:nth-of-type(3) .cm-selectionMatch")
- .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(4) .cm-selectionMatch")
- .waitForElementVisible("#output-text .cm-content .cm-line:nth-of-type(5) .cm-selectionMatch")
- .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(6) .cm-selectionMatch")
- .waitForElementVisible("#output-text .cm-content .cm-line:nth-of-type(7) .cm-selectionMatch");
- /* Selecting output text highlights in input */
- browser // Output
- .click("#output-text")
- .waitForElementNotPresent("#input-text .cm-selectionLayer .cm-selectionBackground")
- .waitForElementNotPresent("#output-text .cm-selectionLayer .cm-selectionBackground")
- .waitForElementNotPresent("#input-text .cm-content .cm-line .cm-selectionMatch")
- .waitForElementNotPresent("#output-text .cm-content .cm-line .cm-selectionMatch")
- .doubleClick("#output-text .cm-content .cm-line:nth-of-type(7)")
- .waitForElementVisible("#output-text .cm-selectionLayer .cm-selectionBackground")
- .waitForElementVisible("#output-text .cm-content .cm-line:nth-of-type(1) .cm-selectionMatch")
- .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(2) .cm-selectionMatch")
- .waitForElementVisible("#output-text .cm-content .cm-line:nth-of-type(3) .cm-selectionMatch")
- .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(4) .cm-selectionMatch")
- .waitForElementVisible("#output-text .cm-content .cm-line:nth-of-type(5) .cm-selectionMatch")
- .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(6) .cm-selectionMatch")
- .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(7) .cm-selectionMatch");
- browser // Input
- .waitForElementVisible("#input-text .cm-selectionLayer .cm-selectionBackground")
- .waitForElementVisible("#input-text .cm-content .cm-line:nth-of-type(1) .cm-selectionMatch")
- .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(2) .cm-selectionMatch")
- .waitForElementVisible("#input-text .cm-content .cm-line:nth-of-type(3) .cm-selectionMatch")
- .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(4) .cm-selectionMatch")
- .waitForElementVisible("#input-text .cm-content .cm-line:nth-of-type(5) .cm-selectionMatch")
- .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(6) .cm-selectionMatch")
- .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(7) .cm-selectionMatch");
- // Turn autobake off again
- browser.click("#auto-bake-label");
- },
- "Character encoding": browser => {
- const CHINESE_CHARS = "不要恐慌。";
- /* Dropup works */
- /* Selecting changes output correctly */
- setInput(browser, CHINESE_CHARS, false);
- setChrEnc(browser, "input", "UTF-8");
- bake(browser);
- expectOutput(browser, "\u00E4\u00B8\u008D\u00E8\u00A6\u0081\u00E6\u0081\u0090\u00E6\u0085\u008C\u00E3\u0080\u0082");
- /* Changing output to match input works as expected */
- setChrEnc(browser, "output", "UTF-8");
- bake(browser);
- expectOutput(browser, CHINESE_CHARS);
- /* Encodings appear in the URL */
- browser.assert.urlContains("ienc=65001");
- browser.assert.urlContains("oenc=65001");
- /* Preserved when changing tabs */
- browser
- .click("#btn-new-tab")
- .waitForElementVisible("#input-tabs li:nth-of-type(2).active-input-tab");
- browser.expect.element("#input-text .chr-enc-value").text.that.equals("Raw Bytes");
- browser.expect.element("#output-text .chr-enc-value").text.that.equals("Raw Bytes");
- setChrEnc(browser, "input", "UTF-7");
- setChrEnc(browser, "output", "UTF-7");
- browser
- .click("#input-tabs li:nth-of-type(1)")
- .waitForElementVisible("#input-tabs li:nth-of-type(1).active-input-tab");
- browser.expect.element("#input-text .chr-enc-value").text.that.equals("UTF-8");
- browser.expect.element("#output-text .chr-enc-value").text.that.equals("UTF-8");
- /* Try various encodings */
- // These are not meant to be realistic encodings for this data
- setInput(browser, CHINESE_CHARS, false);
- setChrEnc(browser, "input", "UTF-8");
- setChrEnc(browser, "output", "UTF-16LE");
- bake(browser);
- expectOutput(browser, "\uB8E4\uE88D\u81A6\u81E6\uE690\u8C85\u80E3");
- setChrEnc(browser, "output", "Simplified Chinese GBK");
- bake(browser);
- expectOutput(browser, "\u6D93\u5D88\uFDFF\u93AD\u612D\u53A1\u9286\u0000");
- setChrEnc(browser, "input", "UTF-7");
- bake(browser);
- expectOutput(browser, "+Tg0-+iYE-+YFA-+YUw-");
- setChrEnc(browser, "input", "Traditional Chinese Big5");
- bake(browser);
- expectOutput(browser, "\u3043\u74B6\uFDFF\u7A3A\uFDFF");
- setChrEnc(browser, "output", "Windows-1251 Cyrillic");
- bake(browser);
- expectOutput(browser, "\u00A4\u0408\u00ADn\u00AE\u0408\u00B7W\u040EC");
- },
- "Line endings": browser => {
- /* Dropup works */
- /* Selecting changes view in input */
- setInput(browser, MULTI_LINE_STRING);
- // Line endings: LF
- // Input
- browser
- .waitForElementPresent("#input-text .cm-content .cm-line:nth-of-type(3)")
- .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(4)")
- .waitForElementNotPresent("#input-text .cm-content .cm-specialChar");
- browser.expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("301");
- browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("3");
- // Output
- bake(browser);
- browser
- .waitForElementPresent("#output-text .cm-content .cm-line:nth-of-type(3)")
- .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(4)")
- .waitForElementNotPresent("#output-text .cm-content .cm-specialChar");
- browser.expect.element("#output-text .cm-status-bar .stats-length-value").text.to.equal("301");
- browser.expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("3");
- // Input EOL: VT
- setEOLSeq(browser, "input", "VT");
- // Input
- browser
- .waitForElementPresent("#input-text .cm-content .cm-line:nth-of-type(1)")
- .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(2)")
- .waitForElementPresent("#input-text .cm-content .cm-specialChar");
- browser.expect.element("#input-text .cm-content .cm-specialChar").text.to.equal("␊");
- browser.expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("301");
- browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("1");
- // Output
- bake(browser);
- browser
- .waitForElementPresent("#output-text .cm-content .cm-line:nth-of-type(3)")
- .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(4)")
- .waitForElementNotPresent("#output-text .cm-content .cm-specialChar");
- browser.expect.element("#output-text .cm-status-bar .stats-length-value").text.to.equal("301");
- browser.expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("3");
- // Output EOL: VT
- setEOLSeq(browser, "output", "VT");
- // Input
- browser
- .waitForElementPresent("#input-text .cm-content .cm-line:nth-of-type(1)")
- .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(2)")
- .waitForElementPresent("#input-text .cm-content .cm-specialChar");
- browser.expect.element("#input-text .cm-content .cm-specialChar").text.to.equal("␊");
- browser.expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("301");
- browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("1");
- // Output
- browser
- .waitForElementPresent("#output-text .cm-content .cm-line:nth-of-type(1)")
- .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(2)")
- .waitForElementPresent("#output-text .cm-content .cm-specialChar");
- browser.expect.element("#output-text .cm-content .cm-specialChar").text.to.equal("␊");
- browser.expect.element("#output-text .cm-status-bar .stats-length-value").text.to.equal("301");
- browser.expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("1");
- /* Adding new line ending changes output correctly */
- browser.sendKeys("#input-text .cm-content", browser.Keys.RETURN);
- // Input
- browser
- .waitForElementPresent("#input-text .cm-content .cm-line:nth-of-type(2)")
- .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(3)");
- browser.expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("302");
- browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("2");
- // Output
- bake(browser);
- browser
- .waitForElementPresent("#output-text .cm-content .cm-line:nth-of-type(2)")
- .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(3)");
- browser.expect.element("#output-text .cm-status-bar .stats-length-value").text.to.equal("302");
- browser.expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("2");
- // Input EOL: CRLF
- setEOLSeq(browser, "input", "CRLF");
- // Output EOL: CR
- setEOLSeq(browser, "output", "CR");
- browser.sendKeys("#input-text .cm-content", browser.Keys.RETURN);
- // Input
- browser
- .waitForElementPresent("#input-text .cm-content .cm-line:nth-of-type(2)")
- .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(3)")
- .waitForElementPresent("#input-text .cm-content .cm-line:nth-of-type(1) .cm-specialChar:nth-of-type(3)");
- browser.expect.element("#input-text .cm-content .cm-line:nth-of-type(1) .cm-specialChar:nth-of-type(3)").text.to.equal("␋");
- browser.expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("304");
- browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("2");
- // Output
- bake(browser);
- browser
- .waitForElementPresent("#output-text .cm-content .cm-line:nth-of-type(2)")
- .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(3)")
- .waitForElementPresent("#output-text .cm-content .cm-line:nth-of-type(2) .cm-specialChar");
- browser.expect.element("#output-text .cm-content .cm-line:nth-of-type(2) .cm-specialChar").text.to.equal("␊");
- browser.expect.element("#output-text .cm-status-bar .stats-length-value").text.to.equal("304");
- browser.expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("2");
- /* Line endings appear in the URL */
- browser.assert.urlContains("ieol=%0D%0A");
- browser.assert.urlContains("oeol=%0D");
- /* Preserved when changing tabs */
- browser
- .click("#btn-new-tab")
- .waitForElementVisible("#input-tabs li:nth-of-type(2).active-input-tab");
- browser.expect.element("#input-text .eol-value").text.that.equals("LF");
- browser.expect.element("#output-text .eol-value").text.that.equals("LF");
- setEOLSeq(browser, "input", "FF");
- setEOLSeq(browser, "output", "LS");
- browser
- .click("#input-tabs li:nth-of-type(1)")
- .waitForElementVisible("#input-tabs li:nth-of-type(1).active-input-tab");
- browser.expect.element("#input-text .eol-value").text.that.equals("CRLF");
- browser.expect.element("#output-text .eol-value").text.that.equals("CR");
- },
- "File inputs": browser => {
- clear(browser);
- /* Side panel displays correct info */
- uploadFile(browser, "files/TowelDay.jpeg");
- browser
- .waitForElementVisible("#input-text .cm-file-details")
- .waitForElementVisible("#input-text .cm-file-details .file-details-toggle-shown")
- .waitForElementVisible("#input-text .cm-file-details .file-details-thumbnail")
- .waitForElementVisible("#input-text .cm-file-details .file-details-name")
- .waitForElementVisible("#input-text .cm-file-details .file-details-size")
- .waitForElementVisible("#input-text .cm-file-details .file-details-type")
- .waitForElementVisible("#input-text .cm-file-details .file-details-loaded");
- browser.expect.element("#input-text .cm-file-details .file-details-name").text.that.equals("TowelDay.jpeg");
- browser.expect.element("#input-text .cm-file-details .file-details-size").text.that.equals("61,379 bytes");
- browser.expect.element("#input-text .cm-file-details .file-details-type").text.that.equals("image/jpeg");
- browser.expect.element("#input-text .cm-file-details .file-details-loaded").text.that.equals("100%");
- /* Side panel can be hidden */
- browser
- .click("#input-text .cm-file-details .file-details-toggle-shown")
- .waitForElementNotPresent("#input-text .cm-file-details .file-details-toggle-shown")
- .waitForElementVisible("#input-text .cm-file-details .file-details-toggle-hidden")
- .expect.element("#input-text .cm-file-details").to.have.css("width").which.equals("1px");
- browser
- .click("#input-text .cm-file-details .file-details-toggle-hidden")
- .waitForElementNotPresent("#input-text .cm-file-details .file-details-toggle-hidden")
- .waitForElementVisible("#input-text .cm-file-details .file-details-toggle-shown")
- .expect.element("#input-text .cm-file-details").to.have.css("width").which.equals("200px");
- },
- "Folder inputs": browser => {
- clear(browser);
- /* Side panel displays correct info */
- uploadFolder(browser, "files");
- // Tab 1
- browser
- .click("#input-tabs li:nth-of-type(1)")
- .waitForElementVisible("#input-tabs li:nth-of-type(1).active-input-tab");
- browser
- .waitForElementVisible("#input-text .cm-file-details")
- .waitForElementVisible("#input-text .cm-file-details .file-details-toggle-shown")
- .waitForElementVisible("#input-text .cm-file-details .file-details-thumbnail")
- .waitForElementVisible("#input-text .cm-file-details .file-details-name")
- .waitForElementVisible("#input-text .cm-file-details .file-details-size")
- .waitForElementVisible("#input-text .cm-file-details .file-details-type")
- .waitForElementVisible("#input-text .cm-file-details .file-details-loaded");
- browser.expect.element("#input-text .cm-file-details .file-details-name").text.that.equals("TowelDay.jpeg");
- browser.expect.element("#input-text .cm-file-details .file-details-size").text.that.equals("61,379 bytes");
- browser.expect.element("#input-text .cm-file-details .file-details-type").text.that.equals("image/jpeg");
- browser.expect.element("#input-text .cm-file-details .file-details-loaded").text.that.equals("100%");
- // Tab 2
- browser
- .click("#input-tabs li:nth-of-type(2)")
- .waitForElementVisible("#input-tabs li:nth-of-type(2).active-input-tab");
- browser
- .waitForElementVisible("#input-text .cm-file-details")
- .waitForElementVisible("#input-text .cm-file-details .file-details-toggle-shown")
- .waitForElementVisible("#input-text .cm-file-details .file-details-thumbnail")
- .waitForElementVisible("#input-text .cm-file-details .file-details-name")
- .waitForElementVisible("#input-text .cm-file-details .file-details-size")
- .waitForElementVisible("#input-text .cm-file-details .file-details-type")
- .waitForElementVisible("#input-text .cm-file-details .file-details-loaded");
- browser.expect.element("#input-text .cm-file-details .file-details-name").text.that.equals("Hitchhikers_Guide.jpeg");
- browser.expect.element("#input-text .cm-file-details .file-details-size").text.that.equals("36,595 bytes");
- browser.expect.element("#input-text .cm-file-details .file-details-type").text.that.equals("image/jpeg");
- browser.expect.element("#input-text .cm-file-details .file-details-loaded").text.that.equals("100%");
- },
- "Loading from URL": browser => {
- /* Complex deep link populates the input correctly (encoding, eol, input) */
- },
- "Replace input with output": browser => {
- /* Input is correctly populated */
- /* Special characters, encodings and line endings all as expected */
- },
- after: browser => {
- browser.end();
- }
- };
- /** @function
- * Clears the recipe and input
- *
- * @param {Browser} browser - Nightwatch client
- */
- function clear(browser) {
- browser
- .useCss()
- .click("#clr-recipe")
- .click("#clr-io")
- .waitForElementNotPresent("#rec-list li.operation")
- .expect.element("#input-text .cm-content").text.that.equals("");
- }
- /** @function
- * Sets the input to the desired string
- *
- * @param {Browser} browser - Nightwatch client
- * @param {string} input - The text to populate the input with
- * @param {boolean} [type=true] - Whether to type the characters in by using sendKeys,
- * or to set the value of the editor directly (useful for special characters)
- */
- function setInput(browser, input, type=true) {
- clear(browser);
- if (type) {
- browser
- .useCss()
- .sendKeys("#input-text .cm-content", input)
- .pause(100);
- } else {
- browser.execute(text => {
- window.app.setInput(text);
- }, [input]);
- }
- }
- /** @function
- * Triggers a bake
- *
- * @param {Browser} browser - Nightwatch client
- */
- function bake(browser) {
- browser
- .click("#bake")
- .pause(100)
- .waitForElementPresent("#stale-indicator.hidden", 5000);
- }
- /** @function
- * Sets the character encoding in the input or output
- *
- * @param {Browser} browser - Nightwatch client
- * @param {string} io - Either "input" or "output"
- * @param {string} enc - The encoding to be set
- */
- function setChrEnc(browser, io, enc) {
- io = `#${io}-text`;
- browser
- .useCss()
- .click(io + " .chr-enc-value")
- .waitForElementVisible(io + " .chr-enc-select .cm-status-bar-select-scroll")
- .click("link text", enc)
- .waitForElementNotVisible(io + " .chr-enc-select .cm-status-bar-select-scroll")
- .expect.element(io + " .chr-enc-value").text.that.equals(enc);
- }
- /** @function
- * Sets the end of line sequence in the input or output
- *
- * @param {Browser} browser - Nightwatch client
- * @param {string} io - Either "input" or "output"
- * @param {string} eol - The sequence to set
- */
- function setEOLSeq(browser, io, eol) {
- io = `#${io}-text`;
- browser
- .useCss()
- .click(io + " .eol-value")
- .waitForElementVisible(io + " .eol-select .cm-status-bar-select-content")
- .click(`${io} .cm-status-bar-select-content a[data-val=${eol}]`)
- .waitForElementNotVisible(io + " .eol-select .cm-status-bar-select-content")
- .expect.element(io + " .eol-value").text.that.equals(eol);
- }
- /** @function
- * Copies whatever is currently selected
- *
- * @param {Browser} browser - Nightwatch client
- */
- function copy(browser) {
- browser.perform(function() {
- const actions = this.actions({async: true});
- // Ctrl + Ins used as this works on Windows, Linux and Mac
- return actions
- .keyDown(browser.Keys.CONTROL)
- .keyDown(browser.Keys.INSERT)
- .keyUp(browser.Keys.INSERT)
- .keyUp(browser.Keys.CONTROL);
- });
- }
- /** @function
- * Pastes into the target element
- *
- * @param {Browser} browser - Nightwatch client
- * @param {string} el - Target element selector
- */
- function paste(browser, el) {
- browser
- .click(el)
- .perform(function() {
- const actions = this.actions({async: true});
- // Shift + Ins used as this works on Windows, Linux and Mac
- return actions
- .keyDown(browser.Keys.SHIFT)
- .keyDown(browser.Keys.INSERT)
- .keyUp(browser.Keys.INSERT)
- .keyUp(browser.Keys.SHIFT);
- })
- .pause(100);
- }
- /** @function
- * Loads a recipe and input
- *
- * @param {Browser} browser - Nightwatch client
- * @param {string|Array<string>} opName - name of operation to be loaded, array for multiple ops
- * @param {string} input - input text for test
- * @param {Array<string>|Array<Array<string>>} args - arguments, nested if multiple ops
- */
- function loadRecipe(browser, opName, input, args) {
- let recipeConfig;
- if (typeof(opName) === "string") {
- recipeConfig = JSON.stringify([{
- "op": opName,
- "args": args
- }]);
- } else if (opName instanceof Array) {
- recipeConfig = JSON.stringify(
- opName.map((op, i) => {
- return {
- op: op,
- args: args.length ? args[i] : []
- };
- })
- );
- } else {
- throw new Error("Invalid operation type. Must be string or array of strings. Received: " + typeof(opName));
- }
- clear(browser);
- setInput(browser, input, false);
- browser
- .urlHash("recipe=" + recipeConfig)
- .waitForElementPresent("#rec-list li.operation");
- }
- /** @function
- * Tests whether the output matches a given value
- *
- * @param {Browser} browser - Nightwatch client
- * @param {string} expected - The expected output value
- */
- function expectOutput(browser, expected) {
- browser.execute(expected => {
- return expected === window.app.manager.output.outputEditorView.state.doc.toString();
- }, [expected]);
- }
- /** @function
- * Uploads a file using the #open-file input
- *
- * @param {Browser} browser - Nightwatch client
- * @param {string} filename - A path to a file in the samples directory
- */
- function uploadFile(browser, filename) {
- const filepath = require("path").resolve(__dirname + "/../samples/" + filename);
- // The file input cannot be interacted with by nightwatch while it is hidden,
- // so we temporarily expose it for the purposes of this test.
- browser.execute(() => {
- document.getElementById("open-file").style.display = "block";
- });
- browser
- .pause(100)
- .setValue("#open-file", filepath)
- .pause(100);
- browser.execute(() => {
- document.getElementById("open-file").style.display = "none";
- });
- browser.waitForElementVisible("#input-text .cm-file-details");
- }
- /** @function
- * Uploads a folder using the #open-folder input
- *
- * @param {Browser} browser - Nightwatch client
- * @param {string} foldername - A path to a folder in the samples directory
- */
- function uploadFolder(browser, foldername) {
- const folderpath = require("path").resolve(__dirname + "/../samples/" + foldername);
- // The folder input cannot be interacted with by nightwatch while it is hidden,
- // so we temporarily expose it for the purposes of this test.
- browser.execute(() => {
- document.getElementById("open-folder").style.display = "block";
- });
- browser
- .pause(100)
- .setValue("#open-folder", folderpath)
- .pause(500);
- browser.execute(() => {
- document.getElementById("open-folder").style.display = "none";
- });
- browser.waitForElementVisible("#input-text .cm-file-details");
- }
|