01_io.js 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  1. /**
  2. * Tests for input and output of various types to ensure the editors work as expected
  3. * and retain data integrity, especially when it comes to special characters.
  4. *
  5. * @author n1474335 [n1474335@gmail.com]
  6. * @copyright Crown Copyright 2023
  7. * @license Apache-2.0
  8. */
  9. // import {
  10. // clear,
  11. // utils.setInput,
  12. // bake,
  13. // setChrEnc,
  14. // setEOLSeq,
  15. // copy,
  16. // paste,
  17. // loadRecipe,
  18. // expectOutput,
  19. // uploadFile,
  20. // uploadFolder
  21. // } from "./browserUtils.js";
  22. const utils = require("./browserUtils.js");
  23. const SPECIAL_CHARS = [
  24. "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000a\u000b\u000c\u000d\u000e\u000f",
  25. "\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f",
  26. "\u007f",
  27. "\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008a\u008b\u008c\u008d\u008e\u008f",
  28. "\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009a\u009b\u009c\u009d\u009e\u009f",
  29. "\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\u2066\u2067\u2069\ufeff\ufff9\ufffa\ufffb\ufffc"
  30. ].join("");
  31. const ALL_BYTES = [
  32. "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
  33. "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
  34. "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f",
  35. "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f",
  36. "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
  37. "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f",
  38. "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f",
  39. "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f",
  40. "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f",
  41. "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
  42. "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf",
  43. "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf",
  44. "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf",
  45. "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf",
  46. "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef",
  47. "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
  48. ].join("");
  49. const PUA_CHARS = "\ue000\ue001\uf8fe\uf8ff";
  50. 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."
  51. "Why, what did she tell you?"
  52. "I don't know, I didn't listen."`;
  53. const SELECTABLE_STRING = `ONE
  54. two
  55. ONE
  56. three
  57. ONE
  58. four
  59. ONE`;
  60. // Descriptions for named control characters
  61. const CONTROL_CHAR_NAMES = {
  62. 0: "null",
  63. 7: "bell",
  64. 8: "backspace",
  65. 10: "line feed",
  66. 11: "vertical tab",
  67. 13: "carriage return",
  68. 27: "escape",
  69. 8203: "zero width space",
  70. 8204: "zero width non-joiner",
  71. 8205: "zero width joiner",
  72. 8206: "left-to-right mark",
  73. 8207: "right-to-left mark",
  74. 8232: "line separator",
  75. 8237: "left-to-right override",
  76. 8238: "right-to-left override",
  77. 8294: "left-to-right isolate",
  78. 8295: "right-to-left isolate",
  79. 8297: "pop directional isolate",
  80. 8233: "paragraph separator",
  81. 65279: "zero width no-break space",
  82. 65532: "object replacement"
  83. };
  84. module.exports = {
  85. before: browser => {
  86. browser
  87. .resizeWindow(1280, 800)
  88. .url(browser.launchUrl)
  89. .useCss()
  90. .waitForElementNotPresent("#preloader", 10000)
  91. .click("#auto-bake-label");
  92. },
  93. "CodeMirror has loaded correctly": browser => {
  94. /* Editor has initialised */
  95. browser
  96. .useCss()
  97. // Input
  98. .waitForElementVisible("#input-text")
  99. .waitForElementVisible("#input-text .cm-editor")
  100. .waitForElementVisible("#input-text .cm-editor .cm-scroller")
  101. .waitForElementVisible("#input-text .cm-editor .cm-scroller .cm-content")
  102. .waitForElementVisible("#input-text .cm-editor .cm-scroller .cm-content .cm-line")
  103. // Output
  104. .waitForElementVisible("#output-text")
  105. .waitForElementVisible("#output-text .cm-editor")
  106. .waitForElementVisible("#output-text .cm-editor .cm-scroller")
  107. .waitForElementVisible("#output-text .cm-editor .cm-scroller .cm-content")
  108. .waitForElementVisible("#output-text .cm-editor .cm-scroller .cm-content .cm-line");
  109. /* Status bar is showing and has correct values */
  110. browser // Input
  111. .waitForElementVisible("#input-text .cm-status-bar")
  112. .waitForElementVisible("#input-text .cm-status-bar .stats-length-value")
  113. .expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("0");
  114. browser.waitForElementVisible("#input-text .cm-status-bar .stats-lines-value")
  115. .expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("1");
  116. browser.waitForElementVisible("#input-text .cm-status-bar .chr-enc-value")
  117. .expect.element("#input-text .cm-status-bar .chr-enc-value").text.to.equal("Raw Bytes");
  118. browser.waitForElementVisible("#input-text .cm-status-bar .eol-value")
  119. .expect.element("#input-text .cm-status-bar .eol-value").text.to.equal("LF");
  120. browser // Output
  121. .waitForElementVisible("#output-text .cm-status-bar")
  122. .waitForElementVisible("#output-text .cm-status-bar .stats-length-value")
  123. .expect.element("#output-text .cm-status-bar .stats-length-value").text.to.equal("0");
  124. browser.waitForElementVisible("#output-text .cm-status-bar .stats-lines-value")
  125. .expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("1");
  126. browser.waitForElementVisible("#output-text .cm-status-bar .baking-time-info")
  127. .expect.element("#output-text .cm-status-bar .baking-time-info").text.to.contain("ms");
  128. browser.waitForElementVisible("#output-text .cm-status-bar .chr-enc-value")
  129. .expect.element("#output-text .cm-status-bar .chr-enc-value").text.to.equal("Raw Bytes");
  130. browser.waitForElementVisible("#output-text .cm-status-bar .eol-value")
  131. .expect.element("#output-text .cm-status-bar .eol-value").text.to.equal("LF");
  132. },
  133. "Adding content": browser => {
  134. /* Status bar updates correctly */
  135. utils.setInput(browser, MULTI_LINE_STRING);
  136. browser.expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("301");
  137. browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("3");
  138. browser.expect.element("#input-text .cm-status-bar .chr-enc-value").text.to.equal("Raw Bytes");
  139. browser.expect.element("#input-text .cm-status-bar .eol-value").text.to.equal("LF");
  140. browser.expect.element("#output-text .cm-status-bar .stats-length-value").text.to.equal("0");
  141. browser.expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("1");
  142. browser.expect.element("#output-text .cm-status-bar .baking-time-info").text.to.contain("ms");
  143. browser.expect.element("#output-text .cm-status-bar .chr-enc-value").text.to.equal("Raw Bytes");
  144. browser.expect.element("#output-text .cm-status-bar .eol-value").text.to.equal("LF");
  145. /* Output updates correctly */
  146. utils.bake(browser);
  147. browser.expect.element("#output-text .cm-status-bar .stats-length-value").text.to.equal("301");
  148. browser.expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("3");
  149. browser.expect.element("#output-text .cm-status-bar .baking-time-info").text.to.contain("ms");
  150. browser.expect.element("#output-text .cm-status-bar .chr-enc-value").text.to.equal("Raw Bytes");
  151. browser.expect.element("#output-text .cm-status-bar .eol-value").text.to.equal("LF");
  152. },
  153. "Special content": browser => {
  154. /* Special characters are rendered correctly */
  155. utils.setInput(browser, SPECIAL_CHARS, false);
  156. // First line
  157. for (let i = 0x0; i <= 0x8; i++) {
  158. browser.expect.element(`#input-text .cm-line:nth-of-type(1) .cm-specialChar:nth-of-type(${i+1})`)
  159. .to.have.property("title").equals(`Control character ${CONTROL_CHAR_NAMES[i] || "0x" + i.toString(16)}`);
  160. browser.expect.element(`#input-text .cm-line:nth-of-type(1) .cm-specialChar:nth-of-type(${i+1})`)
  161. .text.to.equal(String.fromCharCode(0x2400 + i));
  162. }
  163. // Tab \u0009
  164. browser.expect.element(`#input-text .cm-line:nth-of-type(1)`).to.have.property("textContent").match(/\u0009$/);
  165. // Line feed \u000a
  166. browser.expect.element(`#input-text .cm-line:nth-of-type(1)`).to.have.property("textContent").match(/^.{10}$/);
  167. browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("2");
  168. // Second line
  169. for (let i = 0x0b; i < SPECIAL_CHARS.length; i++) {
  170. const index = SPECIAL_CHARS.charCodeAt(i);
  171. const name = CONTROL_CHAR_NAMES[index] || "0x" + index.toString(16);
  172. const value = index >= 32 ? "\u2022" : String.fromCharCode(0x2400 + index);
  173. browser.expect.element(`#input-text .cm-line:nth-of-type(2) .cm-specialChar:nth-of-type(${i-10})`)
  174. .to.have.property("title").equals(`Control character ${name}`);
  175. browser.expect.element(`#input-text .cm-line:nth-of-type(2) .cm-specialChar:nth-of-type(${i-10})`)
  176. .text.to.equal(value);
  177. }
  178. /* Output renders correctly */
  179. utils.setChrEnc(browser, "output", "UTF-8");
  180. utils.bake(browser);
  181. // First line
  182. for (let i = 0x0; i <= 0x8; i++) {
  183. browser.expect.element(`#output-text .cm-line:nth-of-type(1) .cm-specialChar:nth-of-type(${i+1})`)
  184. .to.have.property("title").equals(`Control character ${CONTROL_CHAR_NAMES[i] || "0x" + i.toString(16)}`);
  185. browser.expect.element(`#output-text .cm-line:nth-of-type(1) .cm-specialChar:nth-of-type(${i+1})`)
  186. .text.to.equal(String.fromCharCode(0x2400 + i));
  187. }
  188. // Tab \u0009
  189. browser.expect.element(`#output-text .cm-line:nth-of-type(1)`).to.have.property("textContent").match(/\u0009$/);
  190. // Line feed \u000a
  191. browser.expect.element(`#output-text .cm-line:nth-of-type(1)`).to.have.property("textContent").match(/^.{10}$/);
  192. browser.expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("2");
  193. // Second line
  194. for (let i = 0x0b; i < SPECIAL_CHARS.length; i++) {
  195. const index = SPECIAL_CHARS.charCodeAt(i);
  196. const name = CONTROL_CHAR_NAMES[index] || "0x" + index.toString(16);
  197. const value = index >= 32 ? "\u2022" : String.fromCharCode(0x2400 + index);
  198. browser.expect.element(`#output-text .cm-content .cm-line:nth-of-type(2) .cm-specialChar:nth-of-type(${i-10})`)
  199. .to.have.property("title").equals(`Control character ${name}`);
  200. browser.expect.element(`#output-text .cm-content .cm-line:nth-of-type(2) .cm-specialChar:nth-of-type(${i-10})`)
  201. .text.to.equal(value);
  202. }
  203. /* Bytes are rendered correctly */
  204. utils.setInput(browser, ALL_BYTES, false);
  205. // Expect length to be 255, since one character is creating a newline
  206. browser.expect.element(`#input-text .cm-content`).to.have.property("textContent").match(/^.{255}$/);
  207. browser.expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("256");
  208. browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("2");
  209. /* PUA \ue000-\uf8ff */
  210. utils.setInput(browser, PUA_CHARS, false);
  211. utils.setChrEnc(browser, "output", "UTF-8");
  212. utils.bake(browser);
  213. // Confirm input and output as expected
  214. /* In order to render whitespace characters as control character pictures in the output, even
  215. when they are the designated line separator, CyberChef sometimes chooses to represent them
  216. internally using the Unicode Private Use Area (https://en.wikipedia.org/wiki/Private_Use_Areas).
  217. See `Utils.escapeWhitespace()` for an example of this.
  218. Therefore, PUA characters should be rendered normally in the Input but as control character
  219. pictures in the output.
  220. */
  221. browser.expect.element(`#input-text .cm-content`).to.have.property("textContent").match(/^\ue000\ue001\uf8fe\uf8ff$/);
  222. browser.expect.element(`#output-text .cm-content`).to.have.property("textContent").match(/^\u2400\u2401\u3cfe\u3cff$/);
  223. /* Can be copied */
  224. utils.setInput(browser, SPECIAL_CHARS, false);
  225. utils.setChrEnc(browser, "output", "UTF-8");
  226. utils.bake(browser);
  227. // Manual copy
  228. browser
  229. .doubleClick("#output-text .cm-content .cm-line:nth-of-type(1) .cm-specialChar:nth-of-type(1)")
  230. .waitForElementVisible("#output-text .cm-selectionBackground");
  231. utils.copy(browser);
  232. utils.paste(browser, "#search"); // Paste into search box as this won't mess with the values
  233. // Ensure that the values are as expected
  234. browser.expect.element("#search").to.have.value.that.equals("\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008");
  235. browser.clearValue("#search");
  236. // Raw copy
  237. browser
  238. .click("#copy-output")
  239. .pause(100);
  240. utils.paste(browser, "#search"); // Paste into search box as this won't mess with the values
  241. // Ensure that the values are as expected
  242. browser.expect.element("#search").to.have.value.that.matches(/^\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009/);
  243. browser.clearValue("#search");
  244. },
  245. "HTML output": browser => {
  246. /* Displays correctly */
  247. utils.loadRecipe(browser, "Entropy", ALL_BYTES);
  248. utils.bake(browser);
  249. browser
  250. .waitForElementVisible("#output-html")
  251. .waitForElementVisible("#output-html #chart-area");
  252. /* Status bar widgets are disabled */
  253. browser.expect.element("#output-text .cm-status-bar .disabled .stats-length-value").to.be.visible;
  254. browser.expect.element("#output-text .cm-status-bar .disabled .stats-lines-value").to.be.visible;
  255. browser.expect.element("#output-text .cm-status-bar .disabled .chr-enc-value").to.be.visible;
  256. browser.expect.element("#output-text .cm-status-bar .disabled .eol-value").to.be.visible;
  257. /* Displays special chars correctly */
  258. utils.loadRecipe(browser, "To Table", ",\u0000\u0001\u0002\u0003\u0004", [",", "\\r\\n", false, "HTML"]);
  259. utils.bake(browser);
  260. for (let i = 0x0; i <= 0x4; i++) {
  261. browser.expect.element(`#output-html .cm-specialChar:nth-of-type(${i+1})`)
  262. .to.have.property("title").equals(`Control character ${CONTROL_CHAR_NAMES[i] || "0x" + i.toString(16)}`);
  263. browser.expect.element(`#output-html .cm-specialChar:nth-of-type(${i+1})`)
  264. .text.to.equal(String.fromCharCode(0x2400 + i));
  265. }
  266. /* Can be copied */
  267. // Raw copy
  268. browser
  269. .click("#copy-output")
  270. .pause(100);
  271. utils.paste(browser, "#search"); // Paste into search box as this won't mess with the values
  272. // Ensure that the values are as expected
  273. browser.expect.element("#search").to.have.value.that.matches(/\u0000\u0001\u0002\u0003\u0004/);
  274. browser.clearValue("#search");
  275. },
  276. "Highlighting": browser => {
  277. utils.setInput(browser, SELECTABLE_STRING);
  278. utils.bake(browser);
  279. /* Selecting input text also selects other instances in input and output */
  280. browser // Input
  281. .click("#auto-bake-label")
  282. .doubleClick("#input-text .cm-content .cm-line:nth-of-type(1)")
  283. .waitForElementVisible("#input-text .cm-selectionLayer .cm-selectionBackground")
  284. .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(1) .cm-selectionMatch")
  285. .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(2) .cm-selectionMatch")
  286. .waitForElementVisible("#input-text .cm-content .cm-line:nth-of-type(3) .cm-selectionMatch")
  287. .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(4) .cm-selectionMatch")
  288. .waitForElementVisible("#input-text .cm-content .cm-line:nth-of-type(5) .cm-selectionMatch")
  289. .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(6) .cm-selectionMatch")
  290. .waitForElementVisible("#input-text .cm-content .cm-line:nth-of-type(7) .cm-selectionMatch");
  291. browser // Output
  292. .waitForElementVisible("#output-text .cm-selectionLayer .cm-selectionBackground")
  293. .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(1) .cm-selectionMatch")
  294. .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(2) .cm-selectionMatch")
  295. .waitForElementVisible("#output-text .cm-content .cm-line:nth-of-type(3) .cm-selectionMatch")
  296. .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(4) .cm-selectionMatch")
  297. .waitForElementVisible("#output-text .cm-content .cm-line:nth-of-type(5) .cm-selectionMatch")
  298. .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(6) .cm-selectionMatch")
  299. .waitForElementVisible("#output-text .cm-content .cm-line:nth-of-type(7) .cm-selectionMatch");
  300. /* Selecting output text highlights in input */
  301. browser // Output
  302. .click("#output-text")
  303. .waitForElementNotPresent("#input-text .cm-selectionLayer .cm-selectionBackground")
  304. .waitForElementNotPresent("#output-text .cm-selectionLayer .cm-selectionBackground")
  305. .waitForElementNotPresent("#input-text .cm-content .cm-line .cm-selectionMatch")
  306. .waitForElementNotPresent("#output-text .cm-content .cm-line .cm-selectionMatch")
  307. .doubleClick("#output-text .cm-content .cm-line:nth-of-type(7)")
  308. .waitForElementVisible("#output-text .cm-selectionLayer .cm-selectionBackground")
  309. .waitForElementVisible("#output-text .cm-content .cm-line:nth-of-type(1) .cm-selectionMatch")
  310. .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(2) .cm-selectionMatch")
  311. .waitForElementVisible("#output-text .cm-content .cm-line:nth-of-type(3) .cm-selectionMatch")
  312. .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(4) .cm-selectionMatch")
  313. .waitForElementVisible("#output-text .cm-content .cm-line:nth-of-type(5) .cm-selectionMatch")
  314. .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(6) .cm-selectionMatch")
  315. .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(7) .cm-selectionMatch");
  316. browser // Input
  317. .waitForElementVisible("#input-text .cm-selectionLayer .cm-selectionBackground")
  318. .waitForElementVisible("#input-text .cm-content .cm-line:nth-of-type(1) .cm-selectionMatch")
  319. .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(2) .cm-selectionMatch")
  320. .waitForElementVisible("#input-text .cm-content .cm-line:nth-of-type(3) .cm-selectionMatch")
  321. .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(4) .cm-selectionMatch")
  322. .waitForElementVisible("#input-text .cm-content .cm-line:nth-of-type(5) .cm-selectionMatch")
  323. .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(6) .cm-selectionMatch")
  324. .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(7) .cm-selectionMatch");
  325. // Turn autobake off again
  326. browser.click("#auto-bake-label");
  327. },
  328. "Character encoding": browser => {
  329. const CHINESE_CHARS = "不要恐慌。";
  330. /* Dropup works */
  331. /* Selecting changes output correctly */
  332. utils.setInput(browser, CHINESE_CHARS, false);
  333. utils.setChrEnc(browser, "input", "UTF-8");
  334. utils.bake(browser);
  335. utils.expectOutput(browser, "\u00E4\u00B8\u008D\u00E8\u00A6\u0081\u00E6\u0081\u0090\u00E6\u0085\u008C\u00E3\u0080\u0082");
  336. /* Changing output to match input works as expected */
  337. utils.setChrEnc(browser, "output", "UTF-8");
  338. utils.bake(browser);
  339. utils.expectOutput(browser, CHINESE_CHARS);
  340. /* Encodings appear in the URL */
  341. browser.assert.urlContains("ienc=65001");
  342. browser.assert.urlContains("oenc=65001");
  343. /* Preserved when changing tabs */
  344. browser
  345. .click("#btn-new-tab")
  346. .waitForElementVisible("#input-tabs li:nth-of-type(2).active-input-tab");
  347. browser.expect.element("#input-text .chr-enc-value").text.that.equals("Raw Bytes");
  348. browser.expect.element("#output-text .chr-enc-value").text.that.equals("Raw Bytes");
  349. utils.setChrEnc(browser, "input", "UTF-7");
  350. utils.setChrEnc(browser, "output", "UTF-7");
  351. browser
  352. .click("#input-tabs li:nth-of-type(1)")
  353. .waitForElementVisible("#input-tabs li:nth-of-type(1).active-input-tab");
  354. browser.expect.element("#input-text .chr-enc-value").text.that.equals("UTF-8");
  355. browser.expect.element("#output-text .chr-enc-value").text.that.equals("UTF-8");
  356. /* Try various encodings */
  357. // These are not meant to be realistic encodings for this data
  358. utils.setInput(browser, CHINESE_CHARS, false);
  359. utils.setChrEnc(browser, "input", "UTF-8");
  360. utils.setChrEnc(browser, "output", "UTF-16LE");
  361. utils.bake(browser);
  362. utils.expectOutput(browser, "\uB8E4\uE88D\u81A6\u81E6\uE690\u8C85\u80E3");
  363. utils.setChrEnc(browser, "output", "Simplified Chinese GBK");
  364. utils.bake(browser);
  365. utils.expectOutput(browser, "\u6D93\u5D88\uFDFF\u93AD\u612D\u53A1\u9286\u0000");
  366. utils.setChrEnc(browser, "input", "UTF-7");
  367. utils.bake(browser);
  368. utils.expectOutput(browser, "+Tg0-+iYE-+YFA-+YUw-");
  369. utils.setChrEnc(browser, "input", "Traditional Chinese Big5");
  370. utils.bake(browser);
  371. utils.expectOutput(browser, "\u3043\u74B6\uFDFF\u7A3A\uFDFF");
  372. utils.setChrEnc(browser, "output", "Windows-1251 Cyrillic");
  373. utils.bake(browser);
  374. utils.expectOutput(browser, "\u00A4\u0408\u00ADn\u00AE\u0408\u00B7W\u040EC");
  375. },
  376. "Line endings": browser => {
  377. /* Dropup works */
  378. /* Selecting changes view in input */
  379. utils.setInput(browser, MULTI_LINE_STRING);
  380. // Line endings: LF
  381. // Input
  382. browser
  383. .waitForElementPresent("#input-text .cm-content .cm-line:nth-of-type(3)")
  384. .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(4)")
  385. .waitForElementNotPresent("#input-text .cm-content .cm-specialChar");
  386. browser.expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("301");
  387. browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("3");
  388. // Output
  389. utils.bake(browser);
  390. browser
  391. .waitForElementPresent("#output-text .cm-content .cm-line:nth-of-type(3)")
  392. .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(4)")
  393. .waitForElementNotPresent("#output-text .cm-content .cm-specialChar");
  394. browser.expect.element("#output-text .cm-status-bar .stats-length-value").text.to.equal("301");
  395. browser.expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("3");
  396. // Input EOL: VT
  397. utils.setEOLSeq(browser, "input", "VT");
  398. // Input
  399. browser
  400. .waitForElementPresent("#input-text .cm-content .cm-line:nth-of-type(1)")
  401. .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(2)")
  402. .waitForElementPresent("#input-text .cm-content .cm-specialChar");
  403. browser.expect.element("#input-text .cm-content .cm-specialChar").text.to.equal("␊");
  404. browser.expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("301");
  405. browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("1");
  406. // Output
  407. utils.bake(browser);
  408. browser
  409. .waitForElementPresent("#output-text .cm-content .cm-line:nth-of-type(3)")
  410. .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(4)")
  411. .waitForElementNotPresent("#output-text .cm-content .cm-specialChar");
  412. browser.expect.element("#output-text .cm-status-bar .stats-length-value").text.to.equal("301");
  413. browser.expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("3");
  414. // Output EOL: VT
  415. utils.setEOLSeq(browser, "output", "VT");
  416. // Input
  417. browser
  418. .waitForElementPresent("#input-text .cm-content .cm-line:nth-of-type(1)")
  419. .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(2)")
  420. .waitForElementPresent("#input-text .cm-content .cm-specialChar");
  421. browser.expect.element("#input-text .cm-content .cm-specialChar").text.to.equal("␊");
  422. browser.expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("301");
  423. browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("1");
  424. // Output
  425. browser
  426. .waitForElementPresent("#output-text .cm-content .cm-line:nth-of-type(1)")
  427. .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(2)")
  428. .waitForElementPresent("#output-text .cm-content .cm-specialChar");
  429. browser.expect.element("#output-text .cm-content .cm-specialChar").text.to.equal("␊");
  430. browser.expect.element("#output-text .cm-status-bar .stats-length-value").text.to.equal("301");
  431. browser.expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("1");
  432. /* Adding new line ending changes output correctly */
  433. browser.sendKeys("#input-text .cm-content", browser.Keys.RETURN);
  434. // Input
  435. browser
  436. .waitForElementPresent("#input-text .cm-content .cm-line:nth-of-type(2)")
  437. .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(3)");
  438. browser.expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("302");
  439. browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("2");
  440. // Output
  441. utils.bake(browser);
  442. browser
  443. .waitForElementPresent("#output-text .cm-content .cm-line:nth-of-type(2)")
  444. .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(3)");
  445. browser.expect.element("#output-text .cm-status-bar .stats-length-value").text.to.equal("302");
  446. browser.expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("2");
  447. // Input EOL: CRLF
  448. utils.setEOLSeq(browser, "input", "CRLF");
  449. // Output EOL: CR
  450. utils.setEOLSeq(browser, "output", "CR");
  451. browser.sendKeys("#input-text .cm-content", browser.Keys.RETURN);
  452. // Input
  453. browser
  454. .waitForElementPresent("#input-text .cm-content .cm-line:nth-of-type(2)")
  455. .waitForElementNotPresent("#input-text .cm-content .cm-line:nth-of-type(3)")
  456. .waitForElementPresent("#input-text .cm-content .cm-line:nth-of-type(1) .cm-specialChar:nth-of-type(3)");
  457. browser.expect.element("#input-text .cm-content .cm-line:nth-of-type(1) .cm-specialChar:nth-of-type(3)").text.to.equal("␋");
  458. browser.expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("304");
  459. browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("2");
  460. // Output
  461. utils.bake(browser);
  462. browser
  463. .waitForElementPresent("#output-text .cm-content .cm-line:nth-of-type(2)")
  464. .waitForElementNotPresent("#output-text .cm-content .cm-line:nth-of-type(3)")
  465. .waitForElementPresent("#output-text .cm-content .cm-line:nth-of-type(2) .cm-specialChar");
  466. browser.expect.element("#output-text .cm-content .cm-line:nth-of-type(2) .cm-specialChar").text.to.equal("␊");
  467. browser.expect.element("#output-text .cm-status-bar .stats-length-value").text.to.equal("304");
  468. browser.expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("2");
  469. /* Line endings appear in the URL */
  470. browser.assert.urlContains("ieol=%0D%0A");
  471. browser.assert.urlContains("oeol=%0D");
  472. /* Preserved when changing tabs */
  473. browser
  474. .click("#btn-new-tab")
  475. .waitForElementVisible("#input-tabs li:nth-of-type(2).active-input-tab");
  476. browser.expect.element("#input-text .eol-value").text.that.equals("LF");
  477. browser.expect.element("#output-text .eol-value").text.that.equals("LF");
  478. utils.setEOLSeq(browser, "input", "FF");
  479. utils.setEOLSeq(browser, "output", "LS");
  480. browser
  481. .click("#input-tabs li:nth-of-type(1)")
  482. .waitForElementVisible("#input-tabs li:nth-of-type(1).active-input-tab");
  483. browser.expect.element("#input-text .eol-value").text.that.equals("CRLF");
  484. browser.expect.element("#output-text .eol-value").text.that.equals("CR");
  485. },
  486. "File inputs": browser => {
  487. utils.clear(browser);
  488. /* Side panel displays correct info */
  489. utils.uploadFile(browser, "files/TowelDay.jpeg");
  490. browser
  491. .waitForElementVisible("#input-text .cm-file-details")
  492. .waitForElementVisible("#input-text .cm-file-details .file-details-toggle-shown")
  493. .waitForElementVisible("#input-text .cm-file-details .file-details-thumbnail")
  494. .waitForElementVisible("#input-text .cm-file-details .file-details-name")
  495. .waitForElementVisible("#input-text .cm-file-details .file-details-size")
  496. .waitForElementVisible("#input-text .cm-file-details .file-details-type")
  497. .waitForElementVisible("#input-text .cm-file-details .file-details-loaded");
  498. browser.expect.element("#input-text .cm-file-details .file-details-name").text.that.equals("TowelDay.jpeg");
  499. browser.expect.element("#input-text .cm-file-details .file-details-size").text.that.equals("61,379 bytes");
  500. browser.expect.element("#input-text .cm-file-details .file-details-type").text.that.equals("image/jpeg");
  501. browser.expect.element("#input-text .cm-file-details .file-details-loaded").text.that.equals("100%");
  502. /* Side panel can be hidden */
  503. browser
  504. .click("#input-text .cm-file-details .file-details-toggle-shown")
  505. .waitForElementNotPresent("#input-text .cm-file-details .file-details-toggle-shown")
  506. .waitForElementVisible("#input-text .cm-file-details .file-details-toggle-hidden")
  507. .expect.element("#input-text .cm-file-details").to.have.css("width").which.equals("1px");
  508. browser
  509. .click("#input-text .cm-file-details .file-details-toggle-hidden")
  510. .waitForElementNotPresent("#input-text .cm-file-details .file-details-toggle-hidden")
  511. .waitForElementVisible("#input-text .cm-file-details .file-details-toggle-shown")
  512. .expect.element("#input-text .cm-file-details").to.have.css("width").which.equals("200px");
  513. },
  514. "Folder inputs": browser => {
  515. utils.clear(browser);
  516. /* Side panel displays correct info */
  517. utils.uploadFolder(browser, "files");
  518. // Tab 1
  519. browser
  520. .click("#input-tabs li:nth-of-type(1)")
  521. .waitForElementVisible("#input-tabs li:nth-of-type(1).active-input-tab");
  522. browser
  523. .waitForElementVisible("#input-text .cm-file-details")
  524. .waitForElementVisible("#input-text .cm-file-details .file-details-toggle-shown")
  525. .waitForElementVisible("#input-text .cm-file-details .file-details-thumbnail")
  526. .waitForElementVisible("#input-text .cm-file-details .file-details-name")
  527. .waitForElementVisible("#input-text .cm-file-details .file-details-size")
  528. .waitForElementVisible("#input-text .cm-file-details .file-details-type")
  529. .waitForElementVisible("#input-text .cm-file-details .file-details-loaded");
  530. browser.expect.element("#input-text .cm-file-details .file-details-name").text.that.equals("TowelDay.jpeg");
  531. browser.expect.element("#input-text .cm-file-details .file-details-size").text.that.equals("61,379 bytes");
  532. browser.expect.element("#input-text .cm-file-details .file-details-type").text.that.equals("image/jpeg");
  533. browser.expect.element("#input-text .cm-file-details .file-details-loaded").text.that.equals("100%");
  534. // Tab 2
  535. browser
  536. .click("#input-tabs li:nth-of-type(2)")
  537. .waitForElementVisible("#input-tabs li:nth-of-type(2).active-input-tab");
  538. browser
  539. .waitForElementVisible("#input-text .cm-file-details")
  540. .waitForElementVisible("#input-text .cm-file-details .file-details-toggle-shown")
  541. .waitForElementVisible("#input-text .cm-file-details .file-details-thumbnail")
  542. .waitForElementVisible("#input-text .cm-file-details .file-details-name")
  543. .waitForElementVisible("#input-text .cm-file-details .file-details-size")
  544. .waitForElementVisible("#input-text .cm-file-details .file-details-type")
  545. .waitForElementVisible("#input-text .cm-file-details .file-details-loaded");
  546. browser.expect.element("#input-text .cm-file-details .file-details-name").text.that.equals("Hitchhikers_Guide.jpeg");
  547. browser.expect.element("#input-text .cm-file-details .file-details-size").text.that.equals("36,595 bytes");
  548. browser.expect.element("#input-text .cm-file-details .file-details-type").text.that.equals("image/jpeg");
  549. browser.expect.element("#input-text .cm-file-details .file-details-loaded").text.that.equals("100%");
  550. },
  551. "Loading from URL": browser => {
  552. /* Complex deep link populates the input correctly (encoding, eol, input) */
  553. browser
  554. .urlHash("recipe=To_Base64('A-Za-z0-9%2B/%3D')&input=VGhlIHNoaXBzIGh1bmcgaW4gdGhlIHNreSBpbiBtdWNoIHRoZSBzYW1lIHdheSB0aGF0IGJyaWNrcyBkb24ndC4M&ienc=21866&oenc=1201&ieol=%0C&oeol=%E2%80%A9")
  555. .waitForElementVisible("#rec-list li.operation");
  556. browser.expect.element(`#input-text .cm-content`).to.have.property("textContent").match(/^.{65}$/);
  557. browser.expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("66");
  558. browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("2");
  559. browser.expect.element("#input-text .chr-enc-value").text.that.equals("KOI8-U Ukrainian Cyrillic");
  560. browser.expect.element("#output-text .chr-enc-value").text.that.equals("UTF-16BE");
  561. browser.expect.element("#input-text .eol-value").text.that.equals("FF");
  562. browser.expect.element("#output-text .eol-value").text.that.equals("PS");
  563. utils.bake(browser);
  564. browser.expect.element(`#output-text .cm-content`).to.have.property("textContent").match(/^.{44}$/);
  565. browser.expect.element("#output-text .cm-status-bar .stats-length-value").text.to.equal("44");
  566. browser.expect.element("#output-text .cm-status-bar .stats-lines-value").text.to.equal("1");
  567. },
  568. "Replace input with output": browser => {
  569. /* Input is correctly populated */
  570. utils.loadRecipe(browser, "XOR", "The ships hung in the sky in much the same way that bricks don't.", [{ "option": "Hex", "string": "65" }, "Standard", false]);
  571. utils.setChrEnc(browser, "input", "UTF-32LE");
  572. utils.setChrEnc(browser, "output", "UTF-7");
  573. utils.setEOLSeq(browser, "input", "CRLF");
  574. utils.setEOLSeq(browser, "output", "LS");
  575. browser
  576. .sendKeys("#input-text .cm-content", browser.Keys.RETURN)
  577. .expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("2");
  578. utils.bake(browser);
  579. browser.expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("67");
  580. browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("2");
  581. browser.expect.element("#input-text .chr-enc-value").text.that.equals("UTF-32LE");
  582. browser.expect.element("#input-text .eol-value").text.that.equals("CRLF");
  583. browser.expect.element("#output-text .cm-status-bar .stats-length-value").text.to.equal("268");
  584. browser
  585. .click("#switch")
  586. .waitForElementVisible("#stale-indicator");
  587. browser.expect.element("#input-text .cm-status-bar .stats-length-value").text.to.equal("268");
  588. /* Special characters, encodings and line endings all as expected */
  589. browser.expect.element("#input-text .cm-status-bar .stats-lines-value").text.to.equal("1");
  590. browser.expect.element("#input-text .chr-enc-value").text.that.equals("UTF-7");
  591. browser.expect.element("#input-text .eol-value").text.that.equals("LS");
  592. browser.expect.element("#input-text .cm-line:nth-of-type(1) .cm-specialChar:nth-of-type(1)").text.to.equal("␍");
  593. browser.expect.element("#input-text .cm-line:nth-of-type(1) .cm-specialChar:nth-of-type(49)").text.to.equal("␑");
  594. browser.waitForElementNotPresent("#input-text .cm-line:nth-of-type(1) .cm-specialChar:nth-of-type(50)");
  595. },
  596. after: browser => {
  597. browser.end();
  598. }
  599. };