123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836
  1. /*jshint maxlen:80*/
  2. /*global reqwest:true, sink:true, start:true, ender:true, v:true, boosh:true*/
  3. (function (ajax) {
  4. var BIND_ARGS = 'bind'
  5. , PASS_ARGS = 'pass'
  6. , FakeXHR = (function () {
  7. function FakeXHR () {
  8. this.args = {}
  9. FakeXHR.last = this
  10. }
  11. FakeXHR.setup = function () {
  12. FakeXHR.oldxhr = window['XMLHttpRequest']
  13. FakeXHR.oldaxo = window['ActiveXObject']
  14. window['XMLHttpRequest'] = FakeXHR
  15. window['ActiveXObject'] = FakeXHR
  16. FakeXHR.last = null
  17. }
  18. FakeXHR.restore = function () {
  19. window['XMLHttpRequest'] = FakeXHR.oldxhr
  20. window['ActiveXObject'] = FakeXHR.oldaxo
  21. }
  22. FakeXHR.prototype.methodCallCount = function (name) {
  23. return this.args[name] ? this.args[name].length : 0
  24. }
  25. FakeXHR.prototype.methodCallArgs = function (name, i, j) {
  26. var a = this.args[name]
  27. && this.args[name].length > i ? this.args[name][i] : null
  28. if (arguments.length > 2) return a && a.length > j ? a[j] : null
  29. return a
  30. }
  31. v.each(['open', 'send', 'setRequestHeader' ], function (f) {
  32. FakeXHR.prototype[f] = function () {
  33. if (!this.args[f]) this.args[f] = []
  34. this.args[f].push(arguments)
  35. }
  36. })
  37. return FakeXHR
  38. }())
  39. sink('Setup', function (test, ok, before, after) {
  40. before(function () {
  41. ajax.ajaxSetup({
  42. dataFilter: function (resp, type) {
  43. // example filter to prevent json hijacking
  44. return resp.substring('])}while(1);</x>'.length)
  45. }
  46. })
  47. })
  48. after(function () {
  49. ajax.ajaxSetup({
  50. // reset to original data filter
  51. dataFilter: function (resp, type) {
  52. return resp
  53. }
  54. })
  55. })
  56. test('dataFilter', function (complete) {
  57. ajax({
  58. url: '/tests/fixtures/fixtures_with_prefix.json'
  59. , type: 'json'
  60. , success: function (resp) {
  61. ok(resp, 'received response')
  62. ok(
  63. resp && resp.boosh == 'boosh'
  64. , 'correctly evaluated response as JSON'
  65. )
  66. complete()
  67. }
  68. })
  69. })
  70. })
  71. sink('Mime Types', function (test, ok) {
  72. test('JSON', function (complete) {
  73. ajax({
  74. url: '/tests/fixtures/fixtures.json'
  75. , type: 'json'
  76. , success: function (resp) {
  77. ok(resp, 'received response')
  78. ok(
  79. resp && resp.boosh == 'boosh'
  80. , 'correctly evaluated response as JSON'
  81. )
  82. complete()
  83. }
  84. })
  85. })
  86. test('JSONP', function (complete) {
  87. // stub callback prefix
  88. reqwest.getcallbackPrefix = function (id) {
  89. return 'reqwest_' + id
  90. }
  91. ajax({
  92. url: '/tests/fixtures/fixtures_jsonp.jsonp?callback=?'
  93. , type: 'jsonp'
  94. , success: function (resp) {
  95. ok(resp, 'received response for unique generated callback')
  96. ok(
  97. resp && resp.boosh == 'boosh'
  98. , 'correctly evaled response for unique generated cb as JSONP'
  99. )
  100. complete()
  101. }
  102. })
  103. })
  104. test('JS', function (complete) {
  105. ajax({
  106. url: '/tests/fixtures/fixtures.js'
  107. , type: 'js'
  108. , success: function () {
  109. ok(
  110. typeof boosh !== 'undefined' && boosh == 'boosh'
  111. , 'evaluated response as JavaScript'
  112. )
  113. complete()
  114. }
  115. })
  116. })
  117. test('HTML', function (complete) {
  118. ajax({
  119. url: '/tests/fixtures/fixtures.html'
  120. , type: 'html'
  121. , success: function (resp) {
  122. ok(resp == '<p>boosh</p>', 'evaluated response as HTML')
  123. complete()
  124. }
  125. })
  126. })
  127. test('XML', function (complete) {
  128. ajax({
  129. url: '/tests/fixtures/fixtures.xml'
  130. , type: 'xml'
  131. , success: function (resp) {
  132. ok(resp
  133. && resp.documentElement
  134. && resp.documentElement.nodeName == 'root'
  135. , 'XML Response root is <root>'
  136. )
  137. ok(resp
  138. && resp.documentElement
  139. && resp.documentElement.hasChildNodes
  140. && resp.documentElement.firstChild.nodeName == 'boosh'
  141. && resp.documentElement.firstChild.firstChild.nodeValue
  142. == 'boosh'
  143. , 'Correct XML response'
  144. )
  145. complete()
  146. }
  147. , error: function (err) {
  148. ok(false, err.responseText)
  149. complete()
  150. }
  151. })
  152. })
  153. test('XML (404)', function (complete) {
  154. ajax({
  155. url:'/tests/fixtures/badfixtures.xml'
  156. , type:'xml'
  157. , success: function (resp) {
  158. if (resp == null) {
  159. ok(true, 'XML response is null')
  160. complete()
  161. } else {
  162. ok(resp
  163. && resp.documentElement
  164. && resp.documentElement.firstChild
  165. && (/error/i).test(resp.documentElement.firstChild.nodeValue)
  166. , 'XML response reports parsing error'
  167. )
  168. complete()
  169. }
  170. }
  171. , error: function () {
  172. ok(true, 'No XML response (error())')
  173. complete()
  174. }
  175. })
  176. })
  177. })
  178. sink('JSONP', function (test, ok) {
  179. test('Named callback in query string', function (complete) {
  180. ajax({
  181. url: '/tests/fixtures/fixtures_jsonp2.jsonp?foo=bar'
  182. , type: 'jsonp'
  183. , jsonpCallback: 'foo'
  184. , success: function (resp) {
  185. ok(resp, 'received response for custom callback')
  186. ok(
  187. resp && resp.boosh == 'boosh'
  188. , 'correctly evaluated response as JSONP with custom callback'
  189. )
  190. complete()
  191. }
  192. })
  193. })
  194. test('Unnamed callback in query string', function (complete) {
  195. ajax({
  196. url: '/tests/fixtures/fixtures_jsonp3.jsonp?foo=?'
  197. , type: 'jsonp'
  198. , jsonpCallback: 'foo'
  199. , success: function (resp) {
  200. ok(resp, 'received response for custom wildcard callback')
  201. ok(
  202. resp && resp.boosh == 'boosh'
  203. , 'correctly evaled response as JSONP with custom wildcard cb'
  204. )
  205. complete()
  206. }
  207. })
  208. })
  209. test('No callback, no query string', function (complete) {
  210. ajax({
  211. url: '/tests/fixtures/fixtures_jsonp3.jsonp'
  212. , type: 'jsonp'
  213. , jsonpCallback: 'foo'
  214. , success: function (resp) {
  215. ok(resp, 'received response for custom wildcard callback')
  216. ok(
  217. resp && resp.boosh == 'boosh'
  218. , 'correctly evaled response as JSONP with custom cb not in url'
  219. )
  220. complete()
  221. }
  222. })
  223. })
  224. test('No callback in existing query string', function (complete) {
  225. ajax({
  226. url: '/tests/none.jsonp?echo&somevar=some+long+str+here'
  227. , type: 'jsonp'
  228. , jsonpCallbackName: 'yohoho'
  229. , success: function (resp) {
  230. ok(resp && resp.query, 'received response from echo callback')
  231. ok(
  232. resp && resp.query && resp.query.somevar == 'some long str here'
  233. , 'correctly evaluated response as JSONP with echo callback'
  234. )
  235. complete()
  236. }
  237. })
  238. })
  239. test('Append data to existing query string', function (complete) {
  240. ajax({
  241. url: '/tests/none.jsonp?echo' // should append &somevar...
  242. , type: 'jsonp'
  243. , data: { somevar: 'some long str here', anothervar: 'yo ho ho!' }
  244. , success: function (resp) {
  245. ok(resp && resp.query, 'received response from echo callback')
  246. ok(
  247. resp && resp.query && resp.query.somevar == 'some long str here'
  248. , 'correctly sent and received data object from JSONP echo (1)'
  249. )
  250. ok(
  251. resp && resp.query && resp.query.anothervar == 'yo ho ho!'
  252. , 'correctly sent and received data object from JSONP echo (2)'
  253. )
  254. complete()
  255. }
  256. })
  257. })
  258. test('Generate complete query string from data', function (complete) {
  259. ajax({
  260. url: '/tests/none.jsonp' // should append ?echo...etc.
  261. , type: 'jsonp'
  262. , data: [
  263. { name: 'somevar', value: 'some long str here' }
  264. , { name: 'anothervar', value: 'yo ho ho!' }
  265. , { name: 'echo', value: true }
  266. ]
  267. , success: function (resp) {
  268. ok(resp && resp.query, 'received response from echo callback')
  269. ok(
  270. resp && resp.query && resp.query.somevar == 'some long str here'
  271. , 'correctly sent and received data array from JSONP echo (1)'
  272. )
  273. ok(
  274. resp && resp.query && resp.query.anothervar == 'yo ho ho!'
  275. , 'correctly sent and received data array from JSONP echo (2)'
  276. )
  277. complete()
  278. }
  279. })
  280. })
  281. test('Append data to query string and insert callback name'
  282. , function (complete) {
  283. ajax({
  284. // should append data and match callback correctly
  285. url: '/tests/none.jsonp?callback=?'
  286. , type: 'jsonp'
  287. , jsonpCallbackName: 'reqwest_foo'
  288. , data: { foo: 'bar', boo: 'baz', echo: true }
  289. , success: function (resp) {
  290. ok(resp && resp.query, 'received response from echo callback')
  291. ok(
  292. resp && resp.query && resp.query.callback == 'reqwest_foo'
  293. , 'correctly matched callback in URL'
  294. )
  295. complete()
  296. }
  297. })
  298. })
  299. })
  300. sink('Callbacks', function (test, ok) {
  301. test('sync version', function (done) {
  302. var r = ajax({
  303. method: 'get'
  304. , url: '/tests/fixtures/fixtures.json'
  305. , type: 'json'
  306. , async: false
  307. })
  308. var request = r.request,
  309. responseText = request.response !== undefined ? request.response : request.responseText
  310. ok(eval('(' + responseText + ')').boosh == 'boosh', 'can make sync calls')
  311. done()
  312. })
  313. test('no callbacks', function (complete) {
  314. var pass = true
  315. try {
  316. ajax('/tests/fixtures/fixtures.js')
  317. } catch (ex) {
  318. pass = false
  319. } finally {
  320. ok(pass, 'successfully doesnt fail without callback')
  321. complete()
  322. }
  323. })
  324. test('complete is called', function (complete) {
  325. ajax({
  326. url: '/tests/fixtures/fixtures.js'
  327. , complete: function () {
  328. ok(true, 'called complete')
  329. complete()
  330. }
  331. })
  332. })
  333. test('invalid JSON sets error on resp object', function (complete) {
  334. ajax({
  335. url: '/tests/fixtures/invalidJSON.json'
  336. , type: 'json'
  337. , success: function () {
  338. ok(false, 'success callback fired')
  339. complete()
  340. }
  341. , error: function (resp, msg) {
  342. ok(
  343. msg == 'Could not parse JSON in response'
  344. , 'error callback fired'
  345. )
  346. complete()
  347. }
  348. })
  349. })
  350. test('multiple parallel named JSONP callbacks', 8, function () {
  351. ajax({
  352. url: '/tests/fixtures/fixtures_jsonp_multi.jsonp?callback=reqwest_0'
  353. , type: 'jsonp'
  354. , success: function (resp) {
  355. ok(resp, 'received response from call #1')
  356. ok(
  357. resp && resp.a == 'a'
  358. , 'evaluated response from call #1 as JSONP'
  359. )
  360. }
  361. })
  362. ajax({
  363. url: '/tests/fixtures/fixtures_jsonp_multi_b.jsonp?callback=reqwest_0'
  364. , type: 'jsonp'
  365. , success: function (resp) {
  366. ok(resp, 'received response from call #2')
  367. ok(
  368. resp && resp.b == 'b'
  369. , 'evaluated response from call #2 as JSONP'
  370. )
  371. }
  372. })
  373. ajax({
  374. url: '/tests/fixtures/fixtures_jsonp_multi_c.jsonp?callback=reqwest_0'
  375. , type: 'jsonp'
  376. , success: function (resp) {
  377. ok(resp, 'received response from call #2')
  378. ok(
  379. resp && resp.c == 'c'
  380. , 'evaluated response from call #3 as JSONP'
  381. )
  382. }
  383. })
  384. ajax({
  385. url: '/tests/fixtures/fixtures_jsonp_multi.jsonp?callback=reqwest_0'
  386. , type: 'jsonp'
  387. , success: function (resp) {
  388. ok(resp, 'received response from call #2')
  389. ok(
  390. resp && resp.a == 'a'
  391. , 'evaluated response from call #4 as JSONP'
  392. )
  393. }
  394. })
  395. })
  396. test('JSONP also supports success promises', function (complete) {
  397. ajax({
  398. url: '/tests/none.jsonp?echo'
  399. , type: 'jsonp'
  400. , success: function (resp) {
  401. ok(resp, 'received response in constructor success callback')
  402. }
  403. })
  404. .then(function (resp) {
  405. ok(resp, 'received response in promise success callback')
  406. return resp;
  407. })
  408. .then(function (resp) {
  409. ok(resp, 'received response in second promise success callback')
  410. complete()
  411. })
  412. })
  413. test('JSONP also supports error promises', function (complete) {
  414. ajax({
  415. url: '/tests/timeout/'
  416. , type: 'jsonp'
  417. , error: function (err) {
  418. ok(err, 'received error response in constructor error callback')
  419. }
  420. })
  421. .fail(function (err) {
  422. ok(err, 'received error response in promise error callback')
  423. })
  424. .fail(function (err) {
  425. ok(err, 'received error response in second promise error callback')
  426. complete()
  427. })
  428. .abort()
  429. })
  430. })
  431. if (window.XMLHttpRequest
  432. && ('withCredentials' in new window.XMLHttpRequest())) {
  433. sink('Cross-origin Resource Sharing', function (test, ok) {
  434. test('make request to another origin', 1, function () {
  435. ajax({
  436. url: 'http://' + window.location.hostname + ':5678/get-value'
  437. , type: 'text'
  438. , method: 'get'
  439. , crossOrigin: true
  440. , complete: function (resp) {
  441. ok(resp.responseText === 'hello', 'request made successfully')
  442. }
  443. })
  444. })
  445. test('set cookie on other origin', 2, function () {
  446. ajax({
  447. url: 'http://' + window.location.hostname + ':5678/set-cookie'
  448. , type: 'text'
  449. , method: 'get'
  450. , crossOrigin: true
  451. , withCredentials: true
  452. , before: function (http) {
  453. ok(
  454. http.withCredentials === true
  455. , 'has set withCredentials on connection object'
  456. )
  457. }
  458. , complete: function (resp) {
  459. ok(resp.status === 200, 'cookie set successfully')
  460. }
  461. })
  462. })
  463. test('get cookie from other origin', 1, function () {
  464. ajax({
  465. url: 'http://'
  466. + window.location.hostname
  467. + ':5678/get-cookie-value'
  468. , type: 'text'
  469. , method: 'get'
  470. , crossOrigin: true
  471. , withCredentials: true
  472. , complete: function (resp) {
  473. ok(
  474. resp.responseText == 'hello'
  475. , 'cookie value retrieved successfully'
  476. )
  477. }
  478. })
  479. })
  480. })
  481. }
  482. sink('Connection Object', function (test, ok) {
  483. test('use xhr factory provided in the options', function (complete) {
  484. var reqwest
  485. , xhr
  486. if (typeof XMLHttpRequest !== 'undefined') {
  487. xhr = new XMLHttpRequest()
  488. } else if (typeof ActiveXObject !== 'undefined') {
  489. xhr = new ActiveXObject('Microsoft.XMLHTTP')
  490. } else {
  491. ok(false, 'browser not supported')
  492. }
  493. reqwest = ajax({
  494. url: '/tests/fixtures/fixtures.html',
  495. xhr: function () {
  496. return xhr
  497. }
  498. })
  499. ok(reqwest.request === xhr, 'uses factory')
  500. complete()
  501. })
  502. test('fallbacks to own xhr factory if falsy is returned', function (complete) {
  503. var reqwest
  504. FakeXHR.setup()
  505. try {
  506. reqwest = ajax({
  507. url: '/tests/fixtures/fixtures.html',
  508. xhr: function () {
  509. return null
  510. }
  511. })
  512. ok(reqwest.request instanceof FakeXHR, 'fallbacks correctly')
  513. complete()
  514. } finally {
  515. FakeXHR.restore()
  516. }
  517. })
  518. test('setRequestHeaders', function (complete) {
  519. ajax({
  520. url: '/tests/fixtures/fixtures.html'
  521. , data: 'foo=bar&baz=thunk'
  522. , method: 'post'
  523. , headers: {
  524. 'Accept': 'application/x-foo'
  525. }
  526. , success: function () {
  527. ok(true, 'can post headers')
  528. complete()
  529. }
  530. })
  531. })
  532. test('can inspect http before send', function (complete) {
  533. var connection = ajax({
  534. url: '/tests/fixtures/fixtures.js'
  535. , method: 'post'
  536. , type: 'js'
  537. , before: function (http) {
  538. ok(http.readyState == 1, 'received http connection object')
  539. }
  540. , success: function () {
  541. // Microsoft.XMLHTTP appears not to run this async in IE6&7, it
  542. // processes the request and triggers success() before ajax() even
  543. // returns. Perhaps a better solution would be to defer the calls
  544. // within handleReadyState()
  545. setTimeout(function () {
  546. ok(
  547. connection.request.readyState == 4
  548. , 'success callback has readyState of 4'
  549. )
  550. complete()
  551. }, 0)
  552. }
  553. })
  554. })
  555. test('ajax() encodes array `data`', function (complete) {
  556. FakeXHR.setup()
  557. try {
  558. ajax({
  559. url: '/tests/fixtures/fixtures.html'
  560. , method: 'post'
  561. , data: [
  562. { name: 'foo', value: 'bar' }
  563. , { name: 'baz', value: 'thunk' }
  564. ]
  565. })
  566. ok(FakeXHR.last.methodCallCount('send') == 1, 'send called')
  567. ok(
  568. FakeXHR.last.methodCallArgs('send', 0).length == 1
  569. , 'send called with 1 arg'
  570. )
  571. ok(
  572. FakeXHR.last.methodCallArgs('send', 0, 0) == 'foo=bar&baz=thunk'
  573. , 'send called with encoded array'
  574. )
  575. complete()
  576. } finally {
  577. FakeXHR.restore()
  578. }
  579. })
  580. test('ajax() encodes hash `data`', function (complete) {
  581. FakeXHR.setup()
  582. try {
  583. ajax({
  584. url: '/tests/fixtures/fixtures.html'
  585. , method: 'post'
  586. , data: { bar: 'foo', thunk: 'baz' }
  587. })
  588. ok(FakeXHR.last.methodCallCount('send') == 1, 'send called')
  589. ok(
  590. FakeXHR.last.methodCallArgs('send', 0).length == 1
  591. , 'send called with 1 arg'
  592. )
  593. ok(
  594. FakeXHR.last.methodCallArgs('send', 0, 0) == 'bar=foo&thunk=baz'
  595. , 'send called with encoded array'
  596. )
  597. complete()
  598. } finally {
  599. FakeXHR.restore()
  600. }
  601. })
  602. test('ajax() obeys `processData`', function (complete) {
  603. FakeXHR.setup()
  604. try {
  605. var d = { bar: 'foo', thunk: 'baz' }
  606. ajax({
  607. url: '/tests/fixtures/fixtures.html'
  608. , processData: false
  609. , method: 'post'
  610. , data: d
  611. })
  612. ok(FakeXHR.last.methodCallCount('send') == 1, 'send called')
  613. ok(
  614. FakeXHR.last.methodCallArgs('send', 0).length == 1
  615. , 'send called with 1 arg'
  616. )
  617. ok(
  618. FakeXHR.last.methodCallArgs('send', 0, 0) === d
  619. , 'send called with exact `data` object'
  620. )
  621. complete()
  622. } finally {
  623. FakeXHR.restore()
  624. }
  625. })
  626. function testXhrGetUrlAdjustment(url, data, expectedUrl, complete) {
  627. FakeXHR.setup()
  628. try {
  629. ajax({ url: url, data: data })
  630. ok(FakeXHR.last.methodCallCount('open') == 1, 'open called')
  631. ok(
  632. FakeXHR.last.methodCallArgs('open', 0).length == 3
  633. , 'open called with 3 args'
  634. )
  635. ok(
  636. FakeXHR.last.methodCallArgs('open', 0, 0) == 'GET'
  637. , 'first arg of open() is "GET"'
  638. )
  639. ok(FakeXHR.last.methodCallArgs('open', 0, 1) == expectedUrl
  640. , 'second arg of open() is URL with query string')
  641. ok(
  642. FakeXHR.last.methodCallArgs('open', 0, 2) === true
  643. , 'third arg of open() is `true`'
  644. )
  645. ok(FakeXHR.last.methodCallCount('send') == 1, 'send called')
  646. ok(
  647. FakeXHR.last.methodCallArgs('send', 0).length == 1
  648. , 'send called with 1 arg'
  649. )
  650. ok(
  651. FakeXHR.last.methodCallArgs('send', 0, 0) === null
  652. , 'send called with null'
  653. )
  654. complete()
  655. } finally {
  656. FakeXHR.restore()
  657. }
  658. }
  659. test('ajax() appends GET URL with ?`data`', function (complete) {
  660. testXhrGetUrlAdjustment(
  661. '/tests/fixtures/fixtures.html'
  662. , 'bar=foo&thunk=baz'
  663. , '/tests/fixtures/fixtures.html?bar=foo&thunk=baz'
  664. , complete
  665. )
  666. })
  667. test('ajax() appends GET URL with ?`data` (serialized object)'
  668. , function (complete) {
  669. testXhrGetUrlAdjustment(
  670. '/tests/fixtures/fixtures.html'
  671. , { bar: 'foo', thunk: 'baz' }
  672. , '/tests/fixtures/fixtures.html?bar=foo&thunk=baz'
  673. , complete
  674. )
  675. })
  676. test('ajax() appends GET URL with &`data` (serialized array)'
  677. , function (complete) {
  678. testXhrGetUrlAdjustment(
  679. '/tests/fixtures/fixtures.html?x=y'
  680. , [ { name: 'bar', value: 'foo'}, {name: 'thunk', value: 'baz' } ]
  681. , '/tests/fixtures/fixtures.html?x=y&bar=foo&thunk=baz'
  682. , complete
  683. )
  684. })
  685. })
  686. sink('Standard vs compat mode', function (test, ok) {
  687. function methodMatch(resp, method) {
  688. return resp && resp.method === method
  689. }
  690. function headerMatch(resp, key, expected) {
  691. return resp && resp.headers && resp.headers[key] === expected
  692. }
  693. function queryMatch(resp, key, expected) {
  694. return resp && resp.query && resp.query[key] === expected
  695. }
  696. test('standard mode default', function (complete) {
  697. ajax({
  698. url: '/tests/none.json?echo'
  699. , success: function (resp) {
  700. ok(methodMatch(resp, 'GET'), 'correct request method (GET)')
  701. ok(
  702. headerMatch(
  703. resp
  704. , 'content-type'
  705. , 'application/x-www-form-urlencoded'
  706. )
  707. , 'correct Content-Type request header'
  708. )
  709. ok(
  710. headerMatch(resp, 'x-requested-with', 'XMLHttpRequest')
  711. , 'correct X-Requested-With header'
  712. )
  713. ok(
  714. headerMatch(
  715. resp
  716. , 'accept'
  717. , 'text/javascript, text/html, application/xml, text/xml, */*'
  718. )
  719. , 'correct Accept header'
  720. )
  721. complete()
  722. }
  723. })
  724. })
  725. test('standard mode custom content-type', function (complete) {
  726. ajax({
  727. url: '/tests/none.json?echo'
  728. , contentType: 'yapplication/foobar'
  729. , success: function (resp) {
  730. ok(methodMatch(resp, 'GET'), 'correct request method (GET)')
  731. ok(
  732. headerMatch(resp, 'content-type', 'yapplication/foobar')
  733. , 'correct Content-Type request header'
  734. )
  735. ok(
  736. headerMatch(resp, 'x-requested-with', 'XMLHttpRequest')
  737. , 'correct X-Requested-With header'
  738. )
  739. ok(
  740. headerMatch(
  741. resp
  742. , 'accept'
  743. , 'text/javascript, text/html, application/xml, text/xml, */*'
  744. )
  745. , 'correct Accept header'
  746. )
  747. complete()
  748. }
  749. })
  750. })
  751. test('standard mode on no content-type', function (complete) {
  752. ajax({
  753. url: '/tests/204'
  754. , success: function (resp) {
  755. ok(true, 'Nothing blew up.')
  756. }
  757. })
  758. })
  759. test('compat mode "dataType=json" headers', function (complete) {
  760. ajax.compat({
  761. url: '/tests/none.json?echo'
  762. , dataType: 'json' // should map to 'type'
  763. , success: function (resp) {
  764. ok(methodMatch(resp, 'GET'), 'correct request method (GET)')
  765. ok(
  766. headerMatch(
  767. resp
  768. , 'content-type'
  769. , 'application/x-www-form-urlencoded'
  770. )
  771. , 'correct Content-Type request header'
  772. )
  773. ok(
  774. headerMatch(resp, 'x-requested-with', 'XMLHttpRequest')
  775. , 'correct X-Requested-With header'
  776. )
  777. ok(
  778. headerMatch(resp, 'accept', 'application/json, text/javascript')
  779. , 'correct Accept header'
  780. )
  781. complete()
  782. }
  783. })
  784. })
  785. test('compat mode "dataType=json" with "type=post" headers'
  786. , function (complete) {
  787. ajax.compat({
  788. url: '/tests/none.json?echo'
  789. , type: 'post'
  790. , dataType: 'json' // should map to 'type'
  791. , success: function (resp) {
  792. ok(methodMatch(resp, 'POST'), 'correct request method (POST)')
  793. ok(
  794. headerMatch(
  795. resp
  796. , 'content-type'
  797. , 'application/x-www-form-urlencoded'
  798. )
  799. , 'correct Content-Type request header'
  800. )
  801. ok(
  802. headerMatch(resp, 'x-requested-with', 'XMLHttpRequest')
  803. , 'correct X-Requested-With header'
  804. )
  805. ok(
  806. headerMatch(resp, 'accept', 'application/json, text/javascript')
  807. , 'correct Accept header'
  808. )
  809. complete()
  810. }
  811. })
  812. })
  813. test('compat mode "dataType=json" headers (with additional headers)'
  814. , function (complete) {
  815. ajax.compat({
  816. url: '/tests/none.json?echo'
  817. , dataType: 'json' // should map to 'type'
  818. // verify that these are left intact and nothing screwy
  819. // happens with headers
  820. , headers: { one: 1, two: 2 }
  821. , success: function (resp) {
  822. ok(
  823. headerMatch(
  824. resp
  825. , 'content-type'
  826. , 'application/x-www-form-urlencoded'
  827. )
  828. , 'correct Content-Type request header'
  829. )
  830. ok(
  831. headerMatch(resp, 'x-requested-with', 'XMLHttpRequest')
  832. , 'correct X-Requested-With header'
  833. )
  834. ok(
  835. headerMatch(resp, 'accept', 'application/json, text/javascript')
  836. , 'correct Accept header'
  837. )
  838. ok(
  839. headerMatch(resp, 'one', '1') && headerMatch(resp, 'two', '2')
  840. , 'left additional headers intact'
  841. )
  842. complete()
  843. }
  844. })
  845. })
  846. test('compat mode "dataType=jsonp" query string', function (complete) {
  847. ajax.compat({
  848. url: '/tests/none.jsonp?echo'
  849. , dataType: 'jsonp'
  850. , jsonp: 'testCallback' // should map to jsonpCallback
  851. , jsonpCallback: 'foobar' // should map to jsonpCallbackName
  852. , success: function (resp) {
  853. ok(
  854. queryMatch(resp, 'echo', '')
  855. , 'correct Content-Type request header'
  856. )
  857. ok(
  858. queryMatch(resp, 'testCallback', 'foobar')
  859. , 'correct X-Requested-With header'
  860. )
  861. complete()
  862. }
  863. })
  864. })
  865. })
  866. /***************** SERIALIZER TESTS ***********************/
  867. // define some helpers for the serializer tests that are used often and
  868. // shared with the ender integration tests
  869. function createSerializeHelper(ok) {
  870. var forms = document.forms
  871. , foo = forms[0].getElementsByTagName('input')[1]
  872. , bar = forms[0].getElementsByTagName('input')[2]
  873. , choices = forms[0].getElementsByTagName('select')[0]
  874. , BIND_ARGS = 'bind'
  875. , PASS_ARGS = 'pass'
  876. function reset() {
  877. forms[1].reset()
  878. }
  879. function formElements(formIndex, tagName, elementIndex) {
  880. return forms[formIndex].getElementsByTagName(tagName)[elementIndex]
  881. }
  882. function isArray(a) {
  883. return Object.prototype.toString.call(a) == '[object Array]'
  884. }
  885. function sameValue(value, expected) {
  886. if (expected == null) {
  887. return value === null
  888. } else if (isArray(expected)) {
  889. if (value.length !== expected.length) return false
  890. for (var i = 0; i < expected.length; i++) {
  891. if (value[i] != expected[i]) return false
  892. }
  893. return true
  894. } else return value == expected
  895. }
  896. function testInput(input, name, value, str) {
  897. var sa = ajax.serialize(input, { type: 'array' })
  898. , sh = ajax.serialize(input, { type: 'map' })
  899. , av, i
  900. if (value != null) {
  901. av = isArray(value) ? value : [ value ]
  902. ok(
  903. sa.length == av.length
  904. , 'serialize(' + str + ', {type:\'array\'}) returns array '
  905. + '[{name,value}]'
  906. )
  907. for (i = 0; i < av.length; i++) {
  908. ok(
  909. name == sa[i].name
  910. , 'serialize(' + str + ', {type:\'array\'})[' + i + '].name'
  911. )
  912. ok(
  913. av[i] == sa[i].value
  914. , 'serialize(' + str + ', {type:\'array\'})[' + i + '].value'
  915. )
  916. }
  917. ok(sameValue(sh[name], value), 'serialize(' + str + ', {type:\'map\'})')
  918. } else {
  919. // the cases where an element shouldn't show up at all, checkbox not
  920. // checked for example
  921. ok(sa.length === 0, 'serialize(' + str + ', {type:\'array\'}) is []')
  922. ok(
  923. v.keys(sh).length === 0
  924. , 'serialize(' + str + ', {type:\'map\'}) is {}'
  925. )
  926. }
  927. }
  928. function testFormSerialize(method, type) {
  929. var expected =
  930. 'foo=bar&bar=baz&wha=1&wha=3&who=tawoo&%24escapable+name'
  931. + '%24=escapeme&choices=two&opinions=world+peace+is+not+real'
  932. ok(method, 'serialize() bound to context')
  933. ok(
  934. (method ? method(forms[0]) : null) == expected
  935. , 'serialized form (' + type + ')'
  936. )
  937. }
  938. function executeMultiArgumentMethod(method, argType, options) {
  939. var els = [ foo, bar, choices ]
  940. , ths = argType === BIND_ARGS ? ender(els) : null
  941. , args = argType === PASS_ARGS ? els : []
  942. if (!!options) args.push(options)
  943. return method.apply(ths, args)
  944. }
  945. function testMultiArgumentSerialize(method, type, argType) {
  946. ok(method, 'serialize() bound in context')
  947. var result = method ? executeMultiArgumentMethod(method, argType) : null
  948. ok(
  949. result == 'foo=bar&bar=baz&choices=two'
  950. , 'serialized all 3 arguments together'
  951. )
  952. }
  953. function verifyFormSerializeArray(result, type) {
  954. var expected = [
  955. { name: 'foo', value: 'bar' }
  956. , { name: 'bar', value: 'baz' }
  957. , { name: 'wha', value: 1 }
  958. , { name: 'wha', value: 3 }
  959. , { name: 'who', value: 'tawoo' }
  960. , { name: '$escapable name$', value: 'escapeme' }
  961. , { name: 'choices', value: 'two' }
  962. , { name: 'opinions', value: 'world peace is not real' }
  963. ]
  964. , i
  965. for (i = 0; i < expected.length; i++) {
  966. ok(v.some(result, function (v) {
  967. return v.name == expected[i].name && v.value == expected[i].value
  968. }), 'serialized ' + expected[i].name + ' (' + type + ')')
  969. }
  970. }
  971. function testFormSerializeArray(method, type) {
  972. ok(method, 'serialize(..., {type:\'array\'}) bound to context')
  973. var result = method ? method(forms[0], { type: 'array' }) : []
  974. if (!result) result = []
  975. verifyFormSerializeArray(result, type)
  976. }
  977. function testMultiArgumentSerializeArray(method, type, argType) {
  978. ok(method, 'serialize(..., {type:\'array\'}) bound to context')
  979. var result = method
  980. ? executeMultiArgumentMethod(method, argType, { type: 'array' })
  981. : []
  982. if (!result) result = []
  983. ok(result.length == 3, 'serialized as array of 3')
  984. ok(
  985. result.length == 3
  986. && result[0].name == 'foo'
  987. && result[0].value == 'bar'
  988. , 'serialized first element (' + type + ')'
  989. )
  990. ok(
  991. result.length == 3
  992. && result[1].name == 'bar'
  993. && result[1].value == 'baz'
  994. , 'serialized second element (' + type + ')'
  995. )
  996. ok(
  997. result.length == 3
  998. && result[2].name == 'choices'
  999. && result[2].value == 'two'
  1000. , 'serialized third element (' + type + ')'
  1001. )
  1002. }
  1003. function testFormSerializeHash(method, type) {
  1004. var expected = {
  1005. foo: 'bar'
  1006. , bar: 'baz'
  1007. , wha: [ '1', '3' ]
  1008. , who: 'tawoo'
  1009. , '$escapable name$': 'escapeme'
  1010. , choices: 'two'
  1011. , opinions: 'world peace is not real'
  1012. }
  1013. , result
  1014. ok(method, 'serialize({type:\'map\'}) bound to context')
  1015. result = method ? method(forms[0], { type: 'map' }) : {}
  1016. if (!result) result = {}
  1017. ok(
  1018. v.keys(expected).length === v.keys(result).length
  1019. , 'same number of keys (' + type + ')'
  1020. )
  1021. v.each(v.keys(expected), function (k) {
  1022. ok(
  1023. sameValue(expected[k], result[k])
  1024. , 'same value for ' + k + ' (' + type + ')'
  1025. )
  1026. })
  1027. }
  1028. function testMultiArgumentSerializeHash(method, type, argType) {
  1029. ok(method, 'serialize({type:\'map\'}) bound to context')
  1030. var result = method
  1031. ? executeMultiArgumentMethod(method, argType, { type: 'map' })
  1032. : {}
  1033. if (!result) result = {}
  1034. ok(result.foo == 'bar', 'serialized first element (' + type + ')')
  1035. ok(result.bar == 'baz', 'serialized second element (' + type + ')')
  1036. ok(result.choices == 'two', 'serialized third element (' + type + ')')
  1037. }
  1038. return {
  1039. reset: reset
  1040. , formElements: formElements
  1041. , testInput: testInput
  1042. , testFormSerialize: testFormSerialize
  1043. , testMultiArgumentSerialize: testMultiArgumentSerialize
  1044. , testFormSerializeArray: testFormSerializeArray
  1045. , verifyFormSerializeArray: verifyFormSerializeArray
  1046. , testMultiArgumentSerializeArray: testMultiArgumentSerializeArray
  1047. , testFormSerializeHash: testFormSerializeHash
  1048. , testMultiArgumentSerializeHash: testMultiArgumentSerializeHash
  1049. }
  1050. }
  1051. sink('Serializing', function (test, ok) {
  1052. /*
  1053. * Serialize forms according to spec.
  1054. * * reqwest.serialize(ele[, ele...]) returns a query string style
  1055. * serialization
  1056. * * reqwest.serialize(ele[, ele...], {type:'array'}) returns a
  1057. * [ { name: 'name', value: 'value'}, ... ] style serialization,
  1058. * compatible with jQuery.serializeArray()
  1059. * * reqwest.serialize(ele[, ele...], {type:\'map\'}) returns a
  1060. * { 'name': 'value', ... } style serialization, compatible with
  1061. * Prototype Form.serializeElements({hash:true})
  1062. * Some tests based on spec notes here:
  1063. * http://malsup.com/jquery/form/comp/test.html
  1064. */
  1065. var sHelper = createSerializeHelper(ok)
  1066. sHelper.reset()
  1067. test('correctly serialize textarea', function (complete) {
  1068. var textarea = sHelper.formElements(1, 'textarea', 0)
  1069. , sa
  1070. // the texarea has 2 different newline styles, should come out as
  1071. // normalized CRLF as per forms spec
  1072. ok(
  1073. 'T3=%3F%0D%0AA+B%0D%0AZ' == ajax.serialize(textarea)
  1074. , 'serialize(textarea)'
  1075. )
  1076. sa = ajax.serialize(textarea, { type: 'array' })
  1077. ok(sa.length == 1, 'serialize(textarea, {type:\'array\'}) returns array')
  1078. sa = sa[0]
  1079. ok('T3' == sa.name, 'serialize(textarea, {type:\'array\'}).name')
  1080. ok(
  1081. '?\r\nA B\r\nZ' == sa.value
  1082. , 'serialize(textarea, {type:\'array\'}).value'
  1083. )
  1084. ok(
  1085. '?\r\nA B\r\nZ' == ajax.serialize(textarea, { type: 'map' }).T3
  1086. , 'serialize(textarea, {type:\'map\'})'
  1087. )
  1088. complete()
  1089. })
  1090. test('correctly serialize input[type=hidden]', function (complete) {
  1091. sHelper.testInput(
  1092. sHelper.formElements(1, 'input', 0)
  1093. , 'H1'
  1094. , 'x'
  1095. , 'hidden'
  1096. )
  1097. sHelper.testInput(
  1098. sHelper.formElements(1, 'input', 1)
  1099. , 'H2'
  1100. , ''
  1101. , 'hidden[no value]'
  1102. )
  1103. complete()
  1104. })
  1105. test('correctly serialize input[type=password]', function (complete) {
  1106. sHelper.testInput(
  1107. sHelper.formElements(1, 'input', 2)
  1108. , 'PWD1'
  1109. , 'xyz'
  1110. , 'password'
  1111. )
  1112. sHelper.testInput(
  1113. sHelper.formElements(1, 'input', 3)
  1114. , 'PWD2'
  1115. , ''
  1116. , 'password[no value]'
  1117. )
  1118. complete()
  1119. })
  1120. test('correctly serialize input[type=text]', function (complete) {
  1121. sHelper.testInput(
  1122. sHelper.formElements(1, 'input', 4)
  1123. , 'T1'
  1124. , ''
  1125. , 'text[no value]'
  1126. )
  1127. sHelper.testInput(
  1128. sHelper.formElements(1, 'input', 5)
  1129. , 'T2'
  1130. , 'YES'
  1131. , 'text[readonly]'
  1132. )
  1133. sHelper.testInput(
  1134. sHelper.formElements(1, 'input', 10)
  1135. , 'My Name'
  1136. , 'me'
  1137. , 'text[space name]'
  1138. )
  1139. complete()
  1140. })
  1141. test('correctly serialize input[type=checkbox]', function (complete) {
  1142. var cb1 = sHelper.formElements(1, 'input', 6)
  1143. , cb2 = sHelper.formElements(1, 'input', 7)
  1144. sHelper.testInput(cb1, 'C1', null, 'checkbox[not checked]')
  1145. cb1.checked = true
  1146. sHelper.testInput(cb1, 'C1', '1', 'checkbox[checked]')
  1147. // special case here, checkbox with no value='' should give you 'on'
  1148. // for cb.value
  1149. sHelper.testInput(cb2, 'C2', null, 'checkbox[no value, not checked]')
  1150. cb2.checked = true
  1151. sHelper.testInput(cb2, 'C2', 'on', 'checkbox[no value, checked]')
  1152. complete()
  1153. })
  1154. test('correctly serialize input[type=radio]', function (complete) {
  1155. var r1 = sHelper.formElements(1, 'input', 8)
  1156. , r2 = sHelper.formElements(1, 'input', 9)
  1157. sHelper.testInput(r1, 'R1', null, 'radio[not checked]')
  1158. r1.checked = true
  1159. sHelper.testInput(r1, 'R1', '1', 'radio[not checked]')
  1160. sHelper.testInput(r2, 'R1', null, 'radio[no value, not checked]')
  1161. r2.checked = true
  1162. sHelper.testInput(r2, 'R1', '', 'radio[no value, checked]')
  1163. complete()
  1164. })
  1165. test('correctly serialize input[type=reset]', function (complete) {
  1166. sHelper.testInput(
  1167. sHelper.formElements(1, 'input', 11)
  1168. , 'rst'
  1169. , null
  1170. , 'reset'
  1171. )
  1172. complete()
  1173. })
  1174. test('correctly serialize input[type=file]', function (complete) {
  1175. sHelper.testInput(
  1176. sHelper.formElements(1, 'input', 12)
  1177. , 'file'
  1178. , null
  1179. , 'file'
  1180. )
  1181. complete()
  1182. })
  1183. test('correctly serialize input[type=submit]', function (complete) {
  1184. // we're only supposed to serialize a submit button if it was clicked to
  1185. // perform this serialization:
  1186. // http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2
  1187. // but we'll pretend to be oblivious to this part of the spec...
  1188. sHelper.testInput(
  1189. sHelper.formElements(1, 'input', 13)
  1190. , 'sub'
  1191. , 'NO'
  1192. , 'submit'
  1193. )
  1194. complete()
  1195. })
  1196. test('correctly serialize select with no options', function (complete) {
  1197. var select = sHelper.formElements(1, 'select', 0)
  1198. sHelper.testInput(select, 'S1', null, 'select, no options')
  1199. complete()
  1200. })
  1201. test('correctly serialize select with values', function (complete) {
  1202. var select = sHelper.formElements(1, 'select', 1)
  1203. sHelper.testInput(select, 'S2', 'abc', 'select option 1 (default)')
  1204. select.selectedIndex = 1
  1205. sHelper.testInput(select, 'S2', 'def', 'select option 2')
  1206. select.selectedIndex = 6
  1207. sHelper.testInput(select, 'S2', 'disco stu', 'select option 7')
  1208. // a special case where we have <option value=''>X</option>, should
  1209. // return '' rather than X which will happen if you just do a simple
  1210. // `value=(option.value||option.text)`
  1211. select.selectedIndex = 9
  1212. sHelper.testInput(
  1213. select
  1214. , 'S2'
  1215. , ''
  1216. , 'select option 9, value="" should yield ""'
  1217. )
  1218. select.selectedIndex = -1
  1219. sHelper.testInput(select, 'S2', null, 'select, unselected')
  1220. complete()
  1221. })
  1222. test('correctly serialize select without explicit values'
  1223. , function (complete) {
  1224. var select = sHelper.formElements(1, 'select', 2)
  1225. sHelper.testInput(select, 'S3', 'ABC', 'select option 1 (default)')
  1226. select.selectedIndex = 1
  1227. sHelper.testInput(select, 'S3', 'DEF', 'select option 2')
  1228. select.selectedIndex = 6
  1229. sHelper.testInput(select, 'S3', 'DISCO STU!', 'select option 7')
  1230. select.selectedIndex = -1
  1231. sHelper.testInput(select, 'S3', null, 'select, unselected')
  1232. complete()
  1233. })
  1234. test('correctly serialize select multiple', function (complete) {
  1235. var select = sHelper.formElements(1, 'select', 3)
  1236. sHelper.testInput(select, 'S4', null, 'select, unselected (default)')
  1237. select.options[1].selected = true
  1238. sHelper.testInput(select, 'S4', '2', 'select option 2')
  1239. select.options[3].selected = true
  1240. sHelper.testInput(select, 'S4', [ '2', '4' ], 'select options 2 & 4')
  1241. select.options[8].selected = true
  1242. sHelper.testInput(
  1243. select
  1244. , 'S4'
  1245. , [ '2', '4', 'Disco Stu!' ]
  1246. , 'select option 2 & 4 & 9'
  1247. )
  1248. select.options[3].selected = false
  1249. sHelper.testInput(
  1250. select
  1251. , 'S4'
  1252. , [ '2', 'Disco Stu!' ]
  1253. , 'select option 2 & 9'
  1254. )
  1255. select.options[1].selected = false
  1256. select.options[8].selected = false
  1257. sHelper.testInput(select, 'S4', null, 'select, all unselected')
  1258. complete()
  1259. })
  1260. test('correctly serialize options', function (complete) {
  1261. var option = sHelper.formElements(1, 'select', 1).options[6]
  1262. sHelper.testInput(
  1263. option
  1264. , '-'
  1265. , null
  1266. , 'just option (with value), shouldn\'t serialize'
  1267. )
  1268. option = sHelper.formElements(1, 'select', 2).options[6]
  1269. sHelper.testInput(
  1270. option
  1271. , '-'
  1272. , null
  1273. , 'option (without value), shouldn\'t serialize'
  1274. )
  1275. complete()
  1276. })
  1277. test('correctly serialize disabled', function (complete) {
  1278. var input = sHelper.formElements(1, 'input', 14)
  1279. , select
  1280. sHelper.testInput(input, 'D1', null, 'disabled text input')
  1281. input = sHelper.formElements(1, 'input', 15)
  1282. sHelper.testInput(input, 'D2', null, 'disabled checkbox')
  1283. input = sHelper.formElements(1, 'input', 16)
  1284. sHelper.testInput(input, 'D3', null, 'disabled radio')
  1285. select = sHelper.formElements(1, 'select', 4)
  1286. sHelper.testInput(select, 'D4', null, 'disabled select')
  1287. select = sHelper.formElements(1, 'select', 3)
  1288. sHelper.testInput(select, 'D5', null, 'disabled select option')
  1289. select = sHelper.formElements(1, 'select', 6)
  1290. sHelper.testInput(select, 'D6', null, 'disabled multi select')
  1291. select = sHelper.formElements(1, 'select', 7)
  1292. sHelper.testInput(select, 'D7', null, 'disabled multi select option')
  1293. complete()
  1294. })
  1295. test('serialize(form)', function (complete) {
  1296. sHelper.testFormSerialize(ajax.serialize, 'direct')
  1297. complete()
  1298. })
  1299. test('serialize(form, {type:\'array\'})', function (complete) {
  1300. sHelper.testFormSerializeArray(ajax.serialize, 'direct')
  1301. complete()
  1302. })
  1303. test('serialize(form, {type:\'map\'})', function (complete) {
  1304. sHelper.testFormSerializeHash(ajax.serialize, 'direct')
  1305. complete()
  1306. })
  1307. // mainly for Ender integration, so you can do this:
  1308. // $('input[name=T2],input[name=who],input[name=wha]').serialize()
  1309. test('serialize(element, element, element...)', function (complete) {
  1310. sHelper.testMultiArgumentSerialize(ajax.serialize, 'direct', PASS_ARGS)
  1311. complete()
  1312. })
  1313. // mainly for Ender integration, so you can do this:
  1314. // $('input[name=T2],input[name=who],input[name=wha]')
  1315. // .serialize({type:'array'})
  1316. test('serialize(element, element, element..., {type:\'array\'})'
  1317. , function (complete) {
  1318. sHelper.testMultiArgumentSerializeArray(
  1319. ajax.serialize
  1320. , 'direct'
  1321. , PASS_ARGS
  1322. )
  1323. complete()
  1324. })
  1325. // mainly for Ender integration, so you can do this:
  1326. // $('input[name=T2],input[name=who],input[name=wha]')
  1327. // .serialize({type:'map'})
  1328. test('serialize(element, element, element...)', function (complete) {
  1329. sHelper.testMultiArgumentSerializeHash(
  1330. ajax.serialize
  1331. , 'direct'
  1332. , PASS_ARGS
  1333. )
  1334. complete()
  1335. })
  1336. test('toQueryString([{ name: x, value: y }, ... ]) name/value array'
  1337. , function (complete) {
  1338. var arr = [
  1339. { name: 'foo', value: 'bar' }
  1340. , { name: 'baz', value: '' }
  1341. , { name: 'x', value: -20 }
  1342. , { name: 'x', value: 20 }
  1343. ]
  1344. ok(ajax.toQueryString(arr) == 'foo=bar&baz=&x=-20&x=20', 'simple')
  1345. arr = [
  1346. { name: 'dotted.name.intact', value: '$@%' }
  1347. , { name: '$ $', value: 20 }
  1348. , { name: 'leave britney alone', value: 'waa haa haa' }
  1349. ]
  1350. ok(
  1351. ajax.toQueryString(arr) ==
  1352. 'dotted.name.intact=%24%40%25&%24+%24=20'
  1353. + '&leave+britney+alone=waa+haa+haa'
  1354. , 'escaping required'
  1355. )
  1356. complete()
  1357. })
  1358. test('toQueryString({name: value,...} complex object', function (complete) {
  1359. var obj = { 'foo': 'bar', 'baz': '', 'x': -20 }
  1360. ok(ajax.toQueryString(obj) == 'foo=bar&baz=&x=-20', 'simple')
  1361. obj = {
  1362. 'dotted.name.intact': '$@%'
  1363. , '$ $': 20
  1364. , 'leave britney alone': 'waa haa haa'
  1365. }
  1366. ok(
  1367. ajax.toQueryString(obj) ==
  1368. 'dotted.name.intact=%24%40%25&%24+%24=20'
  1369. + '&leave+britney+alone=waa+haa+haa'
  1370. , 'escaping required'
  1371. )
  1372. complete()
  1373. })
  1374. test('toQueryString({name: [ value1, value2 ...],...} object with arrays', function (complete) {
  1375. var obj = { 'foo': 'bar', 'baz': [ '', '', 'boo!' ], 'x': [ -20, 2.2, 20 ] }
  1376. ok(ajax.toQueryString(obj, true) == "foo=bar&baz=&baz=&baz=boo!&x=-20&x=2.2&x=20", "object with arrays")
  1377. ok(ajax.toQueryString(obj) == "foo=bar&baz%5B%5D=&baz%5B%5D=&baz%5B%5D=boo!&x%5B%5D=-20&x%5B%5D=2.2&x%5B%5D=20")
  1378. complete()
  1379. })
  1380. test('toQueryString({name: { nestedName: value },...} object with objects', function(complete) {
  1381. var obj = { 'foo': { 'bar': 'baz' }, 'x': [ { 'bar': 'baz' }, { 'boo': 'hiss' } ] }
  1382. ok(ajax.toQueryString(obj) == "foo%5Bbar%5D=baz&x%5B0%5D%5Bbar%5D=baz&x%5B1%5D%5Bboo%5D=hiss", "object with objects")
  1383. complete()
  1384. })
  1385. })
  1386. sink('Ender Integration', function (test, ok) {
  1387. var sHelper = createSerializeHelper(ok)
  1388. sHelper.reset()
  1389. test('$.ajax alias for reqwest, not bound to boosh', 1, function () {
  1390. ok(ender.ajax === ajax, '$.ajax is reqwest')
  1391. })
  1392. // sHelper.test that you can do $.serialize(form)
  1393. test('$.serialize(form)', function (complete) {
  1394. sHelper.testFormSerialize(ender.serialize, 'ender')
  1395. complete()
  1396. })
  1397. // sHelper.test that you can do $.serialize(form)
  1398. test('$.serialize(form, {type:\'array\'})', function (complete) {
  1399. sHelper.testFormSerializeArray(ender.serialize, 'ender')
  1400. complete()
  1401. })
  1402. // sHelper.test that you can do $.serialize(form)
  1403. test('$.serialize(form, {type:\'map\'})', function (complete) {
  1404. sHelper.testFormSerializeHash(ender.serialize, 'ender')
  1405. complete()
  1406. })
  1407. // sHelper.test that you can do $.serializeObject(form)
  1408. test('$.serializeArray(...) alias for serialize(..., {type:\'map\'}'
  1409. , function (complete) {
  1410. sHelper.verifyFormSerializeArray(
  1411. ender.serializeArray(document.forms[0])
  1412. , 'ender'
  1413. )
  1414. complete()
  1415. })
  1416. test('$.serialize(element, element, element...)', function (complete) {
  1417. sHelper.testMultiArgumentSerialize(ender.serialize, 'ender', PASS_ARGS)
  1418. complete()
  1419. })
  1420. test('$.serialize(element, element, element..., {type:\'array\'})'
  1421. , function (complete) {
  1422. sHelper.testMultiArgumentSerializeArray(
  1423. ender.serialize
  1424. , 'ender'
  1425. , PASS_ARGS
  1426. )
  1427. complete()
  1428. })
  1429. test('$.serialize(element, element, element..., {type:\'map\'})'
  1430. , function (complete) {
  1431. sHelper.testMultiArgumentSerializeHash(
  1432. ender.serialize
  1433. , 'ender'
  1434. , PASS_ARGS
  1435. )
  1436. complete()
  1437. })
  1438. test('$(element, element, element...).serialize()', function (complete) {
  1439. sHelper.testMultiArgumentSerialize(ender.fn.serialize, 'ender', BIND_ARGS)
  1440. complete()
  1441. })
  1442. test('$(element, element, element...).serialize({type:\'array\'})'
  1443. , function (complete) {
  1444. sHelper.testMultiArgumentSerializeArray(
  1445. ender.fn.serialize
  1446. , 'ender'
  1447. , BIND_ARGS
  1448. )
  1449. complete()
  1450. })
  1451. test('$(element, element, element...).serialize({type:\'map\'})'
  1452. , function (complete) {
  1453. sHelper.testMultiArgumentSerializeHash(
  1454. ender.fn.serialize
  1455. , 'ender'
  1456. , BIND_ARGS
  1457. )
  1458. complete()
  1459. })
  1460. test('$.toQueryString alias for reqwest.toQueryString, not bound to boosh'
  1461. , function (complete) {
  1462. ok(
  1463. ender.toQueryString === ajax.toQueryString
  1464. , '$.toQueryString is reqwest.toQueryString'
  1465. )
  1466. complete()
  1467. })
  1468. })
  1469. /**
  1470. * Promise tests for `then` `fail` and `always`
  1471. */
  1472. sink('Promises', function (test, ok) {
  1473. test('always callback is called', function (complete) {
  1474. ajax({
  1475. url: '/tests/fixtures/fixtures.js'
  1476. })
  1477. .always(function () {
  1478. ok(true, 'called complete')
  1479. complete()
  1480. })
  1481. })
  1482. test('success and error handlers are called', 3, function () {
  1483. ajax({
  1484. url: '/tests/fixtures/invalidJSON.json'
  1485. , type: 'json'
  1486. })
  1487. .then(
  1488. function () {
  1489. ok(false, 'success callback fired')
  1490. }
  1491. , function (resp, msg) {
  1492. ok(
  1493. msg == 'Could not parse JSON in response'
  1494. , 'error callback fired'
  1495. )
  1496. }
  1497. )
  1498. ajax({
  1499. url: '/tests/fixtures/invalidJSON.json'
  1500. , type: 'json'
  1501. })
  1502. .fail(function (resp, msg) {
  1503. ok(msg == 'Could not parse JSON in response', 'fail callback fired')
  1504. })
  1505. ajax({
  1506. url: '/tests/fixtures/fixtures.json'
  1507. , type: 'json'
  1508. })
  1509. .then(
  1510. function () {
  1511. ok(true, 'success callback fired')
  1512. }
  1513. , function () {
  1514. ok(false, 'error callback fired')
  1515. }
  1516. )
  1517. })
  1518. test('then is chainable', 2, function () {
  1519. ajax({
  1520. url: '/tests/fixtures/fixtures.json'
  1521. , type: 'json'
  1522. })
  1523. .then(
  1524. function (resp) {
  1525. ok(true, 'first success callback fired')
  1526. return 'new value';
  1527. }
  1528. )
  1529. .then(
  1530. function (resp) {
  1531. ok(resp === 'new value', 'second success callback fired')
  1532. }
  1533. )
  1534. })
  1535. test('success does not chain with then', 2, function () {
  1536. ajax({
  1537. url: '/tests/fixtures/fixtures.json'
  1538. , type: 'json'
  1539. , success: function() {
  1540. ok(true, 'success callback fired')
  1541. return 'some independent value';
  1542. }
  1543. })
  1544. .then(
  1545. function (resp) {
  1546. ok(
  1547. resp && resp !== 'some independent value'
  1548. , 'then callback fired'
  1549. )
  1550. }
  1551. )
  1552. })
  1553. test('then & always handlers can be added after a response is received'
  1554. , 2
  1555. , function () {
  1556. var a = ajax({
  1557. url: '/tests/fixtures/fixtures.json'
  1558. , type: 'json'
  1559. })
  1560. .always(function () {
  1561. setTimeout(function () {
  1562. a.then(
  1563. function () {
  1564. ok(true, 'success callback called')
  1565. }
  1566. , function () {
  1567. ok(false, 'error callback called')
  1568. }
  1569. ).always(function () {
  1570. ok(true, 'complete callback called')
  1571. })
  1572. }, 1)
  1573. })
  1574. })
  1575. test('then is chainable after a response is received'
  1576. , 2
  1577. , function () {
  1578. var a = ajax({
  1579. url: '/tests/fixtures/fixtures.json'
  1580. , type: 'json'
  1581. })
  1582. .always(function () {
  1583. setTimeout(function () {
  1584. a.then(function () {
  1585. ok(true, 'first success callback called')
  1586. return 'new value';
  1587. }).then(function (resp) {
  1588. ok(resp === 'new value', 'second success callback called')
  1589. })
  1590. }, 1)
  1591. })
  1592. })
  1593. test('failure handlers can be added after a response is received'
  1594. , function (complete) {
  1595. var a = ajax({
  1596. url: '/tests/fixtures/invalidJSON.json'
  1597. , type: 'json'
  1598. })
  1599. .always(function () {
  1600. setTimeout(function () {
  1601. a
  1602. .fail(function () {
  1603. ok(true, 'fail callback called')
  1604. complete()
  1605. })
  1606. }, 1)
  1607. })
  1608. })
  1609. test('.then success and fail are optional parameters', 1, function () {
  1610. try {
  1611. ajax({
  1612. url: '/tests/fixtures/invalidJSON.json'
  1613. , type: 'json'
  1614. })
  1615. .then()
  1616. } catch (ex) {
  1617. ok(false, '.then() parameters should be optional')
  1618. } finally {
  1619. ok(true, 'passed .then() optional parameters')
  1620. }
  1621. })
  1622. })
  1623. sink('Timeout', function (test, ok) {
  1624. test('xmlHttpRequest', function (complete) {
  1625. var ts = +new Date()
  1626. ajax({
  1627. url: '/tests/timeout'
  1628. , type: 'json'
  1629. , timeout: 250
  1630. , error: function (err, msg) {
  1631. ok(err, 'received error response')
  1632. try {
  1633. ok(err && err.status === 0, 'correctly caught timeout')
  1634. ok(msg && msg === 'Request is aborted: timeout', 'timeout message received')
  1635. } catch (e) {
  1636. ok(true, 'IE is a troll')
  1637. }
  1638. var tt = Math.abs(+new Date() - ts)
  1639. ok(
  1640. tt > 200 && tt < 300
  1641. , 'timeout close enough to 250 (' + tt + ')'
  1642. )
  1643. complete()
  1644. }
  1645. })
  1646. })
  1647. test('jsonpRequest', function (complete) {
  1648. var ts = +new Date()
  1649. ajax({
  1650. url: '/tests/timeout'
  1651. , type: 'jsonp'
  1652. , timeout: 250
  1653. , error: function (err) {
  1654. ok(err, 'received error response')
  1655. var tt = Math.abs(+new Date() - ts)
  1656. ok(
  1657. tt > 200 && tt < 300
  1658. , 'timeout close enough to 250 (' + tt + ')'
  1659. )
  1660. complete()
  1661. }
  1662. })
  1663. })
  1664. })
  1665. start()
  1666. }(reqwest))