prettyprinter_test.go 13 KB

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