Mirror: The spec-compliant minimum of client-side GraphQL.
1import { describe, it, expect } from 'vitest';
2import { readFileSync } from 'fs';
3
4import { parse, print as graphql_print } from 'graphql';
5import { print, printString, printBlockString } from '../printer';
6
7function dedentString(string) {
8 const trimmedStr = string
9 .replace(/^\n*/m, '') // remove leading newline
10 .replace(/[ \t\n]*$/, ''); // remove trailing spaces and tabs
11 // fixes indentation by removing leading spaces and tabs from each line
12 let indent = '';
13 for (const char of trimmedStr) {
14 if (char !== ' ' && char !== '\t') {
15 break;
16 }
17 indent += char;
18 }
19
20 return trimmedStr.replace(RegExp('^' + indent, 'mg'), ''); // remove indent
21}
22
23function dedent(strings, ...values) {
24 let str = strings[0];
25 for (let i = 1; i < strings.length; ++i) str += values[i - 1] + strings[i]; // interpolation
26 return dedentString(str);
27}
28
29describe('printString', () => {
30 it('prints strings as expected', () => {
31 expect(printString('test')).toEqual('"test"');
32 expect(printString('\n')).toEqual('"\\n"');
33 });
34});
35
36describe('printBlockString', () => {
37 it('prints block strings as expected', () => {
38 expect(printBlockString('test')).toEqual('"""\ntest\n"""');
39 expect(printBlockString('\n')).toEqual('"""\n\n\n"""');
40 expect(printBlockString('"""')).toEqual('"""\n\\"""\n"""');
41 });
42});
43
44describe('print', () => {
45 it('prints the kitchen sink document like graphql.js does', () => {
46 const sink = JSON.parse(readFileSync(__dirname + '/kitchen_sink.json', { encoding: 'utf8' }));
47 const doc = print(sink);
48 expect(doc).toMatchSnapshot();
49 expect(doc).toEqual(graphql_print(sink));
50 });
51
52 it('prints minimal ast', () => {
53 expect(
54 print({
55 kind: 'Field',
56 name: { kind: 'Name', value: 'foo' },
57 } as any)
58 ).toBe('foo');
59
60 expect(
61 print({
62 kind: 'Name',
63 value: 'foo',
64 } as any)
65 ).toBe('foo');
66
67 expect(
68 print({
69 kind: 'Document',
70 definitions: [],
71 } as any)
72 ).toBe('');
73 });
74
75 it('prints integers and floats', () => {
76 expect(
77 print({
78 kind: 'IntValue',
79 value: '12',
80 } as any)
81 ).toBe('12');
82
83 expect(
84 print({
85 kind: 'FloatValue',
86 value: '12e2',
87 } as any)
88 ).toBe('12e2');
89 });
90
91 it('prints lists of values', () => {
92 expect(
93 print({
94 kind: 'ListValue',
95 values: [{ kind: 'NullValue' }],
96 } as any)
97 ).toBe('[null]');
98 });
99
100 it('prints types', () => {
101 expect(
102 print({
103 kind: 'ListType',
104 type: {
105 kind: 'NonNullType',
106 type: {
107 kind: 'NamedType',
108 name: {
109 kind: 'Name',
110 value: 'Type',
111 },
112 },
113 },
114 } as any)
115 ).toBe('[Type!]');
116 });
117
118 // NOTE: The shim won't throw for invalid AST nodes
119 it('returns empty strings for invalid AST', () => {
120 const badAST = { random: 'Data' };
121 expect(print(badAST as any)).toBe('');
122 });
123
124 it('correctly prints non-query operations without name', () => {
125 const queryASTShorthanded = parse('query { id, name }');
126 expect(print(queryASTShorthanded)).toBe(dedent`
127 {
128 id
129 name
130 }
131 `);
132
133 const mutationAST = parse('mutation { id, name }');
134 expect(print(mutationAST)).toBe(dedent`
135 mutation {
136 id
137 name
138 }
139 `);
140
141 const queryASTWithArtifacts = parse('query ($foo: TestType) @testDirective { id, name }');
142 expect(print(queryASTWithArtifacts)).toBe(dedent`
143 query ($foo: TestType) @testDirective {
144 id
145 name
146 }
147 `);
148
149 const mutationASTWithArtifacts = parse('mutation ($foo: TestType) @testDirective { id, name }');
150 expect(print(mutationASTWithArtifacts)).toBe(dedent`
151 mutation ($foo: TestType) @testDirective {
152 id
153 name
154 }
155 `);
156 });
157
158 it('prints query with variable directives', () => {
159 const queryASTWithVariableDirective = parse(
160 'query ($foo: TestType = {a: 123} @testDirective(if: true) @test) { id }'
161 );
162 expect(print(queryASTWithVariableDirective)).toBe(dedent`
163 query ($foo: TestType = {a: 123} @testDirective(if: true) @test) {
164 id
165 }
166 `);
167 });
168
169 it('keeps arguments on one line if line is short (<= 80 chars)', () => {
170 const printed = print(parse('{trip(wheelchair:false arriveBy:false){dateTime}}'));
171
172 expect(printed).toBe(
173 dedent`
174 {
175 trip(wheelchair: false, arriveBy: false) {
176 dateTime
177 }
178 }
179 `
180 );
181 });
182});