SwiftyMarkdownCharacterTests.swift 41 KB

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