UNPKG

12.3 kBPlain TextView Raw
1import { stripIndent } from "common-tags";
2
3import { SwiftGenerator, SwiftSource, swift } from "../language";
4import { valueFromAST } from "graphql";
5
6describe("Swift code generation: Basic language constructs", () => {
7 let generator: SwiftGenerator<any>;
8
9 beforeEach(() => {
10 generator = new SwiftGenerator({});
11 });
12
13 it(`should generate a class declaration`, () => {
14 generator.classDeclaration(
15 { className: "Hero", modifiers: ["public", "final"] },
16 () => {
17 generator.propertyDeclaration({
18 propertyName: "name",
19 typeName: "String"
20 });
21 generator.propertyDeclaration({
22 propertyName: "age",
23 typeName: "Int"
24 });
25 }
26 );
27
28 expect(generator.output).toBe(stripIndent`
29 public final class Hero {
30 public var name: String
31 public var age: Int
32 }
33 `);
34 });
35
36 it(`should generate a class declaration matching modifiers`, () => {
37 generator.classDeclaration(
38 { className: "Hero", modifiers: ["final"] },
39 () => {
40 generator.propertyDeclaration({
41 propertyName: "name",
42 typeName: "String"
43 });
44 generator.propertyDeclaration({
45 propertyName: "age",
46 typeName: "Int"
47 });
48 }
49 );
50
51 expect(generator.output).toBe(stripIndent`
52 final class Hero {
53 public var name: String
54 public var age: Int
55 }
56 `);
57 });
58
59 it(`should generate a class declaration with proper escaping`, () => {
60 generator.classDeclaration(
61 { className: "Type", modifiers: ["public", "final"] },
62 () => {
63 generator.propertyDeclaration({
64 propertyName: "name",
65 typeName: "String"
66 });
67 generator.propertyDeclaration({
68 propertyName: "age",
69 typeName: "Int"
70 });
71 generator.propertyDeclaration({
72 propertyName: "self",
73 typeName: "Self"
74 });
75 }
76 );
77
78 expect(generator.output).toBe(stripIndent`
79 public final class \`Type\` {
80 public var name: String
81 public var age: Int
82 public var \`self\`: \`Self\`
83 }
84 `);
85 });
86
87 it(`should generate a struct declaration`, () => {
88 generator.structDeclaration({ structName: "Hero" }, false, () => {
89 generator.propertyDeclaration({
90 propertyName: "name",
91 typeName: "String"
92 });
93 generator.propertyDeclaration({
94 propertyName: "age",
95 typeName: "Int"
96 });
97 });
98
99 expect(generator.output).toBe(stripIndent`
100 public struct Hero {
101 public var name: String
102 public var age: Int
103 }
104 `);
105 });
106
107 it(`should generate a namespaced fragment`, () => {
108 generator.structDeclaration(
109 {
110 structName: "Hero",
111 adoptedProtocols: ["GraphQLFragment"],
112 namespace: "StarWars"
113 },
114 false,
115 () => {
116 generator.propertyDeclaration({
117 propertyName: "name",
118 typeName: "String"
119 });
120 generator.propertyDeclaration({
121 propertyName: "age",
122 typeName: "Int"
123 });
124 }
125 );
126
127 expect(generator.output).toBe(stripIndent`
128 public struct Hero: GraphQLFragment {
129 public var name: String
130 public var age: Int
131 }
132 `);
133 });
134
135 it(`should generate a namespaced fragment which is not public for individual files`, () => {
136 generator.structDeclaration(
137 {
138 structName: "Hero",
139 adoptedProtocols: ["GraphQLFragment"],
140 namespace: "StarWars"
141 },
142 true,
143 () => {
144 generator.propertyDeclaration({
145 propertyName: "name",
146 typeName: "String"
147 });
148 generator.propertyDeclaration({
149 propertyName: "age",
150 typeName: "Int"
151 });
152 }
153 );
154
155 expect(generator.output).toBe(stripIndent`
156 struct Hero: GraphQLFragment {
157 public var name: String
158 public var age: Int
159 }
160 `);
161 });
162
163 it(`should generate an escaped struct declaration`, () => {
164 generator.structDeclaration({ structName: "Type" }, false, () => {
165 generator.propertyDeclaration({
166 propertyName: "name",
167 typeName: "String"
168 });
169 generator.propertyDeclaration({
170 propertyName: "yearOfBirth",
171 typeName: "Int"
172 });
173 generator.propertyDeclaration({
174 propertyName: "self",
175 typeName: "Self"
176 });
177 });
178
179 expect(generator.output).toBe(stripIndent`
180 public struct \`Type\` {
181 public var name: String
182 public var yearOfBirth: Int
183 public var \`self\`: \`Self\`
184 }
185 `);
186 });
187
188 it(`should generate nested struct declarations`, () => {
189 generator.structDeclaration({ structName: "Hero" }, false, () => {
190 generator.propertyDeclaration({
191 propertyName: "name",
192 typeName: "String"
193 });
194 generator.propertyDeclaration({
195 propertyName: "friends",
196 typeName: "[Friend]"
197 });
198
199 generator.structDeclaration({ structName: "Friend" }, false, () => {
200 generator.propertyDeclaration({
201 propertyName: "name",
202 typeName: "String"
203 });
204 });
205 });
206
207 expect(generator.output).toBe(stripIndent`
208 public struct Hero {
209 public var name: String
210 public var friends: [Friend]
211
212 public struct Friend {
213 public var name: String
214 }
215 }
216 `);
217 });
218
219 it(`should generate a protocol declaration`, () => {
220 generator.protocolDeclaration(
221 { protocolName: "HeroDetails", adoptedProtocols: ["HasName"] },
222 () => {
223 generator.protocolPropertyDeclaration({
224 propertyName: "name",
225 typeName: "String"
226 });
227 generator.protocolPropertyDeclaration({
228 propertyName: "age",
229 typeName: "Int"
230 });
231 generator.protocolPropertyDeclaration({
232 propertyName: "default",
233 typeName: "Boolean"
234 });
235 }
236 );
237
238 expect(generator.output).toBe(stripIndent`
239 public protocol HeroDetails: HasName {
240 var name: String { get }
241 var age: Int { get }
242 var \`default\`: Boolean { get }
243 }
244 `);
245 });
246
247 it(`should handle multi-line descriptions`, () => {
248 generator.structDeclaration(
249 { structName: "Hero", description: "A hero" },
250 false,
251 () => {
252 generator.propertyDeclaration({
253 propertyName: "name",
254 typeName: "String",
255 description: `A multiline comment \n on the hero's name.`
256 });
257 generator.propertyDeclaration({
258 propertyName: "age",
259 typeName: "String",
260 description: `A multiline comment \n on the hero's age.`
261 });
262 }
263 );
264
265 expect(generator.output).toMatchSnapshot();
266 });
267});
268
269describe("Swift code generation: Escaping", () => {
270 describe("using SwiftSource", () => {
271 it(`should escape identifiers`, () => {
272 expect(SwiftSource.identifier("self").source).toBe("`self`");
273 expect(SwiftSource.identifier("public").source).toBe("`public`");
274 expect(SwiftSource.identifier("Array<Type>").source).toBe(
275 "Array<`Type`>"
276 );
277 expect(SwiftSource.identifier("[Self?]?").source).toBe("[`Self`?]?");
278 });
279
280 it(`should not escape other words`, () => {
281 expect(SwiftSource.identifier("me").source).toBe("me");
282 expect(SwiftSource.identifier("_Self").source).toBe("_Self");
283 expect(SwiftSource.identifier("classes").source).toBe("classes");
284 });
285
286 it(`should escape fewer words in member position`, () => {
287 expect(SwiftSource.identifier(".self").source).toBe(".`self`");
288 expect(SwiftSource.identifier(".public").source).toBe(".public");
289 expect(SwiftSource.identifier("Foo.Self.Type.self.class").source).toBe(
290 "Foo.Self.`Type`.`self`.class"
291 );
292 });
293
294 it(`should escape fewer words at offset 0 with member escaping`, () => {
295 expect(SwiftSource.memberName("self").source).toBe("`self`");
296 expect(SwiftSource.memberName("public").source).toBe("public");
297 expect(SwiftSource.memberName(" public").source).toBe(" `public`");
298 expect(SwiftSource.memberName("Foo.Self.Type.self.class").source).toBe(
299 "Foo.Self.`Type`.`self`.class"
300 );
301 });
302
303 it(`should escape strings`, () => {
304 expect(SwiftSource.string("foobar").source).toBe('"foobar"');
305 expect(SwiftSource.string("foo\n bar ").source).toBe('"foo\\n bar "');
306 expect(SwiftSource.string("one'two\"three\\four\tfive").source).toBe(
307 '"one\'two\\"three\\\\four\\tfive"'
308 );
309 });
310
311 it(`should trim strings when asked`, () => {
312 expect(SwiftSource.string("foobar", true).source).toBe('"foobar"');
313 expect(SwiftSource.string("foo\n bar ", true).source).toBe('"foo bar"');
314 });
315
316 it(`should generate multiline strings`, () => {
317 expect(SwiftSource.multilineString("foobar").source).toBe(
318 '"""\nfoobar\n"""'
319 );
320 expect(SwiftSource.multilineString("foo\n bar ").source).toBe(
321 '"""\nfoo\n bar \n"""'
322 );
323 expect(SwiftSource.multilineString(`"""foo"""`).source).toBe(
324 '#"""\n"""foo"""\n"""#'
325 );
326 expect(SwiftSource.multilineString("foo\\nbar").source).toBe(
327 '#"""\nfoo\\nbar\n"""#'
328 );
329 expect(SwiftSource.multilineString(`"""\\"""#"""`).source).toBe(
330 '##"""\n"""\\"""#"""\n"""##'
331 );
332 expect(SwiftSource.multilineString(`foo\\\\#bar`).source).toBe(
333 '##"""\nfoo\\\\#bar\n"""##'
334 );
335 expect(SwiftSource.multilineString(`foo\\\\#\\##bar`).source).toBe(
336 '###"""\nfoo\\\\#\\##bar\n"""###'
337 );
338 expect(SwiftSource.multilineString("foo\\###nbar").source).toBe(
339 '####"""\nfoo\\###nbar\n"""####'
340 );
341 });
342
343 it(`should support concatenation`, () => {
344 expect(swift`one`.concat().source).toBe("one");
345 expect(swift`one`.concat(swift`two`).source).toBe("onetwo");
346 expect(swift`one`.concat(swift`two`, swift`three`).source).toBe(
347 "onetwothree"
348 );
349 });
350
351 it(`should support appending`, () => {
352 let value = swift`one`;
353 value.append();
354 expect(value.source).toBe("one");
355 value.append(swift`foo`);
356 expect(value.source).toBe("onefoo");
357 value.append(swift`bar`, swift`baz`, swift`qux`);
358 expect(value.source).toBe("onefoobarbazqux");
359 });
360 });
361 describe("using SwiftGenerator", () => {
362 let generator: SwiftGenerator<any>;
363
364 beforeEach(() => {
365 generator = new SwiftGenerator({});
366 });
367
368 it(`should not trim with multiline string if multiline strings are not suppressed and there is no triple quote`, () => {
369 generator.multilineString("foo\n bar ", false);
370
371 expect(generator.output).toBe('"""\nfoo\n bar \n"""');
372 });
373
374 it(`should trim with multilineString if multiline strings are suppressed`, () => {
375 generator.multilineString("foo\n bar ", true);
376
377 expect(generator.output).toBe('"foo bar"');
378 });
379
380 it(`shouldn't trim with multilineString when using """ even when multiline strings are suppressed`, () => {
381 generator.multilineString('"""\nfoo\n bar \n"""', true);
382 expect(generator.output).toBe('"\\"\\"\\"\\nfoo\\n bar \\n\\"\\"\\""');
383 });
384 });
385 describe("using template strings", () => {
386 it(`should escape interpolated strings but not string literals`, () => {
387 expect(swift`self`.source).toBe("self");
388 expect(swift`${"self"}`.source).toBe("`self`");
389 expect(swift`class ${"Foo.Type.self"}: ${"Protocol?"}`.source).toBe(
390 "class Foo.`Type`.`self`: `Protocol`?"
391 );
392 expect(swift`${["Self", "Foo.Self.self"]}`.source).toBe(
393 "`Self`,Foo.Self.`self`"
394 );
395 expect(swift`${true} ${"true"}`.source).toBe("true `true`");
396 expect(swift`${{ toString: () => "self" }}`.source).toBe("`self`");
397 });
398
399 it(`should not escape already-escaped interpolated strings`, () => {
400 expect(swift`${swift`${"self"}`}`.source).toBe("`self`");
401 expect(swift`${"public"} ${new SwiftSource("public")}`.source).toBe(
402 "`public` public"
403 );
404 });
405
406 it(`should not escape with the raw tag`, () => {
407 expect(SwiftSource.raw`${"self"}`.source).toBe("self");
408 });
409 });
410});
411
\No newline at end of file