SwiftyMarkdownLinkTests.swift 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  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 SwiftyMarkdownLinkTests: SwiftyMarkdownCharacterTests {
  11. func testSingleLinkPositions() {
  12. challenge = TokenTest(input: "[a](b)", output: "a", tokens: [
  13. Token(type: .string, inputString: "a", characterStyles: [CharacterStyle.link])
  14. ])
  15. results = self.attempt(challenge, rules: [.links])
  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. if let existentOpen = results.tokens.filter({ $0.type == .string && (($0.characterStyles as? [CharacterStyle])?.contains(.link) ?? false) }).first {
  27. XCTAssertEqual(existentOpen.metadataStrings.first, "b")
  28. } else {
  29. XCTFail("Failed to find an open link tag")
  30. }
  31. challenge = TokenTest(input: "[Link at](http://voyagetravelapps.com/) start", output: "Link at start", tokens: [
  32. Token(type: .string, inputString: "Link at", characterStyles: [CharacterStyle.link]),
  33. Token(type: .string, inputString: " start")
  34. ])
  35. results = self.attempt(challenge)
  36. if results.stringTokens.count == challenge.tokens.count {
  37. for (idx, token) in results.stringTokens.enumerated() {
  38. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  39. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  40. }
  41. } else {
  42. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  43. }
  44. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  45. XCTAssertEqual(results.attributedString.string, challenge.output)
  46. if let existentOpen = results.tokens.filter({ $0.type == .string && (($0.characterStyles as? [CharacterStyle])?.contains(.link) ?? false) }).first {
  47. XCTAssertEqual(existentOpen.metadataStrings.first, "http://voyagetravelapps.com/")
  48. } else {
  49. XCTFail("Failed to find an open link tag")
  50. }
  51. challenge = TokenTest(input: "A [link at end](http://voyagetravelapps.com/)", output: "A link at end", tokens: [
  52. Token(type: .string, inputString: "A ", characterStyles: []),
  53. Token(type: .string, inputString: "link at end", characterStyles: [CharacterStyle.link])
  54. ])
  55. results = self.attempt(challenge)
  56. if results.stringTokens.count == challenge.tokens.count {
  57. for (idx, token) in results.stringTokens.enumerated() {
  58. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  59. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  60. }
  61. } else {
  62. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  63. }
  64. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  65. XCTAssertEqual(results.attributedString.string, challenge.output)
  66. challenge = TokenTest(input: "A [link in the](http://voyagetravelapps.com/) middle", output: "A link in the middle", tokens: [
  67. Token(type: .string, inputString: "A ", characterStyles: []),
  68. Token(type: .string, inputString: "link in the", characterStyles: [CharacterStyle.link]),
  69. Token(type: .string, inputString: " middle", characterStyles: [])
  70. ])
  71. results = self.attempt(challenge)
  72. if results.stringTokens.count == challenge.tokens.count {
  73. for (idx, token) in results.stringTokens.enumerated() {
  74. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  75. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  76. }
  77. } else {
  78. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  79. }
  80. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  81. XCTAssertEqual(results.attributedString.string, challenge.output)
  82. }
  83. func testEscapedLinks() {
  84. challenge = TokenTest(input: "\\[a](b)", output: "[a](b)", tokens: [
  85. Token(type: .string, inputString: "[a](b)", characterStyles: [])
  86. ])
  87. results = self.attempt(challenge, rules: [.images, .referencedLinks, .links])
  88. if results.stringTokens.count == challenge.tokens.count {
  89. for (idx, token) in results.stringTokens.enumerated() {
  90. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  91. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  92. }
  93. } else {
  94. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  95. }
  96. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  97. XCTAssertEqual(results.attributedString.string, challenge.output)
  98. challenge = TokenTest(input: "![a](b)", output: "!a", tokens: [
  99. Token(type: .string, inputString: "!", characterStyles: []),
  100. Token(type: .string, inputString: "a", characterStyles: [CharacterStyle.link])
  101. ])
  102. results = self.attempt(challenge, rules: [.links])
  103. if results.stringTokens.count == challenge.tokens.count {
  104. for (idx, token) in results.stringTokens.enumerated() {
  105. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  106. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  107. }
  108. } else {
  109. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  110. }
  111. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  112. XCTAssertEqual(results.attributedString.string, challenge.output)
  113. }
  114. func testMultipleLinkPositions() {
  115. challenge = TokenTest(input: "[Link 1](http://voyagetravelapps.com/)[Link 2](https://www.neverendingvoyage.com/)", output: "Link 1Link 2", tokens: [
  116. Token(type: .string, inputString: "Link 1", characterStyles: [CharacterStyle.link]),
  117. Token(type: .string, inputString: "Link 2", characterStyles: [CharacterStyle.link])
  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. var links = results.tokens.filter({ $0.type == .string && (($0.characterStyles as? [CharacterStyle])?.contains(.link) ?? false) })
  131. if links.count == 2 {
  132. XCTAssertEqual(links[0].metadataStrings.first, "http://voyagetravelapps.com/")
  133. XCTAssertEqual(links[1].metadataStrings.first, "https://www.neverendingvoyage.com/")
  134. } else {
  135. XCTFail("Incorrect number of links found. Expecting 2, found \(links.count)")
  136. }
  137. challenge = TokenTest(input: "[Link 1](http://voyagetravelapps.com/), [Link 2](https://www.neverendingvoyage.com/)", output: "Link 1, Link 2", tokens: [
  138. Token(type: .string, inputString: "Link 1", characterStyles: [CharacterStyle.link]),
  139. Token(type: .string, inputString: ", ", characterStyles: []),
  140. Token(type: .string, inputString: "Link 2", characterStyles: [CharacterStyle.link])
  141. ])
  142. results = self.attempt(challenge)
  143. if results.stringTokens.count == challenge.tokens.count {
  144. for (idx, token) in results.stringTokens.enumerated() {
  145. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  146. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  147. }
  148. } else {
  149. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  150. }
  151. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  152. XCTAssertEqual(results.attributedString.string, challenge.output)
  153. links = results.tokens.filter({ $0.type == .string && (($0.characterStyles as? [CharacterStyle])?.contains(.link) ?? false) })
  154. if links.count == 2 {
  155. XCTAssertEqual(links[0].metadataStrings.first, "http://voyagetravelapps.com/")
  156. XCTAssertEqual(links[1].metadataStrings.first, "https://www.neverendingvoyage.com/")
  157. } else {
  158. XCTFail("Incorrect number of links found. Expecting 2, found \(links.count)")
  159. }
  160. challenge = TokenTest(input: "String at start [Link 1](http://voyagetravelapps.com/), [Link 2](https://www.neverendingvoyage.com/)", output: "String at start Link 1, Link 2", tokens: [
  161. Token(type: .string, inputString: "String at start ", characterStyles: []),
  162. Token(type: .string, inputString: "Link 1", characterStyles: [CharacterStyle.link]),
  163. Token(type: .string, inputString: ", ", characterStyles: []),
  164. Token(type: .string, inputString: "Link 2", characterStyles: [CharacterStyle.link])
  165. ])
  166. results = self.attempt(challenge)
  167. if results.stringTokens.count == challenge.tokens.count {
  168. for (idx, token) in results.stringTokens.enumerated() {
  169. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  170. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  171. }
  172. } else {
  173. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  174. }
  175. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  176. XCTAssertEqual(results.attributedString.string, challenge.output)
  177. links = results.tokens.filter({ $0.type == .string && (($0.characterStyles as? [CharacterStyle])?.contains(.link) ?? false) })
  178. if links.count == 2 {
  179. XCTAssertEqual(links[0].metadataStrings.first, "http://voyagetravelapps.com/")
  180. XCTAssertEqual(links[1].metadataStrings.first, "https://www.neverendingvoyage.com/")
  181. } else {
  182. XCTFail("Incorrect number of links found. Expecting 2, found \(links.count)")
  183. }
  184. challenge = TokenTest(input: "String at start [Link 1](http://voyagetravelapps.com/)[Link 2](https://www.neverendingvoyage.com/)", output: "String at start Link 1Link 2", tokens: [
  185. Token(type: .string, inputString: "String at start ", characterStyles: []),
  186. Token(type: .string, inputString: "Link 1", characterStyles: [CharacterStyle.link]),
  187. Token(type: .string, inputString: "Link 2", characterStyles: [CharacterStyle.link])
  188. ])
  189. results = self.attempt(challenge)
  190. if results.stringTokens.count == challenge.tokens.count {
  191. for (idx, token) in results.stringTokens.enumerated() {
  192. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  193. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  194. }
  195. } else {
  196. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  197. }
  198. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  199. XCTAssertEqual(results.attributedString.string, challenge.output)
  200. links = results.tokens.filter({ $0.type == .string && (($0.characterStyles as? [CharacterStyle])?.contains(.link) ?? false) })
  201. if links.count == 2 {
  202. XCTAssertEqual(links[0].metadataStrings.first, "http://voyagetravelapps.com/")
  203. XCTAssertEqual(links[1].metadataStrings.first, "https://www.neverendingvoyage.com/")
  204. } else {
  205. XCTFail("Incorrect number of links found. Expecting 2, found \(links.count)")
  206. }
  207. }
  208. func testForAlternativeURLs() {
  209. challenge = TokenTest(input: "Email us at [simon@voyagetravelapps.com](mailto:simon@voyagetravelapps.com) Twitter [@VoyageTravelApp](twitter://user?screen_name=VoyageTravelApp)", output: "Email us at simon@voyagetravelapps.com Twitter @VoyageTravelApp", tokens: [
  210. Token(type: .string, inputString: "Email us at ", characterStyles: []),
  211. Token(type: .string, inputString: "simon@voyagetravelapps.com", characterStyles: [CharacterStyle.link]),
  212. Token(type: .string, inputString: " Twitter ", characterStyles: []),
  213. Token(type: .string, inputString: "@VoyageTravelApp", characterStyles: [CharacterStyle.link])
  214. ])
  215. results = self.attempt(challenge)
  216. if results.stringTokens.count == challenge.tokens.count {
  217. for (idx, token) in results.stringTokens.enumerated() {
  218. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  219. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  220. }
  221. } else {
  222. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  223. }
  224. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  225. XCTAssertEqual(results.attributedString.string, challenge.output)
  226. let links = results.tokens.filter({ $0.type == .string && (($0.characterStyles as? [CharacterStyle])?.contains(.link) ?? false) })
  227. if links.count == 2 {
  228. XCTAssertEqual(links[0].metadataStrings.first, "mailto:simon@voyagetravelapps.com")
  229. XCTAssertEqual(links[1].metadataStrings.first, "twitter://user?screen_name=VoyageTravelApp")
  230. } else {
  231. XCTFail("Incorrect number of links found. Expecting 2, found \(links.count)")
  232. }
  233. }
  234. func testForLinksMixedWithTokenCharacters() {
  235. challenge = TokenTest(input: "Link ([Surrounded by parentheses](https://www.neverendingvoyage.com/))", output: "Link (Surrounded by parentheses)", tokens: [
  236. Token(type: .string, inputString: "Link (", characterStyles: []),
  237. Token(type: .string, inputString: "Surrounded by parentheses", characterStyles: [CharacterStyle.link]),
  238. Token(type: .string, inputString: ")", 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. var links = results.tokens.filter({ $0.type == .string && (($0.characterStyles as? [CharacterStyle])?.contains(.link) ?? false) })
  252. if links.count == 1 {
  253. XCTAssertEqual(links[0].metadataStrings.first, "https://www.neverendingvoyage.com/")
  254. } else {
  255. XCTFail("Incorrect number of links found. Expecting 2, found \(links.count)")
  256. }
  257. challenge = TokenTest(input: "[[Surrounded by square brackets](https://www.neverendingvoyage.com/)]", output: "[Surrounded by square brackets]", tokens: [
  258. Token(type: .string, inputString: "[", characterStyles: []),
  259. Token(type: .string, inputString: "Surrounded by square brackets", characterStyles: [CharacterStyle.link]),
  260. Token(type: .string, inputString: "]", characterStyles: [])
  261. ])
  262. results = self.attempt(challenge)
  263. if results.stringTokens.count == challenge.tokens.count {
  264. for (idx, token) in results.stringTokens.enumerated() {
  265. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  266. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  267. }
  268. } else {
  269. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  270. }
  271. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  272. XCTAssertEqual(results.attributedString.string, challenge.output)
  273. links = results.tokens.filter({ $0.type == .string && (($0.characterStyles as? [CharacterStyle])?.contains(.link) ?? false) })
  274. if links.count == 1 {
  275. XCTAssertEqual(links[0].metadataStrings.first, "https://www.neverendingvoyage.com/")
  276. } else {
  277. XCTFail("Incorrect number of links found. Expecting 2, found \(links.count)")
  278. }
  279. }
  280. func testMalformedLinks() {
  281. challenge = TokenTest(input: "[Link with missing parenthesis](http://voyagetravelapps.com/", output: "[Link with missing parenthesis](http://voyagetravelapps.com/", tokens: [
  282. Token(type: .string, inputString: "[Link with missing parenthesis](http://voyagetravelapps.com/", characterStyles: [])
  283. ])
  284. results = self.attempt(challenge)
  285. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count )
  286. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  287. XCTAssertEqual(results.attributedString.string, challenge.output)
  288. challenge = TokenTest(input: "A [Link](http://voyagetravelapps.com/", output: "A [Link](http://voyagetravelapps.com/", tokens: [
  289. Token(type: .string, inputString: "A [Link](http://voyagetravelapps.com/", characterStyles: [])
  290. ])
  291. results = self.attempt(challenge)
  292. if results.stringTokens.count == challenge.tokens.count {
  293. for (idx, token) in results.stringTokens.enumerated() {
  294. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  295. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  296. }
  297. } else {
  298. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  299. }
  300. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  301. XCTAssertEqual(results.attributedString.string, challenge.output)
  302. challenge = TokenTest(input: "[A link](((url)", output: "[A link](((url)", tokens: [
  303. Token(type: .string, inputString: "[A link](((url)", characterStyles: [])
  304. ])
  305. results = self.attempt(challenge, rules: [.images, .links])
  306. if results.stringTokens.count == challenge.tokens.count {
  307. for (idx, token) in results.stringTokens.enumerated() {
  308. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  309. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  310. }
  311. } else {
  312. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  313. }
  314. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  315. XCTAssertEqual(results.attributedString.string, challenge.output)
  316. XCTAssertEqual(results.links.count, 0)
  317. challenge = TokenTest(input: "[[a](((b)](c)", output: "[a](((b)", tokens: [
  318. Token(type: .string, inputString: "[a](((b)", characterStyles: [CharacterStyle.link])
  319. ])
  320. results = self.attempt(challenge, rules: [.images, .links])
  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. XCTAssertEqual(results.links.count, 1)
  332. if results.links.count == 1 {
  333. XCTAssertEqual(results.links[0].metadataStrings.first, "c")
  334. } else {
  335. XCTFail("Incorrect link count. Expecting 1, found \(results.links.count)")
  336. }
  337. challenge = TokenTest(input: "[Link with missing square(http://voyagetravelapps.com/)", output: "[Link with missing square(http://voyagetravelapps.com/)", tokens: [
  338. Token(type: .string, inputString: "[Link with missing square(http://voyagetravelapps.com/)", characterStyles: [])
  339. ])
  340. results = self.attempt(challenge)
  341. if results.stringTokens.count == challenge.tokens.count {
  342. for (idx, token) in results.stringTokens.enumerated() {
  343. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  344. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  345. }
  346. } else {
  347. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  348. }
  349. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  350. XCTAssertEqual(results.attributedString.string, challenge.output)
  351. challenge = TokenTest(input: "[Link with [second opening](http://voyagetravelapps.com/)", output: "[Link with second opening", tokens: [
  352. Token(type: .string, inputString: "[Link with ", characterStyles: []),
  353. Token(type: .string, inputString: "second opening", characterStyles: [CharacterStyle.link])
  354. ])
  355. results = self.attempt(challenge)
  356. if results.stringTokens.count == challenge.tokens.count {
  357. for (idx, token) in results.stringTokens.enumerated() {
  358. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  359. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  360. }
  361. } else {
  362. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  363. }
  364. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  365. XCTAssertEqual(results.attributedString.string, challenge.output)
  366. XCTAssertEqual(results.links.count, 1)
  367. if results.links.count == 1 {
  368. XCTAssertEqual(results.links[0].metadataStrings.first, "http://voyagetravelapps.com/")
  369. } else {
  370. XCTFail("Incorrect link count. Expecting 1, found \(results.links.count)")
  371. }
  372. challenge = TokenTest(input: "A [Link(http://voyagetravelapps.com/)", output: "A [Link(http://voyagetravelapps.com/)", tokens: [
  373. Token(type: .string, inputString: "A [Link(http://voyagetravelapps.com/)", characterStyles: [])
  374. ])
  375. results = self.attempt(challenge)
  376. if results.stringTokens.count == challenge.tokens.count {
  377. for (idx, token) in results.stringTokens.enumerated() {
  378. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  379. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  380. }
  381. } else {
  382. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  383. }
  384. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  385. XCTAssertEqual(results.attributedString.string, challenge.output)
  386. }
  387. func testMalformedLinksWithValidLinks() {
  388. challenge = TokenTest(input: "[Link with missing parenthesis](http://voyagetravelapps.com/ followed by a [valid link](http://voyagetravelapps.com/)", output: "[Link with missing parenthesis](http://voyagetravelapps.com/ followed by a valid link", tokens: [
  389. Token(type: .string, inputString: "[Link with missing parenthesis](http://voyagetravelapps.com/ followed by a ", characterStyles: []),
  390. Token(type: .string, inputString: "valid link", characterStyles: [CharacterStyle.link])
  391. ])
  392. results = self.attempt(challenge)
  393. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count )
  394. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  395. XCTAssertEqual(results.attributedString.string, challenge.output)
  396. XCTAssertEqual(results.links.count, 1)
  397. if results.links.count == 1 {
  398. XCTAssertEqual(results.links[0].metadataStrings.first, "http://voyagetravelapps.com/")
  399. } else {
  400. XCTFail("Incorrect link count. Expecting 1, found \(results.links.count)")
  401. }
  402. challenge = TokenTest(input: "A [Link](http://voyagetravelapps.com/ followed by a [valid link](http://voyagetravelapps.com/)", output: "A [Link](http://voyagetravelapps.com/ followed by a valid link", tokens: [
  403. Token(type: .string, inputString: "A [Link](http://voyagetravelapps.com/ followed by a ", characterStyles: []),
  404. Token(type: .string, inputString: "valid link", characterStyles: [CharacterStyle.link])
  405. ])
  406. results = self.attempt(challenge)
  407. if results.stringTokens.count == challenge.tokens.count {
  408. for (idx, token) in results.stringTokens.enumerated() {
  409. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  410. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  411. }
  412. } else {
  413. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  414. }
  415. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  416. XCTAssertEqual(results.attributedString.string, challenge.output)
  417. XCTAssertEqual(results.links.count, 1)
  418. if results.links.count == 1 {
  419. XCTAssertEqual(results.links[0].metadataStrings.first, "http://voyagetravelapps.com/")
  420. } else {
  421. XCTFail("Incorrect link count. Expecting 1, found \(results.links.count)")
  422. }
  423. challenge = TokenTest(input: "[Link with missing square(http://voyagetravelapps.com/) followed by a [valid link](http://voyagetravelapps.com/)", output: "[Link with missing square(http://voyagetravelapps.com/) followed by a valid link", tokens: [
  424. Token(type: .string, inputString: "[Link with missing square(http://voyagetravelapps.com/) followed by a ", characterStyles: []),
  425. Token(type: .string, inputString: "valid link", characterStyles: [CharacterStyle.link])
  426. ])
  427. results = self.attempt(challenge)
  428. if results.stringTokens.count == challenge.tokens.count {
  429. for (idx, token) in results.stringTokens.enumerated() {
  430. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  431. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  432. }
  433. } else {
  434. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  435. }
  436. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  437. XCTAssertEqual(results.attributedString.string, challenge.output)
  438. XCTAssertEqual(results.links.count, 1)
  439. if results.links.count == 1 {
  440. XCTAssertEqual(results.links[0].metadataStrings.first, "http://voyagetravelapps.com/")
  441. } else {
  442. XCTFail("Incorrect link count. Expecting 1, found \(results.links.count)")
  443. }
  444. challenge = TokenTest(input: "A [Link(http://voyagetravelapps.com/) followed by a [valid link](http://voyagetravelapps.com/)", output: "A [Link(http://voyagetravelapps.com/) followed by a valid link", tokens: [
  445. Token(type: .string, inputString: "A [Link(http://voyagetravelapps.com/) followed by a ", characterStyles: []),
  446. Token(type: .string, inputString: "valid link", characterStyles: [CharacterStyle.link])
  447. ])
  448. results = self.attempt(challenge)
  449. if results.stringTokens.count == challenge.tokens.count {
  450. for (idx, token) in results.stringTokens.enumerated() {
  451. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  452. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  453. }
  454. } else {
  455. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  456. }
  457. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  458. XCTAssertEqual(results.attributedString.string, challenge.output)
  459. }
  460. func testLinksWithOtherStyles() {
  461. challenge = TokenTest(input: "A **Bold [Link](http://voyagetravelapps.com/)**", output: "A Bold Link", tokens: [
  462. Token(type: .string, inputString: "A ", characterStyles: []),
  463. Token(type: .string, inputString: "Bold ", characterStyles: [CharacterStyle.bold]),
  464. Token(type: .string, inputString: "Link", characterStyles: [CharacterStyle.link, CharacterStyle.bold])
  465. ])
  466. results = self.attempt(challenge)
  467. if results.stringTokens.count == challenge.tokens.count {
  468. for (idx, token) in results.stringTokens.enumerated() {
  469. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  470. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  471. }
  472. } else {
  473. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  474. }
  475. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  476. XCTAssertEqual(results.links.count, 1)
  477. if results.links.count == 1 {
  478. XCTAssertEqual(results.links[0].metadataStrings.first, "http://voyagetravelapps.com/")
  479. } else {
  480. XCTFail("Incorrect link count. Expecting 1, found \(results.links.count)")
  481. }
  482. challenge = TokenTest(input: "A Bold [**Link**](http://voyagetravelapps.com/)", output: "A Bold Link", tokens: [
  483. Token(type: .string, inputString: "A Bold ", characterStyles: []),
  484. Token(type: .string, inputString: "Link", characterStyles: [ CharacterStyle.link, CharacterStyle.bold])
  485. ])
  486. results = self.attempt(challenge)
  487. if results.stringTokens.count == challenge.tokens.count {
  488. for (idx, token) in results.stringTokens.enumerated() {
  489. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  490. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  491. }
  492. } else {
  493. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  494. }
  495. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  496. XCTAssertEqual(results.attributedString.string, challenge.output)
  497. XCTAssertEqual(results.links.count, 1)
  498. if results.links.count == 1 {
  499. XCTAssertEqual(results.links[0].metadataStrings.first, "http://voyagetravelapps.com/")
  500. } else {
  501. XCTFail("Incorrect link count. Expecting 1, found \(results.links.count)")
  502. }
  503. challenge = TokenTest(input: "[Link1](http://voyagetravelapps.com/) **bold** [Link2](http://voyagetravelapps.com/)", output: "Link1 bold Link2", tokens: [
  504. Token(type: .string, inputString: "Link1", characterStyles: [CharacterStyle.link]),
  505. Token(type: .string, inputString: " ", characterStyles: []),
  506. Token(type: .string, inputString: "bold", characterStyles: [CharacterStyle.bold]),
  507. Token(type: .string, inputString: " ", characterStyles: []),
  508. Token(type: .string, inputString: "Link2", characterStyles: [CharacterStyle.link])
  509. ])
  510. results = self.attempt(challenge)
  511. if results.stringTokens.count == challenge.tokens.count {
  512. for (idx, token) in results.stringTokens.enumerated() {
  513. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  514. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  515. }
  516. } else {
  517. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  518. }
  519. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  520. XCTAssertEqual(results.attributedString.string, challenge.output)
  521. }
  522. func testForImages() {
  523. challenge = TokenTest(input: "An ![Image](imageName)", output: "An ", tokens: [
  524. Token(type: .string, inputString: "An ", characterStyles: []),
  525. Token(type: .string, inputString: "Image", characterStyles: [CharacterStyle.image])
  526. ])
  527. results = self.attempt(challenge)
  528. if results.stringTokens.count == challenge.tokens.count {
  529. for (idx, token) in results.stringTokens.enumerated() {
  530. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  531. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  532. }
  533. } else {
  534. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  535. }
  536. XCTAssertEqual(results.attributedString.string, challenge.output)
  537. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  538. if results.images.count == 1 {
  539. XCTAssertEqual(results.images[0].metadataStrings.first, "imageName")
  540. } else {
  541. XCTFail("Incorrect link count. Expecting 1, found \(results.images.count)")
  542. }
  543. challenge = TokenTest(input: "An [![Image](imageName)](https://www.neverendingvoyage.com/)", output: "An ", tokens: [
  544. Token(type: .string, inputString: "An ", characterStyles: []),
  545. Token(type: .string, inputString: "Image", characterStyles: [CharacterStyle.image, CharacterStyle.link])
  546. ])
  547. results = self.attempt(challenge)
  548. if results.stringTokens.count == challenge.tokens.count {
  549. for (idx, token) in results.stringTokens.enumerated() {
  550. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  551. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  552. }
  553. } else {
  554. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  555. }
  556. XCTAssertEqual(results.attributedString.string, challenge.output)
  557. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  558. if results.images.count == 1 {
  559. XCTAssertEqual(results.images[0].metadataStrings.first, "imageName")
  560. } else {
  561. XCTFail("Incorrect link count. Expecting 1, found \(results.images.count)")
  562. }
  563. if results.links.count == 1 {
  564. XCTAssertEqual(results.links[0].metadataStrings.last, "https://www.neverendingvoyage.com/")
  565. } else {
  566. XCTFail("Incorrect link count. Expecting 1, found \(results.links.count)")
  567. }
  568. }
  569. func testForReferencedImages() {
  570. challenge = TokenTest(input: "A ![referenced image][image]\n[image]: imageName", output: "A ", tokens: [
  571. Token(type: .string, inputString: "A ", characterStyles: []),
  572. Token(type: .string, inputString: "referenced image", characterStyles: [CharacterStyle.image])
  573. ])
  574. results = self.attempt(challenge)
  575. if results.stringTokens.count == challenge.tokens.count {
  576. for (idx, token) in results.stringTokens.enumerated() {
  577. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  578. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  579. }
  580. } else {
  581. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  582. }
  583. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  584. if results.images.count == 1 {
  585. XCTAssertEqual(results.images[0].metadataStrings.first, "imageName")
  586. } else {
  587. XCTFail("Incorrect link count. Expecting 1, found \(results.links.count)")
  588. }
  589. }
  590. func testForReferencedLinks() {
  591. challenge = TokenTest(input: "A [referenced link][link]\n[link]: https://www.neverendingvoyage.com/", output: "A referenced link", tokens: [
  592. Token(type: .string, inputString: "A ", characterStyles: []),
  593. Token(type: .string, inputString: "referenced link", characterStyles: [CharacterStyle.link])
  594. ])
  595. results = self.attempt(challenge)
  596. if results.stringTokens.count == challenge.tokens.count {
  597. for (idx, token) in results.stringTokens.enumerated() {
  598. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  599. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  600. }
  601. } else {
  602. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  603. }
  604. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  605. XCTAssertEqual(results.attributedString.string, challenge.output)
  606. if results.links.count == 1 {
  607. XCTAssertEqual(results.links[0].metadataStrings.first, "https://www.neverendingvoyage.com/")
  608. } else {
  609. XCTFail("Incorrect link count. Expecting 1, found \(results.links.count)")
  610. }
  611. challenge = TokenTest(input: "A [referenced link][link]\n [link]: https://www.neverendingvoyage.com/", output: "A referenced link", tokens: [
  612. Token(type: .string, inputString: "A ", characterStyles: []),
  613. Token(type: .string, inputString: "referenced link", characterStyles: [CharacterStyle.link])
  614. ])
  615. results = self.attempt(challenge)
  616. if results.stringTokens.count == challenge.tokens.count {
  617. for (idx, token) in results.stringTokens.enumerated() {
  618. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  619. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  620. }
  621. } else {
  622. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  623. }
  624. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  625. if results.links.count == 1 {
  626. XCTAssertEqual(results.links[0].metadataStrings.first, "https://www.neverendingvoyage.com/")
  627. } else {
  628. XCTFail("Incorrect link count. Expecting 1, found \(results.links.count)")
  629. }
  630. challenge = TokenTest(input: "An *\\*italic\\** [referenced link][a]\n[a]: link", output: "An *italic* referenced link", tokens: [
  631. Token(type: .string, inputString: "An ", characterStyles: []),
  632. Token(type: .string, inputString: "*italic*", characterStyles: [CharacterStyle.italic]),
  633. Token(type: .string, inputString: " ", characterStyles: []),
  634. Token(type: .string, inputString: "referenced link", characterStyles: [CharacterStyle.link])
  635. ])
  636. results = self.attempt(challenge, rules: [.asterisks, .links, .referencedLinks])
  637. if results.stringTokens.count == challenge.tokens.count {
  638. for (idx, token) in results.stringTokens.enumerated() {
  639. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  640. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  641. }
  642. } else {
  643. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  644. }
  645. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  646. if results.links.count == 1 {
  647. XCTAssertEqual(results.links[0].metadataStrings.first, "link")
  648. } else {
  649. XCTFail("Incorrect link count. Expecting 1, found \(results.links.count)")
  650. }
  651. }
  652. func testForMixedLinkStyles() {
  653. challenge = TokenTest(input: "A [referenced link][link] and a [regular link](http://voyagetravelapps.com/)\n[link]: https://www.neverendingvoyage.com/", output: "A referenced link and a regular link", tokens: [
  654. Token(type: .string, inputString: "A ", characterStyles: []),
  655. Token(type: .string, inputString: "referenced link", characterStyles: [CharacterStyle.link]),
  656. Token(type: .string, inputString: " and a ", characterStyles: []),
  657. Token(type: .string, inputString: "regular link", characterStyles: [CharacterStyle.link])
  658. ])
  659. results = self.attempt(challenge)
  660. if results.stringTokens.count == challenge.tokens.count {
  661. for (idx, token) in results.stringTokens.enumerated() {
  662. XCTAssertEqual(token.inputString, challenge.tokens[idx].inputString)
  663. XCTAssertEqual(token.characterStyles as? [CharacterStyle], challenge.tokens[idx].characterStyles as? [CharacterStyle])
  664. }
  665. } else {
  666. XCTAssertEqual(results.stringTokens.count, challenge.tokens.count)
  667. }
  668. XCTAssertEqual(results.foundStyles, results.expectedStyles)
  669. if results.links.count == 2 {
  670. XCTAssertEqual(results.links[0].metadataStrings.first, "https://www.neverendingvoyage.com/")
  671. XCTAssertEqual(results.links[1].metadataStrings.first, "http://voyagetravelapps.com/")
  672. } else {
  673. XCTFail("Incorrect link count. Expecting 1, found \(results.links.count)")
  674. }
  675. }
  676. }