generate-list-options.spec.ts 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. import { buildSchema, printType } from 'graphql';
  2. import { CustomFields } from '../../../../shared/shared-types';
  3. import { generateListOptions } from './generate-list-options';
  4. // tslint:disable:no-non-null-assertion
  5. describe('generateListOptions()', () => {
  6. const COMMON_TYPES = `
  7. scalar JSON
  8. scalar DateTime
  9. interface PaginatedList {
  10. items: [Node!]!
  11. totalItems: Int!
  12. }
  13. interface Node {
  14. id: ID!
  15. }
  16. enum SortOrder {
  17. ASC
  18. DESC
  19. }
  20. input StringOperators { dummy: String }
  21. input BooleanOperators { dummy: String }
  22. input NumberRange { dummy: String }
  23. input NumberOperators { dummy: String }
  24. input DateRange { dummy: String }
  25. input DateOperators { dummy: String }
  26. type PersonList implements PaginatedList {
  27. items: [Person!]!
  28. totalItems: Int!
  29. }
  30. `;
  31. const removeLeadingWhitespace = s => {
  32. const indent = s.match(/^\s+/m)[0].replace(/\n/, '');
  33. return s.replace(new RegExp(`^${indent}`, 'gm'), '').trim();
  34. };
  35. it('creates the required input types', () => {
  36. const input = `
  37. ${COMMON_TYPES}
  38. type Query {
  39. people(options: PersonListOptions): PersonList
  40. }
  41. type Person {
  42. name: String!
  43. age: Int!
  44. }
  45. # Generated at runtime
  46. input PersonListOptions
  47. `;
  48. const result = generateListOptions(buildSchema(input));
  49. expect(printType(result.getType('PersonListOptions')!)).toBe(
  50. removeLeadingWhitespace(`
  51. input PersonListOptions {
  52. skip: Int
  53. take: Int
  54. sort: PersonSortParameter
  55. filter: PersonFilterParameter
  56. }`),
  57. );
  58. expect(printType(result.getType('PersonSortParameter')!)).toBe(
  59. removeLeadingWhitespace(`
  60. input PersonSortParameter {
  61. name: SortOrder
  62. age: SortOrder
  63. }`),
  64. );
  65. expect(printType(result.getType('PersonFilterParameter')!)).toBe(
  66. removeLeadingWhitespace(`
  67. input PersonFilterParameter {
  68. name: StringOperators
  69. age: NumberOperators
  70. }`),
  71. );
  72. });
  73. it('works with a non-nullabel list type', () => {
  74. const input = `
  75. ${COMMON_TYPES}
  76. type Query {
  77. people: PersonList!
  78. }
  79. type Person {
  80. name: String!
  81. age: Int!
  82. }
  83. `;
  84. const result = generateListOptions(buildSchema(input));
  85. expect(result.getType('PersonListOptions')).toBeTruthy();
  86. });
  87. it('uses the correct filter operators', () => {
  88. const input = `
  89. ${COMMON_TYPES}
  90. type Query {
  91. people(options: PersonListOptions): PersonList
  92. }
  93. type Person {
  94. name: String!
  95. age: Int!
  96. updatedAt: DateTime!
  97. admin: Boolean!
  98. score: Float
  99. personType: PersonType!
  100. }
  101. enum PersonType {
  102. TABS
  103. SPACES
  104. }
  105. # Generated at runtime
  106. input PersonListOptions
  107. `;
  108. const result = generateListOptions(buildSchema(input));
  109. expect(printType(result.getType('PersonFilterParameter')!)).toBe(
  110. removeLeadingWhitespace(`
  111. input PersonFilterParameter {
  112. name: StringOperators
  113. age: NumberOperators
  114. updatedAt: DateOperators
  115. admin: BooleanOperators
  116. score: NumberOperators
  117. personType: StringOperators
  118. }`),
  119. );
  120. });
  121. it('creates the ListOptions interface and argument if not defined', () => {
  122. const input = `
  123. ${COMMON_TYPES}
  124. type Query {
  125. people: PersonList
  126. }
  127. type Person {
  128. name: String!
  129. }
  130. `;
  131. const result = generateListOptions(buildSchema(input));
  132. expect(printType(result.getType('PersonListOptions')!)).toBe(
  133. removeLeadingWhitespace(`
  134. input PersonListOptions {
  135. skip: Int
  136. take: Int
  137. sort: PersonSortParameter
  138. filter: PersonFilterParameter
  139. }`),
  140. );
  141. const args = result.getQueryType()!.getFields().people.args;
  142. expect(args.length).toBe(1);
  143. expect(args[0].name).toBe('options');
  144. expect(args[0].type.toString()).toBe('PersonListOptions');
  145. });
  146. it('extends the ListOptions interface if already defined', () => {
  147. const input = `
  148. ${COMMON_TYPES}
  149. type Query {
  150. people(options: PersonListOptions): PersonList
  151. }
  152. type Person {
  153. name: String!
  154. }
  155. input PersonListOptions {
  156. categoryId: ID
  157. }
  158. `;
  159. const result = generateListOptions(buildSchema(input));
  160. expect(printType(result.getType('PersonListOptions')!)).toBe(
  161. removeLeadingWhitespace(`
  162. input PersonListOptions {
  163. skip: Int
  164. take: Int
  165. sort: PersonSortParameter
  166. filter: PersonFilterParameter
  167. categoryId: ID
  168. }`),
  169. );
  170. const args = result.getQueryType()!.getFields().people.args;
  171. expect(args.length).toBe(1);
  172. expect(args[0].name).toBe('options');
  173. expect(args[0].type.toString()).toBe('PersonListOptions');
  174. });
  175. it('ignores properties with types which cannot be sorted or filtered', () => {
  176. const input = `
  177. ${COMMON_TYPES}
  178. type Query {
  179. people: PersonList
  180. }
  181. type Person {
  182. id: ID!
  183. name: String!
  184. vitals: [Int]
  185. meta: JSON
  186. user: User!
  187. }
  188. type User {
  189. identifier: String!
  190. }
  191. `;
  192. const result = generateListOptions(buildSchema(input));
  193. expect(printType(result.getType('PersonSortParameter')!)).toBe(
  194. removeLeadingWhitespace(`
  195. input PersonSortParameter {
  196. id: SortOrder
  197. name: SortOrder
  198. }`),
  199. );
  200. expect(printType(result.getType('PersonFilterParameter')!)).toBe(
  201. removeLeadingWhitespace(`
  202. input PersonFilterParameter {
  203. name: StringOperators
  204. }`),
  205. );
  206. });
  207. it('generates ListOptions for nested list queries', () => {
  208. const input = `
  209. ${COMMON_TYPES}
  210. type Query {
  211. people: PersonList
  212. }
  213. type Person {
  214. id: ID!
  215. orders(options: OrderListOptions): OrderList
  216. }
  217. type OrderList implements PaginatedList {
  218. items: [Order!]!
  219. totalItems: Int!
  220. }
  221. type Order {
  222. id: ID!
  223. code: String!
  224. }
  225. # Generated at runtime
  226. input OrderListOptions
  227. `;
  228. const result = generateListOptions(buildSchema(input));
  229. expect(printType(result.getType('OrderListOptions')!)).toBe(
  230. removeLeadingWhitespace(`
  231. input OrderListOptions {
  232. skip: Int
  233. take: Int
  234. sort: OrderSortParameter
  235. filter: OrderFilterParameter
  236. }`),
  237. );
  238. expect(printType(result.getType('OrderSortParameter')!)).toBe(
  239. removeLeadingWhitespace(`
  240. input OrderSortParameter {
  241. id: SortOrder
  242. code: SortOrder
  243. }`),
  244. );
  245. expect(printType(result.getType('OrderFilterParameter')!)).toBe(
  246. removeLeadingWhitespace(`
  247. input OrderFilterParameter {
  248. code: StringOperators
  249. }`),
  250. );
  251. });
  252. });