utils.js 48 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703
  1. import { Map, fromJS } from "immutable"
  2. import {
  3. mapToList,
  4. parseSearch,
  5. serializeSearch,
  6. validatePattern,
  7. validateMinLength,
  8. validateMaxLength,
  9. validateDateTime,
  10. validateGuid,
  11. validateNumber,
  12. validateInteger,
  13. validateParam,
  14. validateFile,
  15. validateMaximum,
  16. validateMinimum,
  17. fromJSOrdered,
  18. getAcceptControllingResponse,
  19. createDeepLinkPath,
  20. escapeDeepLinkPath,
  21. getExtensions,
  22. getCommonExtensions,
  23. sanitizeUrl,
  24. requiresValidationURL,
  25. extractFileNameFromContentDispositionHeader,
  26. deeplyStripKey,
  27. getSampleSchema,
  28. paramToIdentifier,
  29. paramToValue,
  30. generateCodeVerifier,
  31. createCodeChallenge,
  32. } from "core/utils"
  33. import {
  34. isAbsoluteUrl,
  35. buildBaseUrl,
  36. buildUrl,
  37. } from "core/utils/url"
  38. import win from "core/window"
  39. describe("utils", () => {
  40. describe("mapToList", () =>{
  41. it("should convert a map to a list, setting `key`", () =>{
  42. // With
  43. const aMap = fromJS({
  44. a: {
  45. one: 1,
  46. },
  47. b: {
  48. two: 2,
  49. }
  50. })
  51. // When
  52. const aList = mapToList(aMap, "someKey")
  53. // Then
  54. expect(aList.toJS()).toEqual([
  55. { someKey: "a", one: 1 },
  56. { someKey: "b", two: 2 },
  57. ])
  58. })
  59. it("should flatten an arbitrarily deep map", () =>{
  60. // With
  61. const aMap = fromJS({
  62. a: {
  63. one: {
  64. alpha: true
  65. }
  66. },
  67. b: {
  68. two: {
  69. bravo: true
  70. },
  71. three: {
  72. charlie: true
  73. }
  74. }
  75. })
  76. // When
  77. const aList = mapToList(aMap, ["levelA", "levelB"])
  78. // Then
  79. expect(aList.toJS()).toEqual([
  80. { levelA: "a", levelB: "one", alpha: true },
  81. { levelA: "b", levelB: "two", bravo: true },
  82. { levelA: "b", levelB: "three", charlie: true },
  83. ])
  84. })
  85. it("should handle an empty map", () =>{
  86. // With
  87. const aMap = fromJS({})
  88. // When
  89. const aList = mapToList(aMap, ["levelA", "levelB"])
  90. // Then
  91. expect(aList.toJS()).toEqual([])
  92. })
  93. })
  94. describe("extractFileNameFromContentDispositionHeader", () =>{
  95. it("should extract quoted filename", () =>{
  96. let cdHeader = "attachment; filename=\"file name.jpg\""
  97. let expectedResult = "file name.jpg"
  98. expect(extractFileNameFromContentDispositionHeader(cdHeader)).toEqual(expectedResult)
  99. })
  100. it("should extract filename", () =>{
  101. let cdHeader = "attachment; filename=filename.jpg"
  102. let expectedResult = "filename.jpg"
  103. expect(extractFileNameFromContentDispositionHeader(cdHeader)).toEqual(expectedResult)
  104. })
  105. it("should extract quoted filename in utf-8", () =>{
  106. let cdHeader = "attachment; filename*=UTF-8''\"%D1%84%D0%B0%D0%B9%D0%BB.txt\""
  107. let expectedResult = "файл.txt"
  108. expect(extractFileNameFromContentDispositionHeader(cdHeader)).toEqual(expectedResult)
  109. })
  110. it("should extract filename in utf-8", () =>{
  111. let cdHeader = "attachment; filename*=utf-8'ru'%D1%84%D0%B0%D0%B9%D0%BB.txt"
  112. let expectedResult = "файл.txt"
  113. expect(extractFileNameFromContentDispositionHeader(cdHeader)).toEqual(expectedResult)
  114. })
  115. it("should not extract filename and return null", () =>{
  116. let cdHeader = "attachment; no file name provided"
  117. let expectedResult = null
  118. expect(extractFileNameFromContentDispositionHeader(cdHeader)).toEqual(expectedResult)
  119. })
  120. })
  121. describe("validateMaximum", () => {
  122. it("doesn't return for valid input", () => {
  123. expect(validateMaximum(9, 10)).toBeFalsy()
  124. expect(validateMaximum(19, 20)).toBeFalsy()
  125. })
  126. it("returns a message for invalid input", () => {
  127. expect(validateMaximum(1, 0)).toEqual("Value must be less than 0")
  128. expect(validateMaximum(10, 9)).toEqual("Value must be less than 9")
  129. expect(validateMaximum(20, 19)).toEqual("Value must be less than 19")
  130. })
  131. })
  132. describe("validateMinimum", () => {
  133. it("doesn't return for valid input", () => {
  134. expect(validateMinimum(2, 1)).toBeFalsy()
  135. expect(validateMinimum(20, 10)).toBeFalsy()
  136. })
  137. it("returns a message for invalid input", () => {
  138. expect(validateMinimum(-1, 0)).toEqual("Value must be greater than 0")
  139. expect(validateMinimum(1, 2)).toEqual("Value must be greater than 2")
  140. expect(validateMinimum(10, 20)).toEqual("Value must be greater than 20")
  141. })
  142. })
  143. describe("validateNumber", () => {
  144. let errorMessage = "Value must be a number"
  145. it("doesn't return for whole numbers", () => {
  146. expect(validateNumber(0)).toBeFalsy()
  147. expect(validateNumber(1)).toBeFalsy()
  148. expect(validateNumber(20)).toBeFalsy()
  149. expect(validateNumber(5000000)).toBeFalsy()
  150. expect(validateNumber("1")).toBeFalsy()
  151. expect(validateNumber("2")).toBeFalsy()
  152. expect(validateNumber(-1)).toBeFalsy()
  153. expect(validateNumber(-20)).toBeFalsy()
  154. expect(validateNumber(-5000000)).toBeFalsy()
  155. })
  156. it("doesn't return for negative numbers", () => {
  157. expect(validateNumber(-1)).toBeFalsy()
  158. expect(validateNumber(-20)).toBeFalsy()
  159. expect(validateNumber(-5000000)).toBeFalsy()
  160. })
  161. it("doesn't return for decimal numbers", () => {
  162. expect(validateNumber(1.1)).toBeFalsy()
  163. expect(validateNumber(2.5)).toBeFalsy()
  164. expect(validateNumber(-30.99)).toBeFalsy()
  165. })
  166. it("returns a message for strings", () => {
  167. expect(validateNumber("")).toEqual(errorMessage)
  168. expect(validateNumber(" ")).toEqual(errorMessage)
  169. expect(validateNumber("test")).toEqual(errorMessage)
  170. })
  171. it("returns a message for invalid input", () => {
  172. expect(validateNumber(undefined)).toEqual(errorMessage)
  173. expect(validateNumber(null)).toEqual(errorMessage)
  174. expect(validateNumber({})).toEqual(errorMessage)
  175. expect(validateNumber([])).toEqual(errorMessage)
  176. expect(validateNumber(true)).toEqual(errorMessage)
  177. expect(validateNumber(false)).toEqual(errorMessage)
  178. })
  179. })
  180. describe("validateInteger", () => {
  181. let errorMessage = "Value must be an integer"
  182. it("doesn't return for positive integers", () => {
  183. expect(validateInteger(0)).toBeFalsy()
  184. expect(validateInteger(1)).toBeFalsy()
  185. expect(validateInteger(20)).toBeFalsy()
  186. expect(validateInteger(5000000)).toBeFalsy()
  187. expect(validateInteger("1")).toBeFalsy()
  188. expect(validateInteger("2")).toBeFalsy()
  189. expect(validateInteger(-1)).toBeFalsy()
  190. expect(validateInteger(-20)).toBeFalsy()
  191. expect(validateInteger(-5000000)).toBeFalsy()
  192. })
  193. it("doesn't return for negative integers", () => {
  194. expect(validateInteger(-1)).toBeFalsy()
  195. expect(validateInteger(-20)).toBeFalsy()
  196. expect(validateInteger(-5000000)).toBeFalsy()
  197. })
  198. it("returns a message for decimal values", () => {
  199. expect(validateInteger(1.1)).toEqual(errorMessage)
  200. expect(validateInteger(2.5)).toEqual(errorMessage)
  201. expect(validateInteger(-30.99)).toEqual(errorMessage)
  202. })
  203. it("returns a message for strings", () => {
  204. expect(validateInteger("")).toEqual(errorMessage)
  205. expect(validateInteger(" ")).toEqual(errorMessage)
  206. expect(validateInteger("test")).toEqual(errorMessage)
  207. })
  208. it("returns a message for invalid input", () => {
  209. expect(validateInteger(undefined)).toEqual(errorMessage)
  210. expect(validateInteger(null)).toEqual(errorMessage)
  211. expect(validateInteger({})).toEqual(errorMessage)
  212. expect(validateInteger([])).toEqual(errorMessage)
  213. expect(validateInteger(true)).toEqual(errorMessage)
  214. expect(validateInteger(false)).toEqual(errorMessage)
  215. })
  216. })
  217. describe("validateFile", () => {
  218. let errorMessage = "Value must be a file"
  219. it("validates against objects which are instances of 'File'", () => {
  220. let fileObj = new win.File([], "Test File")
  221. expect(validateFile(fileObj)).toBeFalsy()
  222. expect(validateFile(null)).toBeFalsy()
  223. expect(validateFile(undefined)).toBeFalsy()
  224. expect(validateFile(1)).toEqual(errorMessage)
  225. expect(validateFile("string")).toEqual(errorMessage)
  226. })
  227. })
  228. describe("validateDateTime", () => {
  229. let errorMessage = "Value must be a DateTime"
  230. it("doesn't return for valid dates", () => {
  231. expect(validateDateTime("Mon, 25 Dec 1995 13:30:00 +0430")).toBeFalsy()
  232. })
  233. it("returns a message for invalid input'", () => {
  234. expect(validateDateTime(null)).toEqual(errorMessage)
  235. expect(validateDateTime("string")).toEqual(errorMessage)
  236. })
  237. })
  238. describe("validateGuid", () => {
  239. let errorMessage = "Value must be a Guid"
  240. it("doesn't return for valid guid", () => {
  241. expect(validateGuid("8ce4811e-cec5-4a29-891a-15d1917153c1")).toBeFalsy()
  242. expect(validateGuid("{8ce4811e-cec5-4a29-891a-15d1917153c1}")).toBeFalsy()
  243. expect(validateGuid("8CE4811E-CEC5-4A29-891A-15D1917153C1")).toBeFalsy()
  244. expect(validateGuid("6ffefd8e-a018-e811-bbf9-60f67727d806")).toBeFalsy()
  245. expect(validateGuid("6FFEFD8E-A018-E811-BBF9-60F67727D806")).toBeFalsy()
  246. expect(validateGuid("00000000-0000-0000-0000-000000000000")).toBeFalsy()
  247. })
  248. it("returns a message for invalid input'", () => {
  249. expect(validateGuid(1)).toEqual(errorMessage)
  250. expect(validateGuid("string")).toEqual(errorMessage)
  251. })
  252. })
  253. describe("validateMaxLength", () => {
  254. it("doesn't return for valid input", () => {
  255. expect(validateMaxLength("a", 1)).toBeFalsy()
  256. expect(validateMaxLength("abc", 5)).toBeFalsy()
  257. })
  258. it("returns a message for invalid input'", () => {
  259. expect(validateMaxLength("abc", 0)).toEqual("Value must be no longer than 0 characters")
  260. expect(validateMaxLength("abc", 1)).toEqual("Value must be no longer than 1 character")
  261. expect(validateMaxLength("abc", 2)).toEqual("Value must be no longer than 2 characters")
  262. })
  263. })
  264. describe("validateMinLength", () => {
  265. it("doesn't return for valid input", () => {
  266. expect(validateMinLength("a", 1)).toBeFalsy()
  267. expect(validateMinLength("abc", 2)).toBeFalsy()
  268. })
  269. it("returns a message for invalid input'", () => {
  270. expect(validateMinLength("", 1)).toEqual("Value must be at least 1 character")
  271. expect(validateMinLength("abc", 5)).toEqual("Value must be at least 5 characters")
  272. expect(validateMinLength("abc", 8)).toEqual("Value must be at least 8 characters")
  273. })
  274. })
  275. describe("validatePattern", () => {
  276. let rxPattern = "^(red|blue)"
  277. let errorMessage = "Value must follow pattern " + rxPattern
  278. it("doesn't return for a match", () => {
  279. expect(validatePattern("red", rxPattern)).toBeFalsy()
  280. expect(validatePattern("blue", rxPattern)).toBeFalsy()
  281. })
  282. it("returns a message for invalid pattern", () => {
  283. expect(validatePattern("pink", rxPattern)).toEqual(errorMessage)
  284. expect(validatePattern("123", rxPattern)).toEqual(errorMessage)
  285. })
  286. it("fails gracefully when an invalid regex value is passed", () => {
  287. expect(() => validatePattern("aValue", "---")).not.toThrow()
  288. expect(() => validatePattern("aValue", 1234)).not.toThrow()
  289. expect(() => validatePattern("aValue", null)).not.toThrow()
  290. expect(() => validatePattern("aValue", [])).not.toThrow()
  291. })
  292. })
  293. describe("validateParam", () => {
  294. let param = null
  295. let value = null
  296. let result = null
  297. const assertValidateParam = (param, value, expectedError) => {
  298. // Swagger 2.0 version
  299. result = validateParam( fromJS(param), fromJS(value))
  300. expect( result ).toEqual( expectedError )
  301. // OAS3 version, using `schema` sub-object
  302. let oas3Param = {
  303. required: param.required,
  304. schema: {
  305. ...param,
  306. required: undefined
  307. }
  308. }
  309. result = validateParam( fromJS(oas3Param), fromJS(value), {
  310. isOAS3: true
  311. })
  312. expect( result ).toEqual( expectedError )
  313. }
  314. const assertValidateOas3Param = (param, value, expectedError) => {
  315. // for cases where you _only_ want to try OAS3
  316. result = validateParam(fromJS(param), value, {
  317. isOAS3: true
  318. })
  319. expect( result ).toEqual( expectedError )
  320. }
  321. it("should check the isOAS3 flag when validating parameters", () => {
  322. // This should "skip" validation because there is no `schema` property
  323. // and we are telling `validateParam` this is an OAS3 spec
  324. param = fromJS({
  325. required: true
  326. })
  327. value = ""
  328. result = validateParam( param, value, {
  329. isOAS3: true
  330. } )
  331. expect( result ).toEqual( [] )
  332. })
  333. it("validates required OAS3 objects", () => {
  334. // valid object
  335. param = {
  336. required: true,
  337. schema: {
  338. type: "object"
  339. }
  340. }
  341. value = {
  342. abc: 123
  343. }
  344. assertValidateOas3Param(param, value, [])
  345. // valid object-as-string
  346. param = {
  347. required: true,
  348. schema: {
  349. type: "object"
  350. }
  351. }
  352. value = JSON.stringify({
  353. abc: 123
  354. })
  355. assertValidateOas3Param(param, value, [])
  356. // invalid object-as-string
  357. param = {
  358. required: true,
  359. schema: {
  360. type: "object"
  361. }
  362. }
  363. value = "{{}"
  364. assertValidateOas3Param(param, value, ["Parameter string value must be valid JSON"])
  365. // missing when required
  366. param = {
  367. required: true,
  368. schema: {
  369. type: "object"
  370. },
  371. }
  372. value = undefined
  373. assertValidateOas3Param(param, value, ["Required field is not provided"])
  374. })
  375. it("validates optional OAS3 objects", () => {
  376. // valid object
  377. param = {
  378. schema: {
  379. type: "object"
  380. }
  381. }
  382. value = {
  383. abc: 123
  384. }
  385. assertValidateOas3Param(param, value, [])
  386. // valid object-as-string
  387. param = {
  388. schema: {
  389. type: "object"
  390. }
  391. }
  392. value = JSON.stringify({
  393. abc: 123
  394. })
  395. assertValidateOas3Param(param, value, [])
  396. // invalid object-as-string
  397. param = {
  398. schema: {
  399. type: "object"
  400. }
  401. }
  402. value = "{{}"
  403. assertValidateOas3Param(param, value, ["Parameter string value must be valid JSON"])
  404. // missing when not required
  405. param = {
  406. schema: {
  407. type: "object"
  408. },
  409. }
  410. value = undefined
  411. assertValidateOas3Param(param, value, [])
  412. })
  413. it("validates required strings", () => {
  414. // invalid string
  415. param = {
  416. required: true,
  417. type: "string"
  418. }
  419. value = ""
  420. assertValidateParam(param, value, ["Required field is not provided"])
  421. // valid string
  422. param = {
  423. required: true,
  424. type: "string"
  425. }
  426. value = "test string"
  427. assertValidateParam(param, value, [])
  428. // valid string with min and max length
  429. param = {
  430. required: true,
  431. type: "string",
  432. maxLength: 50,
  433. minLength: 1
  434. }
  435. value = "test string"
  436. assertValidateParam(param, value, [])
  437. })
  438. it("handles OAS3 `Parameter.content`", () => {
  439. // invalid string
  440. param = {
  441. content: {
  442. "text/plain": {
  443. schema: {
  444. required: true,
  445. type: "string"
  446. }
  447. }
  448. }
  449. }
  450. value = ""
  451. assertValidateOas3Param(param, value, ["Required field is not provided"])
  452. // valid string
  453. param = {
  454. content: {
  455. "text/plain": {
  456. schema: {
  457. required: true,
  458. type: "string"
  459. }
  460. }
  461. }
  462. }
  463. value = "test string"
  464. assertValidateOas3Param(param, value, [])
  465. // invalid (empty) JSON string
  466. param = {
  467. content: {
  468. "application/json": {
  469. schema: {
  470. required: true,
  471. type: "object"
  472. }
  473. }
  474. }
  475. }
  476. value = ""
  477. assertValidateOas3Param(param, value, ["Required field is not provided"])
  478. // invalid (malformed) JSON string
  479. param = {
  480. content: {
  481. "application/json": {
  482. schema: {
  483. required: true,
  484. type: "object"
  485. }
  486. }
  487. }
  488. }
  489. value = "{{}"
  490. assertValidateOas3Param(param, value, ["Parameter string value must be valid JSON"])
  491. // valid (empty object) JSON string
  492. param = {
  493. content: {
  494. "application/json": {
  495. schema: {
  496. required: true,
  497. type: "object"
  498. }
  499. }
  500. }
  501. }
  502. value = "{}"
  503. assertValidateOas3Param(param, value, [])
  504. // valid (empty object) JSON object
  505. param = {
  506. content: {
  507. "application/json": {
  508. schema: {
  509. required: true,
  510. type: "object"
  511. }
  512. }
  513. }
  514. }
  515. value = {}
  516. assertValidateOas3Param(param, value, [])
  517. // should skip JSON validation for non-JSON media types
  518. param = {
  519. content: {
  520. "application/definitely-not-json": {
  521. schema: {
  522. required: true,
  523. type: "object"
  524. }
  525. }
  526. }
  527. }
  528. value = "{{}"
  529. assertValidateOas3Param(param, value, [])
  530. })
  531. it("validates required strings with min and max length", () => {
  532. // invalid string with max length
  533. param = {
  534. required: true,
  535. type: "string",
  536. maxLength: 5
  537. }
  538. value = "test string"
  539. assertValidateParam(param, value, ["Value must be no longer than 5 characters"])
  540. // invalid string with max length 0
  541. param = {
  542. required: true,
  543. type: "string",
  544. maxLength: 0
  545. }
  546. value = "test string"
  547. assertValidateParam(param, value, ["Value must be no longer than 0 characters"])
  548. // invalid string with min length
  549. param = {
  550. required: true,
  551. type: "string",
  552. minLength: 50
  553. }
  554. value = "test string"
  555. assertValidateParam(param, value, ["Value must be at least 50 characters"])
  556. })
  557. it("validates optional strings", () => {
  558. // valid (empty) string
  559. param = {
  560. required: false,
  561. type: "string"
  562. }
  563. value = ""
  564. assertValidateParam(param, value, [])
  565. // valid string
  566. param = {
  567. required: false,
  568. type: "string"
  569. }
  570. value = "test"
  571. assertValidateParam(param, value, [])
  572. })
  573. it("validates required files", () => {
  574. // invalid file
  575. param = {
  576. required: true,
  577. type: "file"
  578. }
  579. value = undefined
  580. assertValidateParam(param, value, ["Required field is not provided"])
  581. // valid file
  582. param = {
  583. required: true,
  584. type: "file"
  585. }
  586. value = new win.File([""], "file.txt")
  587. assertValidateParam(param, value, [])
  588. })
  589. it("validates optional files", () => {
  590. // invalid file
  591. param = {
  592. required: false,
  593. type: "file"
  594. }
  595. value = "not a file"
  596. assertValidateParam(param, value, ["Value must be a file"])
  597. // valid (empty) file
  598. param = {
  599. required: false,
  600. type: "file"
  601. }
  602. value = undefined
  603. assertValidateParam(param, value, [])
  604. // valid file
  605. param = {
  606. required: false,
  607. type: "file"
  608. }
  609. value = new win.File([""], "file.txt")
  610. assertValidateParam(param, value, [])
  611. })
  612. it("validates required arrays", () => {
  613. // invalid (empty) array
  614. param = {
  615. required: true,
  616. type: "array"
  617. }
  618. value = []
  619. assertValidateParam(param, value, ["Required field is not provided"])
  620. // invalid (empty) array, represented as a string
  621. param = {
  622. required: true,
  623. type: "array"
  624. }
  625. value = ""
  626. assertValidateParam(param, value, ["Required field is not provided"])
  627. // invalid (not an array)
  628. param = {
  629. required: true,
  630. type: "array"
  631. }
  632. value = undefined
  633. assertValidateParam(param, value, ["Required field is not provided"])
  634. // invalid array, items do not match correct type
  635. param = {
  636. required: true,
  637. type: "array",
  638. items: {
  639. type: "string"
  640. }
  641. }
  642. value = [1]
  643. assertValidateParam(param, value, [{index: 0, error: "Value must be a string"}])
  644. // valid array, with no 'type' for items
  645. param = {
  646. required: true,
  647. type: "array"
  648. }
  649. value = [1]
  650. assertValidateParam(param, value, [])
  651. // valid array, with no 'type' for items, represented as a string
  652. param = {
  653. required: true,
  654. type: "array"
  655. }
  656. value = "[1]"
  657. assertValidateParam(param, value, [])
  658. // valid array, items match type
  659. param = {
  660. required: true,
  661. type: "array",
  662. items: {
  663. type: "string"
  664. }
  665. }
  666. value = ["1"]
  667. assertValidateParam(param, value, [])
  668. })
  669. it("validates optional arrays", () => {
  670. // valid, empty array
  671. param = {
  672. required: false,
  673. type: "array"
  674. }
  675. value = []
  676. assertValidateParam(param, value, [])
  677. // invalid, items do not match correct type
  678. param = {
  679. required: false,
  680. type: "array",
  681. items: {
  682. type: "number"
  683. }
  684. }
  685. value = ["number"]
  686. assertValidateParam(param, value, [{index: 0, error: "Value must be a number"}])
  687. // valid
  688. param = {
  689. required: false,
  690. type: "array",
  691. items: {
  692. type: "string"
  693. }
  694. }
  695. value = ["test"]
  696. assertValidateParam(param, value, [])
  697. })
  698. it("validates required booleans", () => {
  699. // invalid boolean value
  700. param = {
  701. required: true,
  702. type: "boolean"
  703. }
  704. value = undefined
  705. assertValidateParam(param, value, ["Required field is not provided"])
  706. // invalid boolean value (not a boolean)
  707. param = {
  708. required: true,
  709. type: "boolean"
  710. }
  711. value = "test string"
  712. assertValidateParam(param, value, ["Value must be a boolean"])
  713. // valid boolean value
  714. param = {
  715. required: true,
  716. type: "boolean"
  717. }
  718. value = "true"
  719. assertValidateParam(param, value, [])
  720. // valid boolean value
  721. param = {
  722. required: true,
  723. type: "boolean"
  724. }
  725. value = false
  726. assertValidateParam(param, value, [])
  727. })
  728. it("validates optional booleans", () => {
  729. // valid (empty) boolean value
  730. param = {
  731. required: false,
  732. type: "boolean"
  733. }
  734. value = undefined
  735. assertValidateParam(param, value, [])
  736. // invalid boolean value (not a boolean)
  737. param = {
  738. required: false,
  739. type: "boolean"
  740. }
  741. value = "test string"
  742. assertValidateParam(param, value, ["Value must be a boolean"])
  743. // valid boolean value
  744. param = {
  745. required: false,
  746. type: "boolean"
  747. }
  748. value = "true"
  749. assertValidateParam(param, value, [])
  750. // valid boolean value
  751. param = {
  752. required: false,
  753. type: "boolean"
  754. }
  755. value = false
  756. assertValidateParam(param, value, [])
  757. })
  758. it("validates required numbers", () => {
  759. // invalid number, string instead of a number
  760. param = {
  761. required: true,
  762. type: "number"
  763. }
  764. value = "test"
  765. assertValidateParam(param, value, ["Value must be a number"])
  766. // invalid number, undefined value
  767. param = {
  768. required: true,
  769. type: "number"
  770. }
  771. value = undefined
  772. assertValidateParam(param, value, ["Required field is not provided"])
  773. // valid number with min and max
  774. param = {
  775. required: true,
  776. type: "number",
  777. minimum: 5,
  778. maximum: 99
  779. }
  780. value = 10
  781. assertValidateParam(param, value, [])
  782. // valid negative number with min and max
  783. param = {
  784. required: true,
  785. type: "number",
  786. minimum: -50,
  787. maximum: -5
  788. }
  789. value = -10
  790. assertValidateParam(param, value, [])
  791. // invalid number with maximum:0
  792. param = {
  793. required: true,
  794. type: "number",
  795. maximum: 0
  796. }
  797. value = 1
  798. assertValidateParam(param, value, ["Value must be less than 0"])
  799. // invalid number with minimum:0
  800. param = {
  801. required: true,
  802. type: "number",
  803. minimum: 0
  804. }
  805. value = -10
  806. assertValidateParam(param, value, ["Value must be greater than 0"])
  807. })
  808. it("validates optional numbers", () => {
  809. // invalid number, string instead of a number
  810. param = {
  811. required: false,
  812. type: "number"
  813. }
  814. value = "test"
  815. assertValidateParam(param, value, ["Value must be a number"])
  816. // valid (empty) number
  817. param = {
  818. required: false,
  819. type: "number"
  820. }
  821. value = undefined
  822. assertValidateParam(param, value, [])
  823. // valid number
  824. param = {
  825. required: false,
  826. type: "number"
  827. }
  828. value = 10
  829. assertValidateParam(param, value, [])
  830. })
  831. it("validates required integers", () => {
  832. // invalid integer, string instead of an integer
  833. param = {
  834. required: true,
  835. type: "integer"
  836. }
  837. value = "test"
  838. assertValidateParam(param, value, ["Value must be an integer"])
  839. // invalid integer, undefined value
  840. param = {
  841. required: true,
  842. type: "integer"
  843. }
  844. value = undefined
  845. assertValidateParam(param, value, ["Required field is not provided"])
  846. // valid integer, but 0 is falsy in JS
  847. param = {
  848. required: true,
  849. type: "integer"
  850. }
  851. value = 0
  852. assertValidateParam(param, value, [])
  853. // valid integer
  854. param = {
  855. required: true,
  856. type: "integer"
  857. }
  858. value = 10
  859. assertValidateParam(param, value, [])
  860. })
  861. it("validates optional integers", () => {
  862. // invalid integer, string instead of an integer
  863. param = {
  864. required: false,
  865. type: "integer"
  866. }
  867. value = "test"
  868. assertValidateParam(param, value, ["Value must be an integer"])
  869. // valid (empty) integer
  870. param = {
  871. required: false,
  872. type: "integer"
  873. }
  874. value = undefined
  875. assertValidateParam(param, value, [])
  876. // integers
  877. param = {
  878. required: false,
  879. type: "integer"
  880. }
  881. value = 10
  882. assertValidateParam(param, value, [])
  883. })
  884. })
  885. describe("fromJSOrdered", () => {
  886. it("should create an OrderedMap from an object", () => {
  887. const param = {
  888. value: "test"
  889. }
  890. const result = fromJSOrdered(param).toJS()
  891. expect( result ).toEqual( { value: "test" } )
  892. })
  893. it("should not use an object's length property for Map size", () => {
  894. const param = {
  895. length: 5
  896. }
  897. const result = fromJSOrdered(param).toJS()
  898. expect( result ).toEqual( { length: 5 } )
  899. })
  900. it("should create an OrderedMap from an array", () => {
  901. const param = [1, 1, 2, 3, 5, 8]
  902. const result = fromJSOrdered(param).toJS()
  903. expect( result ).toEqual( [1, 1, 2, 3, 5, 8] )
  904. })
  905. })
  906. describe("getAcceptControllingResponse", () => {
  907. it("should return the first 2xx response with a media type", () => {
  908. const responses = fromJSOrdered({
  909. "200": {
  910. content: {
  911. "application/json": {
  912. schema: {
  913. type: "object"
  914. }
  915. }
  916. }
  917. },
  918. "201": {
  919. content: {
  920. "application/json": {
  921. schema: {
  922. type: "object"
  923. }
  924. }
  925. }
  926. }
  927. })
  928. expect(getAcceptControllingResponse(responses)).toEqual(responses.get("200"))
  929. })
  930. it("should skip 2xx responses without defined media types", () => {
  931. const responses = fromJSOrdered({
  932. "200": {
  933. content: {
  934. "application/json": {
  935. schema: {
  936. type: "object"
  937. }
  938. }
  939. }
  940. },
  941. "201": {
  942. content: {
  943. "application/json": {
  944. schema: {
  945. type: "object"
  946. }
  947. }
  948. }
  949. }
  950. })
  951. expect(getAcceptControllingResponse(responses)).toEqual(responses.get("201"))
  952. })
  953. it("should default to the `default` response if it has defined media types", () => {
  954. const responses = fromJSOrdered({
  955. "200": {
  956. description: "quite empty"
  957. },
  958. "201": {
  959. description: "quite empty"
  960. },
  961. default: {
  962. content: {
  963. "application/json": {
  964. schema: {
  965. type: "object"
  966. }
  967. }
  968. }
  969. }
  970. })
  971. expect(getAcceptControllingResponse(responses)).toEqual(responses.get("default"))
  972. })
  973. it("should return null if there are no suitable controlling responses", () => {
  974. const responses = fromJSOrdered({
  975. "200": {
  976. description: "quite empty"
  977. },
  978. "201": {
  979. description: "quite empty"
  980. },
  981. "default": {
  982. description: "also empty.."
  983. }
  984. })
  985. expect(getAcceptControllingResponse(responses)).toBe(null)
  986. })
  987. it("should return null if an empty OrderedMap is passed", () => {
  988. const responses = fromJSOrdered()
  989. expect(getAcceptControllingResponse(responses)).toBe(null)
  990. })
  991. it("should return null if anything except an OrderedMap is passed", () => {
  992. const responses = {}
  993. expect(getAcceptControllingResponse(responses)).toBe(null)
  994. })
  995. })
  996. describe("createDeepLinkPath", () => {
  997. it("creates a deep link path replacing spaces with underscores", () => {
  998. const result = createDeepLinkPath("tag id with spaces")
  999. expect(result).toEqual("tag%20id%20with%20spaces")
  1000. })
  1001. it("trims input when creating a deep link path", () => {
  1002. let result = createDeepLinkPath(" spaces before and after ")
  1003. expect(result).toEqual("spaces%20before%20and%20after")
  1004. result = createDeepLinkPath(" ")
  1005. expect(result).toEqual("")
  1006. })
  1007. it("creates a deep link path with special characters", () => {
  1008. const result = createDeepLinkPath("!@#$%^&*(){}[]")
  1009. expect(result).toEqual("!@#$%^&*(){}[]")
  1010. })
  1011. it("returns an empty string for invalid input", () => {
  1012. expect( createDeepLinkPath(null) ).toEqual("")
  1013. expect( createDeepLinkPath(undefined) ).toEqual("")
  1014. expect( createDeepLinkPath(1) ).toEqual("")
  1015. expect( createDeepLinkPath([]) ).toEqual("")
  1016. expect( createDeepLinkPath({}) ).toEqual("")
  1017. })
  1018. })
  1019. describe("escapeDeepLinkPath", () => {
  1020. it("creates and escapes a deep link path", () => {
  1021. const result = escapeDeepLinkPath("tag id with spaces?")
  1022. expect(result).toEqual("tag_id_with_spaces\\?")
  1023. })
  1024. it("escapes a deep link path that starts with a number", () => {
  1025. const result = escapeDeepLinkPath("123")
  1026. expect(result).toEqual("\\31 23")
  1027. })
  1028. it("escapes a deep link path with a class selector", () => {
  1029. const result = escapeDeepLinkPath("hello.world")
  1030. expect(result).toEqual("hello\\.world")
  1031. })
  1032. it("escapes a deep link path with an id selector", () => {
  1033. const result = escapeDeepLinkPath("hello#world")
  1034. expect(result).toEqual("hello\\#world")
  1035. })
  1036. it("escapes a deep link path with a space", () => {
  1037. const result = escapeDeepLinkPath("hello world")
  1038. expect(result).toEqual("hello_world")
  1039. })
  1040. it("escapes a deep link path with a percent-encoded space", () => {
  1041. const result = escapeDeepLinkPath("hello%20world")
  1042. expect(result).toEqual("hello_world")
  1043. })
  1044. })
  1045. describe("getExtensions", () => {
  1046. const objTest = Map([[ "x-test", "a"], ["minimum", "b"]])
  1047. it("does not error on empty array", () => {
  1048. const result1 = getExtensions([])
  1049. expect(result1).toEqual([])
  1050. const result2 = getCommonExtensions([])
  1051. expect(result2).toEqual([])
  1052. })
  1053. it("gets only the x- keys", () => {
  1054. const result = getExtensions(objTest)
  1055. expect(result).toEqual(Map([[ "x-test", "a"]]))
  1056. })
  1057. it("gets the common keys", () => {
  1058. const result = getCommonExtensions(objTest, true)
  1059. expect(result).toEqual(Map([[ "minimum", "b"]]))
  1060. })
  1061. })
  1062. describe("deeplyStripKey", () => {
  1063. it("should filter out a specified key", () => {
  1064. const input = {
  1065. $$ref: "#/this/is/my/ref",
  1066. a: {
  1067. $$ref: "#/this/is/my/other/ref",
  1068. value: 12345
  1069. }
  1070. }
  1071. const result = deeplyStripKey(input, "$$ref")
  1072. expect(result).toEqual({
  1073. a: {
  1074. value: 12345
  1075. }
  1076. })
  1077. })
  1078. it("should filter out a specified key by predicate", () => {
  1079. const input = {
  1080. $$ref: "#/this/is/my/ref",
  1081. a: {
  1082. $$ref: "#/keep/this/one",
  1083. value: 12345
  1084. }
  1085. }
  1086. const result = deeplyStripKey(input, "$$ref", (v) => v !== "#/keep/this/one")
  1087. expect(result).toEqual({
  1088. a: {
  1089. value: 12345,
  1090. $$ref: "#/keep/this/one"
  1091. }
  1092. })
  1093. })
  1094. it("should only call the predicate when the key matches", () => {
  1095. const input = {
  1096. $$ref: "#/this/is/my/ref",
  1097. a: {
  1098. $$ref: "#/this/is/my/other/ref",
  1099. value: 12345
  1100. }
  1101. }
  1102. let count = 0
  1103. const result = deeplyStripKey(input, "$$ref", () => {
  1104. count++
  1105. return true
  1106. })
  1107. expect(count).toEqual(2)
  1108. })
  1109. })
  1110. describe("parse and serialize search", () => {
  1111. beforeEach(() => {
  1112. // jsdom in Jest 25+ prevents modifying window.location,
  1113. // so we replace with a stubbed version
  1114. delete win.location
  1115. win.location = {
  1116. search: ""
  1117. }
  1118. })
  1119. afterEach(() => {
  1120. win.location.search = ""
  1121. })
  1122. describe("parsing", () => {
  1123. it("works with empty search", () => {
  1124. win.location.search = ""
  1125. expect(parseSearch()).toEqual({})
  1126. })
  1127. it("works with only one key", () => {
  1128. win.location.search = "?foo"
  1129. expect(parseSearch()).toEqual({foo: ""})
  1130. })
  1131. it("works with keys and values", () => {
  1132. win.location.search = "?foo=fooval&bar&baz=bazval"
  1133. expect(parseSearch()).toEqual({foo: "fooval", bar: "", baz: "bazval"})
  1134. })
  1135. it("decode url encoded components", () => {
  1136. win.location.search = "?foo=foo%20bar"
  1137. expect(parseSearch()).toEqual({foo: "foo bar"})
  1138. })
  1139. })
  1140. describe("serializing", () => {
  1141. it("works with empty map", () => {
  1142. expect(serializeSearch({})).toEqual("")
  1143. })
  1144. it("works with multiple keys with and without values", () => {
  1145. expect(serializeSearch({foo: "", bar: "barval"})).toEqual("foo=&bar=barval")
  1146. })
  1147. it("encode url components", () => {
  1148. expect(serializeSearch({foo: "foo bar"})).toEqual("foo=foo%20bar")
  1149. })
  1150. })
  1151. })
  1152. describe("sanitizeUrl", () => {
  1153. it("should sanitize a `javascript:` url", () => {
  1154. const res = sanitizeUrl("javascript:alert('bam!')")
  1155. expect(res).toEqual("about:blank")
  1156. })
  1157. it("should sanitize a `data:` url", () => {
  1158. const res = sanitizeUrl(`data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=`)
  1159. expect(res).toEqual("about:blank")
  1160. })
  1161. it("should not modify a `http:` url", () => {
  1162. const res = sanitizeUrl(`http://swagger.io/`)
  1163. expect(res).toEqual("http://swagger.io/")
  1164. })
  1165. it("should not modify a `https:` url", () => {
  1166. const res = sanitizeUrl(`https://swagger.io/`)
  1167. expect(res).toEqual("https://swagger.io/")
  1168. })
  1169. it("should gracefully handle empty strings", () => {
  1170. expect(sanitizeUrl("")).toEqual("")
  1171. })
  1172. it("should gracefully handle non-string values", () => {
  1173. expect(sanitizeUrl(123)).toEqual("")
  1174. expect(sanitizeUrl(null)).toEqual("")
  1175. expect(sanitizeUrl(undefined)).toEqual("")
  1176. expect(sanitizeUrl([])).toEqual("")
  1177. expect(sanitizeUrl({})).toEqual("")
  1178. })
  1179. })
  1180. describe("isAbsoluteUrl", () => {
  1181. it("check if url is absolute", () => {
  1182. expect(!!isAbsoluteUrl("http://example.com")).toEqual(true)
  1183. expect(!!isAbsoluteUrl("https://secure-example.com")).toEqual(true)
  1184. expect(!!isAbsoluteUrl("HTTP://uppercase-example.com")).toEqual(true)
  1185. expect(!!isAbsoluteUrl("HTTP://uppercase-secure-example.com")).toEqual(true)
  1186. expect(!!isAbsoluteUrl("http://trailing-slash.com/")).toEqual(true)
  1187. expect(!!isAbsoluteUrl("ftp://file-transfer-protocol.com")).toEqual(true)
  1188. expect(!!isAbsoluteUrl("//no-protocol.com")).toEqual(true)
  1189. })
  1190. it("check if url is not absolute", () => {
  1191. expect(!!isAbsoluteUrl("/url-relative-to-host/base-path/path")).toEqual(false)
  1192. expect(!!isAbsoluteUrl("url-relative-to-base/base-path/path")).toEqual(false)
  1193. })
  1194. })
  1195. describe("buildBaseUrl", () => {
  1196. const specUrl = "https://petstore.swagger.io/v2/swagger.json"
  1197. const noServerSelected = ""
  1198. const absoluteServerUrl = "https://server-example.com/base-path/path"
  1199. const serverUrlRelativeToBase = "server-example/base-path/path"
  1200. const serverUrlRelativeToHost = "/server-example/base-path/path"
  1201. it("build base url with no server selected", () => {
  1202. expect(buildBaseUrl(noServerSelected, specUrl)).toBe("https://petstore.swagger.io/v2/swagger.json")
  1203. })
  1204. it("build base url from absolute server url", () => {
  1205. expect(buildBaseUrl(absoluteServerUrl, specUrl)).toBe("https://server-example.com/base-path/path")
  1206. })
  1207. it("build base url from relative server url", () => {
  1208. expect(buildBaseUrl(serverUrlRelativeToBase, specUrl)).toBe("https://petstore.swagger.io/v2/server-example/base-path/path")
  1209. expect(buildBaseUrl(serverUrlRelativeToHost, specUrl)).toBe("https://petstore.swagger.io/server-example/base-path/path")
  1210. })
  1211. })
  1212. describe("buildUrl", () => {
  1213. const specUrl = "https://petstore.swagger.io/v2/swagger.json"
  1214. const noUrl = ""
  1215. const absoluteUrl = "https://example.com/base-path/path"
  1216. const urlRelativeToBase = "relative-url/base-path/path"
  1217. const urlRelativeToHost = "/relative-url/base-path/path"
  1218. const noServerSelected = ""
  1219. const absoluteServerUrl = "https://server-example.com/base-path/path"
  1220. const serverUrlRelativeToBase = "server-example/base-path/path"
  1221. const serverUrlRelativeToHost = "/server-example/base-path/path"
  1222. it("build no url", () => {
  1223. expect(buildUrl(noUrl, specUrl, { selectedServer: absoluteServerUrl })).toBe(undefined)
  1224. expect(buildUrl(noUrl, specUrl, { selectedServer: serverUrlRelativeToBase })).toBe(undefined)
  1225. expect(buildUrl(noUrl, specUrl, { selectedServer: serverUrlRelativeToHost })).toBe(undefined)
  1226. })
  1227. it("build absolute url", () => {
  1228. expect(buildUrl(absoluteUrl, specUrl, { selectedServer: absoluteServerUrl })).toBe("https://example.com/base-path/path")
  1229. expect(buildUrl(absoluteUrl, specUrl, { selectedServer: serverUrlRelativeToBase })).toBe("https://example.com/base-path/path")
  1230. expect(buildUrl(absoluteUrl, specUrl, { selectedServer: serverUrlRelativeToHost })).toBe("https://example.com/base-path/path")
  1231. })
  1232. it("build relative url with no server selected", () => {
  1233. expect(buildUrl(urlRelativeToBase, specUrl, { selectedServer: noServerSelected })).toBe("https://petstore.swagger.io/v2/relative-url/base-path/path")
  1234. expect(buildUrl(urlRelativeToHost, specUrl, { selectedServer: noServerSelected })).toBe("https://petstore.swagger.io/relative-url/base-path/path")
  1235. })
  1236. it("build relative url with absolute server url", () => {
  1237. expect(buildUrl(urlRelativeToBase, specUrl, { selectedServer: absoluteServerUrl })).toBe("https://server-example.com/base-path/relative-url/base-path/path")
  1238. expect(buildUrl(urlRelativeToHost, specUrl, { selectedServer: absoluteServerUrl })).toBe("https://server-example.com/relative-url/base-path/path")
  1239. })
  1240. it("build relative url with server url relative to base", () => {
  1241. expect(buildUrl(urlRelativeToBase, specUrl, { selectedServer: serverUrlRelativeToBase })).toBe("https://petstore.swagger.io/v2/server-example/base-path/relative-url/base-path/path")
  1242. expect(buildUrl(urlRelativeToHost, specUrl, { selectedServer: serverUrlRelativeToBase })).toBe("https://petstore.swagger.io/relative-url/base-path/path")
  1243. })
  1244. it("build relative url with server url relative to host", () => {
  1245. expect(buildUrl(urlRelativeToBase, specUrl, { selectedServer: serverUrlRelativeToHost })).toBe("https://petstore.swagger.io/server-example/base-path/relative-url/base-path/path")
  1246. expect(buildUrl(urlRelativeToHost, specUrl, { selectedServer: serverUrlRelativeToHost })).toBe("https://petstore.swagger.io/relative-url/base-path/path")
  1247. })
  1248. })
  1249. describe("requiresValidationURL", () => {
  1250. it("Should tell us if we require a ValidationURL", () => {
  1251. const res = requiresValidationURL("https://example.com")
  1252. expect(res).toBe(true)
  1253. })
  1254. it(".. and localhost is not", () => {
  1255. const res = requiresValidationURL("http://localhost")
  1256. expect(res).toBe(false)
  1257. })
  1258. it(".. and neither does 127.0.0.1", () => {
  1259. const res = requiresValidationURL("http://127.0.0.1")
  1260. expect(res).toBe(false)
  1261. })
  1262. it(".. even without the proto", () => {
  1263. const res = requiresValidationURL("127.0.0.1")
  1264. expect(res).toBe(false)
  1265. })
  1266. it(".. and also not with 'none'", () => {
  1267. const res = requiresValidationURL("none")
  1268. expect(res).toBe(false)
  1269. })
  1270. it(".. and also not with 'none'", () => {
  1271. const res = requiresValidationURL("none")
  1272. expect(res).toBe(false)
  1273. })
  1274. it(".. and also not with ''", () => {
  1275. const res = requiresValidationURL("")
  1276. expect(res).toBe(false)
  1277. })
  1278. })
  1279. describe("getSampleSchema", () => {
  1280. const oriDate = Date
  1281. beforeEach(() => {
  1282. Date = function () {
  1283. this.toISOString = function () {
  1284. return "2018-07-07T07:07:05.189Z"
  1285. }
  1286. }
  1287. })
  1288. afterEach(() => {
  1289. Date = oriDate
  1290. })
  1291. it("should stringify string values if json content-type", () => {
  1292. // Given
  1293. const res = getSampleSchema({
  1294. type: "string",
  1295. format: "date-time"
  1296. }, "text/json")
  1297. // Then
  1298. expect(res).toEqual(JSON.stringify(new Date().toISOString()))
  1299. })
  1300. it("should not unnecessarily stringify string values for other content-types", () => {
  1301. // Given
  1302. const res = getSampleSchema({
  1303. type: "string",
  1304. format: "date-time"
  1305. })
  1306. // Then
  1307. expect(res).toEqual(new Date().toISOString())
  1308. })
  1309. it("should not unnecessarily stringify non-object values", () => {
  1310. // Given
  1311. const res = getSampleSchema({
  1312. type: "number"
  1313. })
  1314. // Then
  1315. expect(res).toEqual(0)
  1316. })
  1317. it("should not unnecessarily stringify non-object values if content-type is json", () => {
  1318. // Given
  1319. const res = getSampleSchema({
  1320. type: "number"
  1321. }, "application/json")
  1322. // Then
  1323. expect(res).toEqual(0)
  1324. })
  1325. })
  1326. describe("paramToIdentifier", () => {
  1327. it("should convert an Immutable parameter map to an identifier", () => {
  1328. const param = fromJS({
  1329. name: "id",
  1330. in: "query"
  1331. })
  1332. const res = paramToIdentifier(param)
  1333. expect(res).toEqual("query.id.hash-606199662")
  1334. })
  1335. it("should convert an Immutable parameter map to a set of identifiers", () => {
  1336. const param = fromJS({
  1337. name: "id",
  1338. in: "query"
  1339. })
  1340. const res = paramToIdentifier(param, { returnAll: true })
  1341. expect(res).toEqual([
  1342. "query.id.hash-606199662",
  1343. "query.id",
  1344. "id"
  1345. ])
  1346. })
  1347. it("should convert an unhashable Immutable parameter map to an identifier", () => {
  1348. const param = fromJS({
  1349. name: "id",
  1350. in: "query"
  1351. })
  1352. param.hashCode = null
  1353. const res = paramToIdentifier(param)
  1354. expect(res).toEqual("query.id")
  1355. })
  1356. it("should convert an unhashable Immutable parameter map to a set of identifiers", () => {
  1357. const param = fromJS({
  1358. name: "id",
  1359. in: "query"
  1360. })
  1361. param.hashCode = null
  1362. const res = paramToIdentifier(param, { returnAll: true })
  1363. expect(res).toEqual([
  1364. "query.id",
  1365. "id"
  1366. ])
  1367. })
  1368. it("should convert an Immutable parameter map lacking an `in` value to an identifier", () => {
  1369. const param = fromJS({
  1370. name: "id"
  1371. })
  1372. const res = paramToIdentifier(param)
  1373. expect(res).toEqual("id")
  1374. })
  1375. it("should convert an Immutable parameter map lacking an `in` value to an identifier", () => {
  1376. const param = fromJS({
  1377. name: "id"
  1378. })
  1379. const res = paramToIdentifier(param, { returnAll: true })
  1380. expect(res).toEqual(["id"])
  1381. })
  1382. it("should throw gracefully when given a non-Immutable parameter input", () => {
  1383. const param = {
  1384. name: "id"
  1385. }
  1386. let error = null
  1387. let res = null
  1388. try {
  1389. const res = paramToIdentifier(param)
  1390. } catch(e) {
  1391. error = e
  1392. }
  1393. expect(error).toBeInstanceOf(Error)
  1394. expect(error.message).toContain("received a non-Im.Map parameter as input")
  1395. expect(res).toEqual(null)
  1396. })
  1397. })
  1398. describe("paramToValue", () => {
  1399. it("should identify a hash-keyed value", () => {
  1400. const param = fromJS({
  1401. name: "id",
  1402. in: "query"
  1403. })
  1404. const paramValues = {
  1405. "query.id.hash-606199662": "asdf"
  1406. }
  1407. const res = paramToValue(param, paramValues)
  1408. expect(res).toEqual("asdf")
  1409. })
  1410. it("should identify a in+name value", () => {
  1411. const param = fromJS({
  1412. name: "id",
  1413. in: "query"
  1414. })
  1415. const paramValues = {
  1416. "query.id": "asdf"
  1417. }
  1418. const res = paramToValue(param, paramValues)
  1419. expect(res).toEqual("asdf")
  1420. })
  1421. it("should identify a name value", () => {
  1422. const param = fromJS({
  1423. name: "id",
  1424. in: "query"
  1425. })
  1426. const paramValues = {
  1427. "id": "asdf"
  1428. }
  1429. const res = paramToValue(param, paramValues)
  1430. expect(res).toEqual("asdf")
  1431. })
  1432. })
  1433. describe("generateCodeVerifier", () => {
  1434. it("should generate a value of at least 43 characters", () => {
  1435. const codeVerifier = generateCodeVerifier()
  1436. // Source: https://tools.ietf.org/html/rfc7636#section-4.1
  1437. expect(codeVerifier.length).toBeGreaterThanOrEqual(43)
  1438. })
  1439. })
  1440. describe("createCodeChallenge", () => {
  1441. it("should hash the input using SHA256 and output the base64 url encoded value", () => {
  1442. // The `codeVerifier` has been randomly generated
  1443. const codeVerifier = "cY8OJ9MKvZ7hxQeIyRYD7KFmKA5znSFJ2rELysvy2UI"
  1444. // This value is the `codeVerifier` hashed using SHA256, which has been
  1445. // encoded using base64 url format.
  1446. // Source: https://tools.ietf.org/html/rfc7636#section-4.2
  1447. const expectedCodeChallenge = "LD9lx2p2PbvGkojuJy7-Elex7RnckzmqR7oIXjd4u84"
  1448. expect(createCodeChallenge(codeVerifier)).toBe(expectedCodeChallenge)
  1449. })
  1450. })
  1451. })