prettyprinter_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. /*
  2. * Public Domain Software
  3. *
  4. * I (Matthias Ladkau) am the author of the source code in this file.
  5. * I have placed the source code in this file in the public domain.
  6. *
  7. * For further information see: http://creativecommons.org/publicdomain/zero/1.0/
  8. */
  9. package parser
  10. import (
  11. "fmt"
  12. "os"
  13. "testing"
  14. )
  15. func TestSimpleExpressionPrinting(t *testing.T) {
  16. input := `query {
  17. likeStory(storyID: 12345) {
  18. story {
  19. likeCount
  20. }
  21. }
  22. }`
  23. expectedOutput := `
  24. Document
  25. ExecutableDefinition
  26. OperationDefinition
  27. OperationType: query
  28. SelectionSet
  29. Field
  30. Name: likeStory
  31. Arguments
  32. Argument
  33. Name: storyID
  34. Value: 12345
  35. SelectionSet
  36. Field
  37. Name: story
  38. SelectionSet
  39. Field
  40. Name: likeCount
  41. `[1:]
  42. if err := testPrettyPrinting(input, expectedOutput,
  43. input); err != nil {
  44. t.Error(err)
  45. return
  46. }
  47. /*
  48. From the spec 2.9.4 Strings
  49. Since block strings represent freeform text often used in indented positions,
  50. the string value semantics of a block string excludes uniform indentation and
  51. blank initial and trailing lines via BlockStringValue().
  52. For example, the following operation containing a block string:
  53. mutation {
  54. sendEmail(message: """
  55. Hello,
  56. World!
  57. Yours,
  58. GraphQL.
  59. """)
  60. }
  61. Is identical to the standard quoted string:
  62. mutation {
  63. sendEmail(message: "Hello,\n World!\n\nYours,\n GraphQL.")
  64. }
  65. */
  66. input = `{
  67. foo(bar: """
  68. Hello,
  69. World!
  70. Yours,
  71. GraphQL.
  72. """) # Block string value
  73. }
  74. `
  75. expectedOutput = `
  76. Document
  77. ExecutableDefinition
  78. OperationDefinition
  79. SelectionSet
  80. Field
  81. Name: foo
  82. Arguments
  83. Argument
  84. Name: bar
  85. Value: Hello,
  86. World!
  87. Yours,
  88. GraphQL.
  89. `[1:]
  90. astres, err := ParseWithRuntime("mytest", input, &TestRuntimeProvider{})
  91. if err != nil || fmt.Sprint(astres) != expectedOutput {
  92. t.Error(fmt.Sprintf("Unexpected parser output:\n%v expected was:\n%v Error: %v", astres, expectedOutput, err))
  93. return
  94. }
  95. ppOutput := `{
  96. foo(bar: """Hello,
  97. World!
  98. Yours,
  99. GraphQL.""")
  100. }`
  101. ppres, err := PrettyPrint(astres)
  102. if err != nil || ppres != ppOutput {
  103. fmt.Fprintf(os.Stderr, "#\n%v#", ppres)
  104. t.Error(fmt.Sprintf("Unexpected result:\n%v\nError: %v", ppres, err))
  105. return
  106. }
  107. val := astres.Children[0].Children[0].Children[0].Children[0].Children[1].Children[0].Children[1].Token.Val
  108. if val != "Hello,\n World!\n\nYours,\n GraphQL." {
  109. t.Error("Unexpected result:", val)
  110. }
  111. input = `{
  112. foo(bar: $Hello) # Variable value
  113. foo(bar: 1) # Int value
  114. foo(bar: 1.1) # Float value
  115. foo(bar: "Hello") # String value
  116. foo(bar: false) # Boolean value
  117. foo(bar: null) # Null value
  118. foo(bar: MOBILE_WEB) # Enum value
  119. foo(bar: [1,2,[A,"B"]]) # List value
  120. foo(bar: {foo:"bar"
  121. foo2 : [12],
  122. foo3 : { X:Y }
  123. }) # Object value
  124. }
  125. `
  126. expectedOutput = `
  127. Document
  128. ExecutableDefinition
  129. OperationDefinition
  130. SelectionSet
  131. Field
  132. Name: foo
  133. Arguments
  134. Argument
  135. Name: bar
  136. Variable: Hello
  137. Field
  138. Name: foo
  139. Arguments
  140. Argument
  141. Name: bar
  142. Value: 1
  143. Field
  144. Name: foo
  145. Arguments
  146. Argument
  147. Name: bar
  148. Value: 1.1
  149. Field
  150. Name: foo
  151. Arguments
  152. Argument
  153. Name: bar
  154. Value: Hello
  155. Field
  156. Name: foo
  157. Arguments
  158. Argument
  159. Name: bar
  160. Value: false
  161. Field
  162. Name: foo
  163. Arguments
  164. Argument
  165. Name: bar
  166. Value: null
  167. Field
  168. Name: foo
  169. Arguments
  170. Argument
  171. Name: bar
  172. EnumValue: MOBILE_WEB
  173. Field
  174. Name: foo
  175. Arguments
  176. Argument
  177. Name: bar
  178. ListValue
  179. Value: 1
  180. Value: 2
  181. ListValue
  182. EnumValue: A
  183. Value: B
  184. Field
  185. Name: foo
  186. Arguments
  187. Argument
  188. Name: bar
  189. ObjectValue
  190. ObjectField: foo
  191. Value: bar
  192. ObjectField: foo2
  193. ListValue
  194. Value: 12
  195. ObjectField: foo3
  196. ObjectValue
  197. ObjectField: X
  198. EnumValue: Y
  199. `[1:]
  200. expectedPPResult := `{
  201. foo(bar: $Hello)
  202. foo(bar: 1)
  203. foo(bar: 1.1)
  204. foo(bar: "Hello")
  205. foo(bar: false)
  206. foo(bar: null)
  207. foo(bar: MOBILE_WEB)
  208. foo(bar: [1, 2, [A, "B"]])
  209. foo(bar: {foo : "bar", foo2 : [12], foo3 : {X : Y}})
  210. }`
  211. if err := testPrettyPrinting(input, expectedOutput,
  212. expectedPPResult); err != nil {
  213. t.Error(err)
  214. return
  215. }
  216. input = `{
  217. my : field
  218. }`
  219. expectedOutput = `
  220. Document
  221. ExecutableDefinition
  222. OperationDefinition
  223. SelectionSet
  224. Field
  225. Alias: my
  226. Name: field
  227. `[1:]
  228. if err := testPrettyPrinting(input, expectedOutput,
  229. input); err != nil {
  230. t.Error(err)
  231. return
  232. }
  233. input = `query getBozoProfile ($devicePicSize: Int, $foo: bar=123) {
  234. user(id: 4) {
  235. id
  236. name
  237. profilePic(size: $devicePicSize)
  238. }
  239. }`
  240. expectedOutput = `
  241. Document
  242. ExecutableDefinition
  243. OperationDefinition
  244. OperationType: query
  245. Name: getBozoProfile
  246. VariableDefinitions
  247. VariableDefinition
  248. Variable: devicePicSize
  249. Type: Int
  250. VariableDefinition
  251. Variable: foo
  252. Type: bar
  253. DefaultValue: 123
  254. SelectionSet
  255. Field
  256. Name: user
  257. Arguments
  258. Argument
  259. Name: id
  260. Value: 4
  261. SelectionSet
  262. Field
  263. Name: id
  264. Field
  265. Name: name
  266. Field
  267. Name: profilePic
  268. Arguments
  269. Argument
  270. Name: size
  271. Variable: devicePicSize
  272. `[1:]
  273. if err := testPrettyPrinting(input, expectedOutput,
  274. input); err != nil {
  275. t.Error(err)
  276. return
  277. }
  278. input = `
  279. query withNestedFragments {
  280. user(id: 4) {
  281. friends(first: 10) {
  282. ...friendFields
  283. }
  284. mutualFriends(first: 10) {
  285. ...friendFields
  286. }
  287. }
  288. }
  289. fragment friendFields on User {
  290. id
  291. name
  292. ...standardProfilePic
  293. }
  294. fragment standardProfilePic on User {
  295. profilePic(size: 50)
  296. }`[1:]
  297. expectedOutput = `
  298. Document
  299. ExecutableDefinition
  300. OperationDefinition
  301. OperationType: query
  302. Name: withNestedFragments
  303. SelectionSet
  304. Field
  305. Name: user
  306. Arguments
  307. Argument
  308. Name: id
  309. Value: 4
  310. SelectionSet
  311. Field
  312. Name: friends
  313. Arguments
  314. Argument
  315. Name: first
  316. Value: 10
  317. SelectionSet
  318. FragmentSpread: friendFields
  319. Field
  320. Name: mutualFriends
  321. Arguments
  322. Argument
  323. Name: first
  324. Value: 10
  325. SelectionSet
  326. FragmentSpread: friendFields
  327. ExecutableDefinition
  328. FragmentDefinition
  329. FragmentName: friendFields
  330. TypeCondition: User
  331. SelectionSet
  332. Field
  333. Name: id
  334. Field
  335. Name: name
  336. FragmentSpread: standardProfilePic
  337. ExecutableDefinition
  338. FragmentDefinition
  339. FragmentName: standardProfilePic
  340. TypeCondition: User
  341. SelectionSet
  342. Field
  343. Name: profilePic
  344. Arguments
  345. Argument
  346. Name: size
  347. Value: 50
  348. `[1:]
  349. if err := testPrettyPrinting(input, expectedOutput,
  350. input); err != nil {
  351. t.Error(err)
  352. return
  353. }
  354. input = `
  355. query inlineFragmentTyping {
  356. profiles(handles: ["zuck", "cocacola"]) {
  357. handle
  358. ... on User {
  359. friends {
  360. count
  361. }
  362. }
  363. ... on Page {
  364. likers {
  365. count
  366. }
  367. }
  368. }
  369. }`[1:]
  370. expectedOutput = `
  371. Document
  372. ExecutableDefinition
  373. OperationDefinition
  374. OperationType: query
  375. Name: inlineFragmentTyping
  376. SelectionSet
  377. Field
  378. Name: profiles
  379. Arguments
  380. Argument
  381. Name: handles
  382. ListValue
  383. Value: zuck
  384. Value: cocacola
  385. SelectionSet
  386. Field
  387. Name: handle
  388. InlineFragment
  389. TypeCondition: User
  390. SelectionSet
  391. Field
  392. Name: friends
  393. SelectionSet
  394. Field
  395. Name: count
  396. InlineFragment
  397. TypeCondition: Page
  398. SelectionSet
  399. Field
  400. Name: likers
  401. SelectionSet
  402. Field
  403. Name: count
  404. `[1:]
  405. if err := testPrettyPrinting(input, expectedOutput,
  406. input); err != nil {
  407. t.Error(err)
  408. return
  409. }
  410. input = `
  411. {
  412. my : field(size: 4) @include(if: true) @id() @foo(x: 1, y: "z")
  413. }`[1:]
  414. expectedOutput = `
  415. Document
  416. ExecutableDefinition
  417. OperationDefinition
  418. SelectionSet
  419. Field
  420. Alias: my
  421. Name: field
  422. Arguments
  423. Argument
  424. Name: size
  425. Value: 4
  426. Directives
  427. Directive
  428. Name: include
  429. Arguments
  430. Argument
  431. Name: if
  432. Value: true
  433. Directive
  434. Name: id
  435. Arguments
  436. Directive
  437. Name: foo
  438. Arguments
  439. Argument
  440. Name: x
  441. Value: 1
  442. Argument
  443. Name: y
  444. Value: z
  445. `[1:]
  446. if err := testPrettyPrinting(input, expectedOutput,
  447. input); err != nil {
  448. t.Error(err)
  449. return
  450. }
  451. }
  452. func TestErrorCases(t *testing.T) {
  453. astres, _ := ParseWithRuntime("mytest", `{ a }`, &TestRuntimeProvider{})
  454. astres.Children[0].Name = "foo"
  455. _, err := PrettyPrint(astres)
  456. if err == nil || err.Error() != "Could not find template for foo (tempkey: foo_1)" {
  457. t.Error("Unexpected result:", err)
  458. return
  459. }
  460. astres, err = ParseWithRuntime("mytest", `{ a(b:"""a"a""" x:""){ d} }`, &TestRuntimeProvider{})
  461. if err != nil {
  462. t.Error(err)
  463. return
  464. }
  465. pp, _ := PrettyPrint(astres)
  466. astres, err = ParseWithRuntime("mytest", pp, &TestRuntimeProvider{})
  467. if err != nil {
  468. t.Error(err)
  469. return
  470. }
  471. if astres.String() != `
  472. Document
  473. ExecutableDefinition
  474. OperationDefinition
  475. SelectionSet
  476. Field
  477. Name: a
  478. Arguments
  479. Argument
  480. Name: b
  481. Value: a"a
  482. Argument
  483. Name: x
  484. Value:
  485. SelectionSet
  486. Field
  487. Name: d
  488. `[1:] {
  489. t.Error("Unexpected result:", astres)
  490. return
  491. }
  492. }
  493. func testPPOut(input string) (string, error) {
  494. var ppres string
  495. astres, err := ParseWithRuntime("mytest", input, &TestRuntimeProvider{})
  496. if err == nil {
  497. ppres, err = PrettyPrint(astres)
  498. }
  499. return ppres, err
  500. }
  501. func testPrettyPrinting(input, astOutput, ppOutput string) error {
  502. astres, err := ParseWithRuntime("mytest", input, &TestRuntimeProvider{})
  503. if err != nil || fmt.Sprint(astres) != astOutput {
  504. return fmt.Errorf("Unexpected parser output:\n%v expected was:\n%v Error: %v", astres, astOutput, err)
  505. }
  506. ppres, err := PrettyPrint(astres)
  507. if err != nil || ppres != ppOutput {
  508. fmt.Fprintf(os.Stderr, "#\n%v#", ppres)
  509. return fmt.Errorf("Unexpected result:\n%v\nError: %v", ppres, err)
  510. }
  511. // Make sure the pretty printed result is valid and gets the same parse tree
  512. astres2, err := ParseWithRuntime("mytest", ppres, &TestRuntimeProvider{})
  513. if err != nil || fmt.Sprint(astres2) != astOutput {
  514. return fmt.Errorf("Unexpected parser output from pretty print string:\n%v expected was:\n%v Error: %v", astres2, astOutput, err)
  515. }
  516. return nil
  517. }