Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ajgateno committed Oct 31, 2024
1 parent a8fd1c7 commit 8c9a312
Show file tree
Hide file tree
Showing 5 changed files with 260 additions and 23 deletions.
3 changes: 2 additions & 1 deletion generators/typescript/codegen/src/ast/CodeBlock.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { CodeBlock as CommonCodeBlock } from "@fern-api/generator-commons";
import { AstNode, Writer } from "../typescript";
import { AstNode } from "./core/AstNode";
import { Writer } from "./core/Writer";

export declare namespace CodeBlock {
/* Write arbitrary code */
Expand Down
65 changes: 46 additions & 19 deletions generators/typescript/codegen/src/ast/TypeLiteral.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ interface Object_ {
interface String_ {
type: "string";
value: string;
multiline?: boolean;
}

interface Tuple {
Expand Down Expand Up @@ -62,6 +61,7 @@ interface ArrayField {
interface ObjectField {
type: "objectField";
name: string;
valueType: Type;
value: TypeLiteral;
}

Expand Down Expand Up @@ -96,17 +96,13 @@ export class TypeLiteral extends AstNode {
break;
}
case "string": {
if (this.internalType.multiline ?? false) {
if (this.internalType.value.includes("\n")) {
writer.write("`");
if (this.internalType.value.includes("\n")) {
const parts = this.internalType.value.split("\n");
const head = parts[0] + "\n";
const tail = parts.slice(1, -1).join("\n");
writer.write(head);
writer.writeNoIndent(tail);
} else {
writer.write(this.internalType.value);
}
const parts = this.internalType.value.split("\n");
const head = parts[0] + "\n";
const tail = parts.slice(1).join("\n");
writer.write(head.replaceAll("`", "\\`"));
writer.writeNoIndent(tail.replaceAll("`", "\\`"));
writer.write("`");
} else {
writer.write(`"${this.internalType.value.replaceAll('"', '\\"')}"`);
Expand All @@ -126,7 +122,7 @@ export class TypeLiteral extends AstNode {
private writeIterable(writer: Writer, value: IterableLiteral<IterableLiteralField>) {
if (value.fields.length === 0) {
// Don't allow "multiline" empty collections.
writer.writeTextStatement(`${value.leftBrace}${value.rightBrace}`);
writer.write(`${value.leftBrace}${value.rightBrace}`);
} else if (value.multiline ?? false) {
writer.writeLine(`${value.leftBrace}`);
writer.indent();
Expand All @@ -135,9 +131,9 @@ export class TypeLiteral extends AstNode {
writer.writeLine(",");
}
writer.dedent();
writer.writeTextStatement("]");
writer.write(`${value.rightBrace}`);
} else {
writer.write("[");
writer.write(`${value.leftBrace}`);
const init = value.fields.slice(0, -1);
const last = value.fields[value.fields.length - 1];
for (const elem of init) {
Expand All @@ -146,9 +142,9 @@ export class TypeLiteral extends AstNode {
}
// Need for eslint; last cannot be null because of the first if
if (last != null) {
last.value.write(writer);
this.writeIterableField(writer, last);
}
writer.writeTextStatement("]");
writer.write(`${value.rightBrace}`);
}
}

Expand Down Expand Up @@ -190,6 +186,13 @@ export class TypeLiteral extends AstNode {
});
}

public static arrayField(value: TypeLiteral): ArrayField {
return {
type: "arrayField",
value
};
}

public static boolean(value: boolean): TypeLiteral {
return new this({ type: "boolean", value });
}
Expand All @@ -208,11 +211,27 @@ export class TypeLiteral extends AstNode {
});
}

public static string({ value, multiline }: { value: string; multiline?: boolean }): TypeLiteral {
public static objectField({
name,
valueType,
value
}: {
name: string;
valueType: Type;
value: TypeLiteral;
}): ObjectField {
return {
type: "objectField",
name,
valueType,
value
};
}

public static string(value: string): TypeLiteral {
return new this({
type: "string",
value,
multiline
value
});
}

Expand All @@ -225,4 +244,12 @@ export class TypeLiteral extends AstNode {
rightBrace: "]"
});
}

public static tupleField({ valueType, value }: { valueType: Type; value: TypeLiteral }): TupleField {
return {
type: "tupleField",
valueType,
value
};
}
}
139 changes: 139 additions & 0 deletions generators/typescript/codegen/src/ast/__test__/TypeLiteral.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,149 @@
import { ts } from "../..";

describe("TypeLiteral", () => {
describe("emptyArrayToString", () => {
it("Should generate an empty array", () => {
const literal = ts.TypeLiteral.array({
valueType: ts.Type.string(),
fields: []
});
expect(literal.toStringFormatted()).toMatchSnapshot();
});
});

describe("arrayOfStringsToString", () => {
it("Should generate an array of strings", () => {
const literal = ts.TypeLiteral.array({
valueType: ts.Type.string(),
fields: [
ts.TypeLiteral.arrayField(ts.TypeLiteral.string("Hello, World!")),
ts.TypeLiteral.arrayField(ts.TypeLiteral.string("Goodbye, World!"))
]
});
expect(literal.toStringFormatted()).toMatchSnapshot();
});
});

// N.B. If the array is too short prettier is going to print it on a single line
describe("multilineArrayOfStringsToString", () => {
it("Should generate an array of strings", () => {
const literal = ts.TypeLiteral.array({
valueType: ts.Type.string(),
fields: [
ts.TypeLiteral.arrayField(ts.TypeLiteral.string("Hello, World!")),
ts.TypeLiteral.arrayField(ts.TypeLiteral.string("Goodbye, World!")),
ts.TypeLiteral.arrayField(ts.TypeLiteral.string("Hello, World!")),
ts.TypeLiteral.arrayField(ts.TypeLiteral.string("Goodbye, World!")),
ts.TypeLiteral.arrayField(ts.TypeLiteral.string("Hello, World!")),
ts.TypeLiteral.arrayField(ts.TypeLiteral.string("Goodbye, World!")),
ts.TypeLiteral.arrayField(ts.TypeLiteral.string("Hello, World!")),
ts.TypeLiteral.arrayField(ts.TypeLiteral.string("Goodbye, World!"))
],
multiline: true
});
expect(literal.toStringFormatted()).toMatchSnapshot();
});
});

describe("trueBooleanToString", () => {
it("Should generate a true boolean", () => {
const literal = ts.TypeLiteral.boolean(true);
expect(literal.toStringFormatted()).toMatchSnapshot();
});
});

describe("falseBooleanToString", () => {
it("Should generate a true boolean", () => {
const literal = ts.TypeLiteral.boolean(false);
expect(literal.toStringFormatted()).toMatchSnapshot();
});
});

describe("numberToString", () => {
it("Should generate a simple number", () => {
const literal = ts.TypeLiteral.number(7);
expect(literal.toStringFormatted()).toMatchSnapshot();
});
});

describe("stringToString", () => {
it("Should generate a simple string literal", () => {
const literal = ts.TypeLiteral.string("Hello, World!");
expect(literal.toStringFormatted()).toMatchSnapshot();
});
});

describe("stringWithDoubleQuotesToString", () => {
it("Should generate a simple string literal with escaped double quotes", () => {
const literal = ts.TypeLiteral.string('"Hello, World!"');
expect(literal.toStringFormatted()).toMatchSnapshot();
});
});

describe("manyLinesMultilineStringToString", () => {
it("Should generate a multiline string with backticks", () => {
const literal = ts.TypeLiteral.string(`Hello,
World!`);
expect(literal.toStringFormatted()).toMatchSnapshot();
});
});

describe("manyLinesMultilineStringWithBackticksToString", () => {
it("Should generate a multiline string with escaped backticks", () => {
const literal = ts.TypeLiteral.string(`\`Hello,
World!\``);
expect(literal.toStringFormatted()).toMatchSnapshot();
});
});

describe("simpleObjectToString", () => {
it("Should generate a simple object", () => {
const actual = ts.codeblock((writer) => {
writer.write("let myObj = ");
writer.writeNode(
ts.TypeLiteral.object({
fields: [
ts.TypeLiteral.objectField({
name: "name",
valueType: ts.Type.string(),
value: ts.TypeLiteral.string("John Smith")
}),
ts.TypeLiteral.objectField({
name: "hometown",
valueType: ts.Type.string(),
value: ts.TypeLiteral.string("New York, New York")
})
]
})
);
});
expect(actual.toStringFormatted()).toMatchSnapshot();
});
});

describe("multilineObjectToString", () => {
it("Should generate a multiline object", () => {
const actual = ts.codeblock((writer) => {
writer.write("let myObj = ");
writer.writeNode(
ts.TypeLiteral.object({
fields: [
ts.TypeLiteral.objectField({
name: "name",
valueType: ts.Type.string(),
value: ts.TypeLiteral.string("John Smith")
}),
ts.TypeLiteral.objectField({
name: "hometown",
valueType: ts.Type.string(),
value: ts.TypeLiteral.string("New York, New York")
})
],
multiline: true
})
);
});
expect(actual.toStringFormatted()).toMatchSnapshot();
});
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,75 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`TypeLiteral > numberToString > Should generate a simple number`] = `
"7
exports[`TypeLiteral > arrayOfStringsToString > Should generate an array of strings 1`] = `
"["Hello, World!", "Goodbye, World!"];
"
`;

exports[`TypeLiteral > emptyArrayToString > Should generate an empty array 1`] = `
"[];
"
`;

exports[`TypeLiteral > falseBooleanToString > Should generate a true boolean 1`] = `
"false;
"
`;

exports[`TypeLiteral > manyLinesMultilineStringToString > Should generate a multiline string with backticks 1`] = `
"\`Hello,
World!\`;
"
`;

exports[`TypeLiteral > manyLinesMultilineStringWithBackticksToString > Should generate a multiline string with escaped backticks 1`] = `
"\`\\\`Hello,
World!\\\`\`;
"
`;

exports[`TypeLiteral > multilineArrayOfStringsToString > Should generate an array of strings 1`] = `
"[
"Hello, World!",
"Goodbye, World!",
"Hello, World!",
"Goodbye, World!",
"Hello, World!",
"Goodbye, World!",
"Hello, World!",
"Goodbye, World!",
];
"
`;

exports[`TypeLiteral > multilineObjectToString > Should generate a multiline object 1`] = `
"let myObj = {
name: "John Smith",
hometown: "New York, New York",
};
"
`;

exports[`TypeLiteral > numberToString > Should generate a simple number 1`] = `
"7;
"
`;

exports[`TypeLiteral > simpleObjectToString > Should generate a simple object 1`] = `
"let myObj = { name: "John Smith", hometown: "New York, New York" };
"
`;

exports[`TypeLiteral > stringToString > Should generate a simple string literal 1`] = `
""Hello, World!";
"
`;

exports[`TypeLiteral > stringWithDoubleQuotesToString > Should generate a simple string literal with escaped double quotes 1`] = `
""\\"Hello, World!\\"";
"
`;

exports[`TypeLiteral > trueBooleanToString > Should generate a true boolean 1`] = `
"true;
"
`;
2 changes: 1 addition & 1 deletion generators/typescript/codegen/src/ast/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * from "./core";
export { AstNode, Writer } from "./core";
export { CodeBlock } from "./CodeBlock";
export { Type } from "./Type";
export { TypeLiteral } from "./TypeLiteral";
Expand Down

0 comments on commit 8c9a312

Please sign in to comment.