SwiftyMarkdownCharacterTests.swift 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
  1. //
  2. // SwiftyMarkdownCharacterTests.swift
  3. // SwiftyMarkdownTests
  4. //
  5. // Created by Simon Fairbairn on 17/12/2019.
  6. // Copyright © 2019 Voyage Travel Apps. All rights reserved.
  7. //
  8. @testable import SwiftyMarkdown
  9. import XCTest
  10. class SwiftyMarkdownStylingTests: SwiftyMarkdownCharacterTests {
  11. func testIsolatedCase() {
  12. challenge = TokenTest(input: "a ```b`", output: "a ```b`", tokens : [
  13. Token(type: .string, inputString: "a ```b`", characterStyles: [])
  14. ])
  15. results = self.attempt(challenge, rules: [.backticks])
  16. if results.stringTokens.count == challenge.tokens.count {
  17. for (idx, token) in results.stringTokens.enumerated() {
  18. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  19. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  20. }
  21. } else {
  22. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  23. }
  24. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  25. XCTAssertEqual(results.attributedString.string, challenge.output)
  26. return
  27. challenge = TokenTest(input: """
  28. An asterisk: *
  29. Line break
  30. """, output: """
  31. An asterisk: *
  32. Line break
  33. """, tokens: [
  34. Token(type: .string, inputString: "An asterisk: *", characterStyles: []),
  35. Token(type: .string, inputString: "Line break", characterStyles: [])
  36. ])
  37. results = self.attempt(challenge)
  38. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count )
  39. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  40. XCTAssertEqual(results.attributedString.string, challenge.output)
  41. return
  42. challenge = TokenTest(input: "A [referenced link][link]\n[link]: https://www.neverendingvoyage.com/", output: "A referenced link", tokens: [
  43. Token(type: .string, inputString: "A ", characterStyles: []),
  44. Token(type: .string, inputString: "referenced link", characterStyles: [CharacterStyle.link])
  45. ])
  46. results = self.attempt(challenge)
  47. if results.stringTokens.count == challenge.tokens.count {
  48. for (idx, token) in results.stringTokens.enumerated() {
  49. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  50. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  51. }
  52. } else {
  53. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  54. }
  55. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  56. XCTAssertEqual(results.attributedString.string, challenge.output)
  57. if results.links.count == 1 {
  58. XCTAssertEqual(results.links[0].metadataStrings.first, "https://www.neverendingvoyage.com/")
  59. } else {
  60. XCTFail("Incorrect link count. Expecting 1, found \(results.links.count)")
  61. }
  62. challenge = TokenTest(input: "A [referenced link][link]\n[notLink]: https://www.neverendingvoyage.com/", output: "A [referenced link][link]", tokens: [
  63. Token(type: .string, inputString: "A [referenced link][link]", characterStyles: [])
  64. ])
  65. results = self.attempt(challenge, rules: [.links, .images, .referencedLinks])
  66. XCTAssertEqual(results.attributedString.string, challenge.output)
  67. XCTAssertEqual(results.links.count, 0)
  68. }
  69. func testThatBoldTraitsAreRecognised() {
  70. challenge = TokenTest(input: "**A bold string**", output: "A bold string", tokens: [
  71. Token(type: .string, inputString: "A bold string", characterStyles: [CharacterStyle.bold])
  72. ])
  73. results = self.attempt(challenge)
  74. if results.stringTokens.count == challenge.tokens.count {
  75. for (idx, token) in results.stringTokens.enumerated() {
  76. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  77. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  78. }
  79. } else {
  80. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  81. }
  82. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  83. XCTAssertEqual(results.attributedString.string, challenge.output)
  84. challenge = TokenTest(input: "A string with a **bold** word", output: "A string with a bold word", tokens: [
  85. Token(type: .string, inputString: "A string with a ", characterStyles: []),
  86. Token(type: .string, inputString: "bold", characterStyles: [CharacterStyle.bold]),
  87. Token(type: .string, inputString: " word", characterStyles: [])
  88. ])
  89. results = self.attempt(challenge)
  90. if results.stringTokens.count == challenge.tokens.count {
  91. for (idx, token) in results.stringTokens.enumerated() {
  92. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  93. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  94. }
  95. } else {
  96. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  97. }
  98. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  99. XCTAssertEqual(results.attributedString.string, challenge.output)
  100. challenge = TokenTest(input: "\\*\\*A normal string\\*\\*", output: "**A normal string**", tokens: [
  101. Token(type: .string, inputString: "**A normal string**", characterStyles: [])
  102. ])
  103. results = self.attempt(challenge)
  104. if results.stringTokens.count == challenge.tokens.count {
  105. for (idx, token) in results.stringTokens.enumerated() {
  106. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  107. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  108. }
  109. } else {
  110. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  111. }
  112. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  113. XCTAssertEqual(results.attributedString.string, challenge.output)
  114. challenge = TokenTest(input: "\\\\*\\*A normal \\\\ string\\*\\*", output: "\\**A normal \\\\ string**", tokens: [
  115. Token(type: .string, inputString: "\\**A normal \\\\ string**", characterStyles: [])
  116. ])
  117. results = self.attempt(challenge)
  118. if results.stringTokens.count == challenge.tokens.count {
  119. for (idx, token) in results.stringTokens.enumerated() {
  120. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  121. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  122. }
  123. } else {
  124. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  125. }
  126. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  127. XCTAssertEqual(results.attributedString.string, challenge.output)
  128. challenge = TokenTest(input: "A string with double \\*\\*escaped\\*\\* asterisks", output: "A string with double **escaped** asterisks", tokens: [
  129. Token(type: .string, inputString: "A string with double **escaped** asterisks", characterStyles: [])
  130. ])
  131. results = self.attempt(challenge)
  132. if results.stringTokens.count == challenge.tokens.count {
  133. for (idx, token) in results.stringTokens.enumerated() {
  134. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  135. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  136. }
  137. } else {
  138. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  139. }
  140. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  141. XCTAssertEqual(results.attributedString.string, challenge.output)
  142. challenge = TokenTest(input: "\\**One escaped, one not at either end\\**", output: "*One escaped, one not at either end*", tokens: [
  143. Token(type: .string, inputString: "*", characterStyles: []),
  144. Token(type: .string, inputString: "One escaped, one not at either end*", characterStyles: [CharacterStyle.italic]),
  145. ])
  146. results = self.attempt(challenge)
  147. if results.stringTokens.count == challenge.tokens.count {
  148. for (idx, token) in results.stringTokens.enumerated() {
  149. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  150. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  151. }
  152. } else {
  153. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  154. }
  155. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  156. XCTAssertEqual(results.attributedString.string, challenge.output)
  157. challenge = TokenTest(input: "A string with one \\**escaped\\** asterisk, one not at either end", output: "A string with one *escaped* asterisk, one not at either end", tokens: [
  158. Token(type: .string, inputString: "A string with one *", characterStyles: []),
  159. Token(type: .string, inputString: "escaped*", characterStyles: [CharacterStyle.italic]),
  160. Token(type: .string, inputString: " asterisk, one not at either end", characterStyles: [])
  161. ])
  162. results = self.attempt(challenge)
  163. if results.stringTokens.count == challenge.tokens.count {
  164. for (idx, token) in results.stringTokens.enumerated() {
  165. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  166. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  167. }
  168. } else {
  169. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  170. }
  171. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  172. XCTAssertEqual(results.attributedString.string, challenge.output)
  173. }
  174. func testThatCodeTraitsAreRecognised() {
  175. challenge = TokenTest(input: "`Code (**should** not process internal tags)`", output: "Code (**should** not process internal tags)", tokens: [
  176. Token(type: .string, inputString: "Code (**should** not process internal tags)", characterStyles: [CharacterStyle.code])
  177. ])
  178. results = self.attempt(challenge)
  179. if results.stringTokens.count == challenge.tokens.count {
  180. for (idx, token) in results.stringTokens.enumerated() {
  181. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  182. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  183. }
  184. } else {
  185. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  186. }
  187. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  188. XCTAssertEqual(results.attributedString.string, challenge.output)
  189. challenge = TokenTest(input: "A string with `code` (should not be indented)", output: "A string with code (should not be indented)", tokens : [
  190. Token(type: .string, inputString: "A string with ", characterStyles: []),
  191. Token(type: .string, inputString: "code", characterStyles: [CharacterStyle.code]),
  192. Token(type: .string, inputString: " (should not be indented)", characterStyles: [])
  193. ])
  194. results = self.attempt(challenge)
  195. if results.stringTokens.count == challenge.tokens.count {
  196. for (idx, token) in results.stringTokens.enumerated() {
  197. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  198. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  199. }
  200. } else {
  201. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  202. }
  203. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  204. XCTAssertEqual(results.attributedString.string, challenge.output)
  205. challenge = TokenTest(input: "`A code string` with multiple `code` `instances`", output: "A code string with multiple code instances", tokens : [
  206. Token(type: .string, inputString: "A code string", characterStyles: [CharacterStyle.code]),
  207. Token(type: .string, inputString: " with multiple ", characterStyles: []),
  208. Token(type: .string, inputString: "code", characterStyles: [CharacterStyle.code]),
  209. Token(type: .string, inputString: " ", characterStyles: []),
  210. Token(type: .string, inputString: "instances", characterStyles: [CharacterStyle.code])
  211. ])
  212. results = self.attempt(challenge)
  213. if results.stringTokens.count == challenge.tokens.count {
  214. for (idx, token) in results.stringTokens.enumerated() {
  215. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  216. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  217. }
  218. } else {
  219. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  220. }
  221. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  222. XCTAssertEqual(results.attributedString.string, challenge.output)
  223. challenge = TokenTest(input: "\\`A normal string\\`", output: "`A normal string`", tokens: [
  224. Token(type: .string, inputString: "`A normal string`", characterStyles: [])
  225. ])
  226. results = self.attempt(challenge)
  227. if results.stringTokens.count == challenge.tokens.count {
  228. for (idx, token) in results.stringTokens.enumerated() {
  229. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  230. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  231. }
  232. } else {
  233. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  234. }
  235. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  236. XCTAssertEqual(results.attributedString.string, challenge.output)
  237. challenge = TokenTest(input: "A string with \\`escaped\\` backticks", output: "A string with `escaped` backticks", tokens: [
  238. Token(type: .string, inputString: "A string with `escaped` backticks", characterStyles: [])
  239. ])
  240. results = self.attempt(challenge)
  241. if results.stringTokens.count == challenge.tokens.count {
  242. for (idx, token) in results.stringTokens.enumerated() {
  243. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  244. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  245. }
  246. } else {
  247. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  248. }
  249. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  250. XCTAssertEqual(results.attributedString.string, challenge.output)
  251. challenge = TokenTest(input: "A lonely backtick: `", output: "A lonely backtick: `", tokens: [
  252. Token(type: .string, inputString: "A lonely backtick: `", characterStyles: [])
  253. ])
  254. results = self.attempt(challenge)
  255. if results.stringTokens.count == challenge.tokens.count {
  256. for (idx, token) in results.stringTokens.enumerated() {
  257. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  258. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  259. }
  260. } else {
  261. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  262. }
  263. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  264. XCTAssertEqual(results.attributedString.string, challenge.output)
  265. challenge = TokenTest(input: "Two backticks followed by a full stop ``.", output: "Two backticks followed by a full stop ``.", tokens: [
  266. Token(type: .string, inputString: "Two backticks followed by a full stop ``.", characterStyles: [])
  267. ])
  268. results = self.attempt(challenge)
  269. if results.stringTokens.count == challenge.tokens.count {
  270. for (idx, token) in results.stringTokens.enumerated() {
  271. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  272. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  273. }
  274. } else {
  275. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  276. }
  277. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  278. XCTAssertEqual(results.attributedString.string, challenge.output)
  279. }
  280. func testThatItalicTraitsAreParsedCorrectly() {
  281. challenge = TokenTest(input: "*An italicised string*", output: "An italicised string", tokens : [
  282. Token(type: .string, inputString: "An italicised string", characterStyles: [CharacterStyle.italic])
  283. ])
  284. results = self.attempt(challenge)
  285. if results.stringTokens.count == challenge.tokens.count {
  286. for (idx, token) in results.stringTokens.enumerated() {
  287. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  288. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  289. }
  290. } else {
  291. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  292. }
  293. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  294. XCTAssertEqual(results.attributedString.string, challenge.output)
  295. challenge = TokenTest(input: "A string with *italicised* text", output: "A string with italicised text", tokens : [
  296. Token(type: .string, inputString: "A string with ", characterStyles: []),
  297. Token(type: .string, inputString: "italicised", characterStyles: [CharacterStyle.italic]),
  298. Token(type: .string, inputString: " text", characterStyles: [])
  299. ])
  300. results = self.attempt(challenge)
  301. if results.stringTokens.count == challenge.tokens.count {
  302. for (idx, token) in results.stringTokens.enumerated() {
  303. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  304. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  305. }
  306. } else {
  307. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  308. }
  309. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  310. XCTAssertEqual(results.attributedString.string, challenge.output)
  311. challenge = TokenTest(input: "_An italic string_ with a *mix* _of_ italic *styles*", output: "An italic string with a mix of italic styles", tokens : [
  312. Token(type: .string, inputString: "An italic string", characterStyles: [CharacterStyle.italic]),
  313. Token(type: .string, inputString: " with a ", characterStyles: []),
  314. Token(type: .string, inputString: "mix", characterStyles: [CharacterStyle.italic]),
  315. Token(type: .string, inputString: " ", characterStyles: []),
  316. Token(type: .string, inputString: "of", characterStyles: [CharacterStyle.italic]),
  317. Token(type: .string, inputString: " italic ", characterStyles: []),
  318. Token(type: .string, inputString: "styles", characterStyles: [CharacterStyle.italic])
  319. ])
  320. results = self.attempt(challenge)
  321. if results.stringTokens.count == challenge.tokens.count {
  322. for (idx, token) in results.stringTokens.enumerated() {
  323. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  324. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  325. }
  326. } else {
  327. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  328. }
  329. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  330. XCTAssertEqual(results.attributedString.string, challenge.output)
  331. challenge = TokenTest(input: "\\_A normal string\\_", output: "_A normal string_", tokens: [
  332. Token(type: .string, inputString: "_A normal string_", characterStyles: [])
  333. ])
  334. results = self.attempt(challenge)
  335. if results.stringTokens.count == challenge.tokens.count {
  336. for (idx, token) in results.stringTokens.enumerated() {
  337. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  338. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  339. }
  340. } else {
  341. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  342. }
  343. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  344. XCTAssertEqual(results.attributedString.string, challenge.output)
  345. challenge = TokenTest(input: "A string with \\_escaped\\_ underscores", output: "A string with _escaped_ underscores", tokens: [
  346. Token(type: .string, inputString: "A string with _escaped_ underscores", characterStyles: [])
  347. ])
  348. results = self.attempt(challenge)
  349. if results.stringTokens.count == challenge.tokens.count {
  350. for (idx, token) in results.stringTokens.enumerated() {
  351. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  352. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  353. }
  354. } else {
  355. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  356. }
  357. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  358. XCTAssertEqual(results.attributedString.string, challenge.output)
  359. challenge = TokenTest(input: """
  360. An asterisk: *
  361. Line break
  362. """, output: """
  363. An asterisk: *
  364. Line break
  365. """, tokens: [
  366. Token(type: .string, inputString: "An asterisk: *", characterStyles: []),
  367. Token(type: .string, inputString: "Line break", characterStyles: [])
  368. ])
  369. results = self.attempt(challenge)
  370. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count )
  371. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  372. XCTAssertEqual(results.attributedString.string, challenge.output)
  373. }
  374. func testThatStrikethroughTraitsAreRecognised() {
  375. challenge = TokenTest(input: "~~An~~A crossed-out string", output: "AnA crossed-out string", tokens: [
  376. Token(type: .string, inputString: "An", characterStyles: [CharacterStyle.strikethrough]),
  377. Token(type: .string, inputString: "A crossed-out string", characterStyles: [])
  378. ])
  379. results = self.attempt(challenge)
  380. if results.stringTokens.count == challenge.tokens.count {
  381. for (idx, token) in results.stringTokens.enumerated() {
  382. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  383. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  384. }
  385. } else {
  386. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  387. }
  388. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  389. challenge = TokenTest(input: "A **Bold** string and a ~~removed~~crossed-out string", output: "A Bold string and a removedcrossed-out string", tokens: [
  390. Token(type: .string, inputString: "A ", characterStyles: []),
  391. Token(type: .string, inputString: "Bold", characterStyles: [CharacterStyle.bold]),
  392. Token(type: .string, inputString: " string and a ", characterStyles: []),
  393. Token(type: .string, inputString: "removed", characterStyles: [CharacterStyle.strikethrough]),
  394. Token(type: .string, inputString: "crossed-out string", characterStyles: [])
  395. ])
  396. results = self.attempt(challenge)
  397. if results.stringTokens.count == challenge.tokens.count {
  398. for (idx, token) in results.stringTokens.enumerated() {
  399. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  400. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  401. }
  402. } else {
  403. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  404. }
  405. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  406. challenge = TokenTest(input: "\\~\\~removed\\~\\~crossed-out string. ~This should be ignored~", output: "~~removed~~crossed-out string. ~This should be ignored~", tokens: [
  407. Token(type: .string, inputString: "~~removed~~crossed-out string. ~This should be ignored~", characterStyles: [])
  408. ])
  409. results = self.attempt(challenge)
  410. if results.stringTokens.count == challenge.tokens.count {
  411. for (idx, token) in results.stringTokens.enumerated() {
  412. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  413. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  414. }
  415. } else {
  416. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  417. }
  418. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  419. }
  420. func testThatMixedTraitsAreRecognised() {
  421. challenge = TokenTest(input: "__A bold string__ with a **mix** **of** bold __styles__", output: "A bold string with a mix of bold styles", tokens : [
  422. Token(type: .string, inputString: "A bold string", characterStyles: [CharacterStyle.bold]),
  423. Token(type: .string, inputString: " with a ", characterStyles: []),
  424. Token(type: .string, inputString: "mix", characterStyles: [CharacterStyle.bold]),
  425. Token(type: .string, inputString: " ", characterStyles: []),
  426. Token(type: .string, inputString: "of", characterStyles: [CharacterStyle.bold]),
  427. Token(type: .string, inputString: " bold ", characterStyles: []),
  428. Token(type: .string, inputString: "styles", characterStyles: [CharacterStyle.bold])
  429. ])
  430. results = self.attempt(challenge)
  431. if results.stringTokens.count == challenge.tokens.count {
  432. for (idx, token) in results.stringTokens.enumerated() {
  433. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  434. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  435. }
  436. } else {
  437. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  438. }
  439. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  440. XCTAssertEqual(results.attributedString.string, challenge.output)
  441. challenge = TokenTest(input: "_An italic string_, **followed by a bold one**, `with some code`, \\*\\*and some\\*\\* \\_escaped\\_ \\`characters\\`, `ending` *with* __more__ variety.", output: "An italic string, followed by a bold one, with some code, **and some** _escaped_ `characters`, ending with more variety.", tokens : [
  442. Token(type: .string, inputString: "An italic string", characterStyles: [CharacterStyle.italic]),
  443. Token(type: .string, inputString: ", ", characterStyles: []),
  444. Token(type: .string, inputString: "followed by a bold one", characterStyles: [CharacterStyle.bold]),
  445. Token(type: .string, inputString: ", ", characterStyles: []),
  446. Token(type: .string, inputString: "with some code", characterStyles: [CharacterStyle.code]),
  447. Token(type: .string, inputString: ", **and some** _escaped_ `characters`, ", characterStyles: []),
  448. Token(type: .string, inputString: "ending", characterStyles: [CharacterStyle.code]),
  449. Token(type: .string, inputString: " ", characterStyles: []),
  450. Token(type: .string, inputString: "with", characterStyles: [CharacterStyle.italic]),
  451. Token(type: .string, inputString: " ", characterStyles: []),
  452. Token(type: .string, inputString: "more", characterStyles: [CharacterStyle.bold]),
  453. Token(type: .string, inputString: " variety.", characterStyles: [])
  454. ])
  455. results = self.attempt(challenge)
  456. if results.stringTokens.count == challenge.tokens.count {
  457. for (idx, token) in results.stringTokens.enumerated() {
  458. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  459. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  460. }
  461. } else {
  462. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  463. }
  464. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  465. XCTAssertEqual(results.attributedString.string, challenge.output)
  466. }
  467. func testForExtremeEscapeCombinations() {
  468. challenge = TokenTest(input: "Before *\\***\\****A bold string*\\***\\****\\ After", output: "Before ***A bold string***\\ After", tokens : [
  469. Token(type: .string, inputString: "Before ", characterStyles: []),
  470. Token(type: .string, inputString: "*", characterStyles: [CharacterStyle.italic]),
  471. Token(type: .string, inputString: "**", characterStyles: [CharacterStyle.bold]),
  472. Token(type: .string, inputString: "A bold string**", characterStyles: [CharacterStyle.bold, CharacterStyle.italic]),
  473. Token(type: .string, inputString: "\\ After", characterStyles: [])
  474. ])
  475. results = self.attempt(challenge)
  476. if results.stringTokens.count == challenge.tokens.count {
  477. for (idx, token) in results.stringTokens.enumerated() {
  478. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  479. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  480. }
  481. } else {
  482. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  483. }
  484. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  485. XCTAssertEqual(results.attributedString.string, challenge.output)
  486. }
  487. func testThatExtraCharactersAreHandles() {
  488. challenge = TokenTest(input: "***A bold italic string***", output: "A bold italic string", tokens: [
  489. Token(type: .string, inputString: "A bold italic string", characterStyles: [CharacterStyle.bold, CharacterStyle.italic])
  490. ])
  491. results = self.attempt(challenge)
  492. if results.stringTokens.count == challenge.tokens.count {
  493. for (idx, token) in results.stringTokens.enumerated() {
  494. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  495. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  496. }
  497. } else {
  498. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  499. }
  500. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  501. XCTAssertEqual(results.attributedString.string, challenge.output)
  502. challenge = TokenTest(input: "A string with a ****bold**** word", output: "A string with a bold word", tokens: [
  503. Token(type: .string, inputString: "A string with a ", characterStyles: []),
  504. Token(type: .string, inputString: "bold", characterStyles: [CharacterStyle.bold]),
  505. Token(type: .string, inputString: " word", characterStyles: [])
  506. ])
  507. results = self.attempt(challenge)
  508. if results.stringTokens.count == challenge.tokens.count {
  509. for (idx, token) in results.stringTokens.enumerated() {
  510. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  511. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  512. }
  513. } else {
  514. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  515. }
  516. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  517. XCTAssertEqual(results.attributedString.string, challenge.output)
  518. challenge = TokenTest(input: "A string with a ****bold italic*** word", output: "A string with a *bold italic word", tokens: [
  519. Token(type: .string, inputString: "A string with a ", characterStyles: []),
  520. Token(type: .string, inputString: "*bold italic", characterStyles: [CharacterStyle.bold, CharacterStyle.italic]),
  521. Token(type: .string, inputString: " word", characterStyles: [])
  522. ])
  523. results = self.attempt(challenge)
  524. if results.stringTokens.count == challenge.tokens.count {
  525. for (idx, token) in results.stringTokens.enumerated() {
  526. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  527. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  528. }
  529. } else {
  530. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  531. }
  532. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  533. XCTAssertEqual(results.attributedString.string, challenge.output)
  534. challenge = TokenTest(input: "A string with a ***bold** italic* word", output: "A string with a bold italic word", tokens: [
  535. Token(type: .string, inputString: "A string with a ", characterStyles: []),
  536. Token(type: .string, inputString: "bold", characterStyles: [CharacterStyle.bold, CharacterStyle.italic]),
  537. Token(type: .string, inputString: " italic", characterStyles: [CharacterStyle.italic]),
  538. Token(type: .string, inputString: " word", characterStyles: [])
  539. ])
  540. results = self.attempt(challenge)
  541. if results.stringTokens.count == challenge.tokens.count {
  542. for (idx, token) in results.stringTokens.enumerated() {
  543. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  544. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  545. }
  546. } else {
  547. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  548. }
  549. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  550. XCTAssertEqual(results.attributedString.string, challenge.output)
  551. challenge = TokenTest(input: "A string with a **bold*italic*bold** word", output: "A string with a bolditalicbold word", tokens: [
  552. Token(type: .string, inputString: "A string with a ", characterStyles: []),
  553. Token(type: .string, inputString: "bold", characterStyles: [CharacterStyle.bold]),
  554. Token(type: .string, inputString: "italic", characterStyles: [CharacterStyle.italic, CharacterStyle.bold]),
  555. Token(type: .string, inputString: "bold", characterStyles: [CharacterStyle.bold]),
  556. Token(type: .string, inputString: " word", characterStyles: [])
  557. ])
  558. results = self.attempt(challenge)
  559. if results.stringTokens.count == challenge.tokens.count {
  560. for (idx, token) in results.stringTokens.enumerated() {
  561. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  562. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  563. }
  564. } else {
  565. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  566. }
  567. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  568. XCTAssertEqual(results.attributedString.string, challenge.output)
  569. challenge = TokenTest(input: "A string with ```code`", output: "A string with ```code`", tokens : [
  570. Token(type: .string, inputString: "A string with ```code`", characterStyles: [])
  571. ])
  572. results = self.attempt(challenge)
  573. if results.stringTokens.count == challenge.tokens.count {
  574. for (idx, token) in results.stringTokens.enumerated() {
  575. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  576. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  577. }
  578. } else {
  579. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  580. }
  581. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  582. XCTAssertEqual(results.attributedString.string, challenge.output)
  583. challenge = TokenTest(input: "A string with ```code```", output: "A string with code", tokens : [
  584. Token(type: .string, inputString: "A string with ", characterStyles: []),
  585. Token(type: .string, inputString: "code", characterStyles: [CharacterStyle.code])
  586. ])
  587. results = self.attempt(challenge)
  588. if results.stringTokens.count == challenge.tokens.count {
  589. for (idx, token) in results.stringTokens.enumerated() {
  590. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  591. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  592. }
  593. } else {
  594. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  595. }
  596. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  597. XCTAssertEqual(results.attributedString.string, challenge.output)
  598. }
  599. // The new version of SwiftyMarkdown is a lot more strict than the old version, although this may change in future
  600. func offtestThatMarkdownMistakesAreHandledAppropriately() {
  601. let mismatchedBoldCharactersAtStart = "**This should be bold*"
  602. let mismatchedBoldCharactersWithin = "A string *that should be italic**"
  603. var md = SwiftyMarkdown(string: mismatchedBoldCharactersAtStart)
  604. XCTAssertEqual(md.attributedString().string, "This should be bold")
  605. md = SwiftyMarkdown(string: mismatchedBoldCharactersWithin)
  606. XCTAssertEqual(md.attributedString().string, "A string that should be italic")
  607. }
  608. func offtestAdvancedEscaping() {
  609. challenge = TokenTest(input: "\\***A normal string*\\**", output: "**A normal string*", tokens: [
  610. Token(type: .string, inputString: "**", characterStyles: []),
  611. Token(type: .string, inputString: "A normal string", characterStyles: [CharacterStyle.italic]),
  612. Token(type: .string, inputString: "**", characterStyles: [])
  613. ])
  614. results = self.attempt(challenge)
  615. if results.stringTokens.count == challenge.tokens.count {
  616. for (idx, token) in results.stringTokens.enumerated() {
  617. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  618. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  619. }
  620. } else {
  621. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  622. }
  623. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  624. XCTAssertEqual(results.attributedString.string, challenge.output)
  625. challenge = TokenTest(input: "A string with randomly *\\**escaped**\\* asterisks", output: "A string with randomly **escaped** asterisks", tokens: [
  626. Token(type: .string, inputString: "A string with randomly **", characterStyles: []),
  627. Token(type: .string, inputString: "escaped", characterStyles: [CharacterStyle.italic]),
  628. Token(type: .string, inputString: "** asterisks", characterStyles: [])
  629. ])
  630. results = self.attempt(challenge)
  631. if results.stringTokens.count == challenge.tokens.count {
  632. for (idx, token) in results.stringTokens.enumerated() {
  633. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  634. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  635. }
  636. } else {
  637. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  638. }
  639. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  640. XCTAssertEqual(results.attributedString.string, challenge.output)
  641. }
  642. func testThatAsterisksAndUnderscoresNotAttachedToWordsAreNotRemoved() {
  643. let asteriskFullStop = "Two asterisks followed by a full stop: **."
  644. let asteriskWithBold = "A **bold** word followed by an asterisk * "
  645. let underscoreFullStop = "Two underscores followed by a full stop: __."
  646. let asteriskComma = "An asterisk followed by a full stop: *, *"
  647. let backtickSpace = "A backtick followed by a space: `"
  648. let underscoreSpace = "An underscore followed by a space: _"
  649. let backtickComma = "A backtick followed by a space: `, `"
  650. let underscoreComma = "An underscore followed by a space: _, _"
  651. let backtickWithCode = "A `code` word followed by a backtick ` "
  652. let underscoreWithItalic = "An _italic_ word followed by an underscore _ "
  653. var md = SwiftyMarkdown(string: backtickSpace)
  654. SwiftyMarkdown.characterRules = self.defaultRules
  655. XCTAssertEqual(md.attributedString().string, backtickSpace)
  656. md = SwiftyMarkdown(string: underscoreSpace)
  657. XCTAssertEqual(md.attributedString().string, underscoreSpace)
  658. md = SwiftyMarkdown(string: asteriskFullStop)
  659. XCTAssertEqual(md.attributedString().string, asteriskFullStop)
  660. md = SwiftyMarkdown(string: underscoreFullStop)
  661. XCTAssertEqual(md.attributedString().string, underscoreFullStop)
  662. md = SwiftyMarkdown(string: asteriskComma)
  663. XCTAssertEqual(md.attributedString().string, asteriskComma)
  664. md = SwiftyMarkdown(string: backtickComma)
  665. XCTAssertEqual(md.attributedString().string, backtickComma)
  666. md = SwiftyMarkdown(string: underscoreComma)
  667. XCTAssertEqual(md.attributedString().string, underscoreComma)
  668. md = SwiftyMarkdown(string: asteriskWithBold)
  669. XCTAssertEqual(md.attributedString().string, "A bold word followed by an asterisk *")
  670. md = SwiftyMarkdown(string: backtickWithCode)
  671. XCTAssertEqual(md.attributedString().string, "A code word followed by a backtick `")
  672. md = SwiftyMarkdown(string: underscoreWithItalic)
  673. XCTAssertEqual(md.attributedString().string, "An italic word followed by an underscore _")
  674. }
  675. func testReportedCrashingStrings() {
  676. challenge = TokenTest(input: "[**\\!bang**](https://duckduckgo.com/bang)", output: "\\!bang", tokens: [
  677. Token(type: .string, inputString: "\\!bang", characterStyles: [CharacterStyle.link, CharacterStyle.bold])
  678. ])
  679. results = self.attempt(challenge)
  680. if results.stringTokens.count == challenge.tokens.count {
  681. for (idx, token) in results.stringTokens.enumerated() {
  682. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  683. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  684. }
  685. } else {
  686. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  687. }
  688. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  689. XCTAssertEqual(results.attributedString.string, challenge.output)
  690. }
  691. }