summaryrefslogtreecommitdiff
path: root/unittests/Format/FormatTestJS.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'unittests/Format/FormatTestJS.cpp')
-rw-r--r--unittests/Format/FormatTestJS.cpp208
1 files changed, 203 insertions, 5 deletions
diff --git a/unittests/Format/FormatTestJS.cpp b/unittests/Format/FormatTestJS.cpp
index 230717fe47cc..d8fa8e4b942c 100644
--- a/unittests/Format/FormatTestJS.cpp
+++ b/unittests/Format/FormatTestJS.cpp
@@ -132,6 +132,8 @@ TEST_F(FormatTestJS, ReservedWords) {
verifyFormat("x.interface = 1;");
verifyFormat("x.for = 1;");
verifyFormat("x.of() = 1;");
+ verifyFormat("of(null);");
+ verifyFormat("import {of} from 'x';");
verifyFormat("x.in() = 1;");
verifyFormat("x.let() = 1;");
verifyFormat("x.var() = 1;");
@@ -167,6 +169,8 @@ TEST_F(FormatTestJS, ReservedWordsMethods) {
TEST_F(FormatTestJS, CppKeywords) {
// Make sure we don't mess stuff up because of C++ keywords.
verifyFormat("return operator && (aa);");
+ // .. or QT ones.
+ verifyFormat("slots: Slot[];");
}
TEST_F(FormatTestJS, ES6DestructuringAssignment) {
@@ -240,6 +244,18 @@ TEST_F(FormatTestJS, ContainerLiterals) {
"};");
verifyFormat("var x = {y: (a) => a};");
+ // Methods in object literals.
+ verifyFormat("var x = {\n"
+ " y(a: string): number {\n"
+ " return a;\n"
+ " }\n"
+ "};");
+ verifyFormat("var x = {\n"
+ " y(a: string) {\n"
+ " return a;\n"
+ " }\n"
+ "};");
+
// Computed keys.
verifyFormat("var x = {[a]: 1, b: 2, [c]: 3};");
verifyFormat("var x = {\n"
@@ -266,6 +282,11 @@ TEST_F(FormatTestJS, ContainerLiterals) {
" aaa,\n"
" aaa,\n"
"};");
+ verifyFormat("return {\n"
+ " a,\n"
+ " b: 'b',\n"
+ " c,\n"
+ "};");
}
TEST_F(FormatTestJS, MethodsInObjectLiterals) {
@@ -451,6 +472,8 @@ TEST_F(FormatTestJS, AsyncFunctions) {
verifyFormat("export async function f() {\n"
" return fetch(x);\n"
"}");
+ verifyFormat("let x = async () => f();");
+ verifyFormat("let x = async();");
verifyFormat("class X {\n"
" async asyncMethod() {\n"
" return fetch(1);\n"
@@ -497,6 +520,11 @@ TEST_F(FormatTestJS, ArrayLiterals) {
" [];");
verifyFormat("someFunction([], {a: a});");
+
+ verifyFormat("var string = [\n"
+ " 'aaaaaa',\n"
+ " 'bbbbbb',\n"
+ "].join('+');");
}
TEST_F(FormatTestJS, ColumnLayoutForArrayLiterals) {
@@ -587,6 +615,11 @@ TEST_F(FormatTestJS, FunctionLiterals) {
" doSomething();\n"
"}, this));");
+ verifyFormat("SomeFunction(function() {\n"
+ " foo();\n"
+ " bar();\n"
+ "}.bind(this));");
+
// FIXME: This is bad, we should be wrapping before "function() {".
verifyFormat("someFunction(function() {\n"
" doSomething(); // break\n"
@@ -858,13 +891,25 @@ TEST_F(FormatTestJS, AutomaticSemicolonInsertionHeuristic) {
"return 1",
"a = null\n"
" return 1");
+ // Below "class Y {}" should ideally be on its own line.
verifyFormat(
"x = {\n"
" a: 1\n"
- "}\n"
- "class Y {}",
+ "} class Y {}",
" x = {a : 1}\n"
" class Y { }");
+ verifyFormat(
+ "if (x) {\n"
+ "}\n"
+ "return 1",
+ "if (x) {}\n"
+ " return 1");
+ verifyFormat(
+ "if (x) {\n"
+ "}\n"
+ "class X {}",
+ "if (x) {}\n"
+ " class X {}");
}
TEST_F(FormatTestJS, ImportExportASI) {
@@ -873,11 +918,17 @@ TEST_F(FormatTestJS, ImportExportASI) {
"export function z() {}",
"import {x} from 'y'\n"
" export function z() {}");
+ // Below "class Y {}" should ideally be on its own line.
verifyFormat(
- "export {x}\n"
- "class Y {}",
+ "export {x} class Y {}",
" export {x}\n"
" class Y {\n}");
+ verifyFormat(
+ "if (x) {\n"
+ "}\n"
+ "export class Y {}",
+ "if ( x ) { }\n"
+ " export class Y {}");
}
TEST_F(FormatTestJS, ClosureStyleCasts) {
@@ -913,6 +964,7 @@ TEST_F(FormatTestJS, RegexLiteralClassification) {
verifyFormat("var x = a ? /abc/ : /abc/;");
verifyFormat("for (var i = 0; /abc/.test(s[i]); i++) {\n}");
verifyFormat("var x = !/abc/.test(y);");
+ verifyFormat("var x = foo()! / 10;");
verifyFormat("var x = a && /abc/.test(y);");
verifyFormat("var x = a || /abc/.test(y);");
verifyFormat("var x = a + /abc/.search(y);");
@@ -1018,6 +1070,15 @@ TEST_F(FormatTestJS, RegexLiteralExamples) {
verifyFormat("var regex = search.match(/(?:\?|&)times=([^?&]+)/i);");
}
+TEST_F(FormatTestJS, IgnoresMpegTS) {
+ std::string MpegTS(200, ' ');
+ MpegTS.replace(0, strlen("nearlyLooks + like + ts + code; "),
+ "nearlyLooks + like + ts + code; ");
+ MpegTS[0] = 0x47;
+ MpegTS[188] = 0x47;
+ verifyFormat(MpegTS, MpegTS);
+}
+
TEST_F(FormatTestJS, TypeAnnotations) {
verifyFormat("var x: string;");
verifyFormat("var x: {a: string; b: number;} = {};");
@@ -1045,6 +1106,9 @@ TEST_F(FormatTestJS, TypeAnnotations) {
verifyFormat("function someFunc(args: string[]):\n"
" {longReturnValue: string[]} {}",
getGoogleJSStyleWithColumns(60));
+ verifyFormat(
+ "var someValue = (v as aaaaaaaaaaaaaaaaaaaa<T>[])\n"
+ " .someFunction(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);");
}
TEST_F(FormatTestJS, UnionIntersectionTypes) {
@@ -1072,6 +1136,10 @@ TEST_F(FormatTestJS, ClassDeclarations) {
verifyFormat("class C {\n static x(): string {\n return 'asd';\n }\n}");
verifyFormat("class C extends P implements I {}");
verifyFormat("class C extends p.P implements i.I {}");
+ verifyFormat(
+ "x(class {\n"
+ " a(): A {}\n"
+ "});");
verifyFormat("class Test {\n"
" aaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaa: aaaaaaaaaaaaaaaaaaaa):\n"
" aaaaaaaaaaaaaaaaaaaaaa {}\n"
@@ -1164,6 +1232,20 @@ TEST_F(FormatTestJS, TypeAliases) {
"class C {}");
}
+TEST_F(FormatTestJS, TypeInterfaceLineWrapping) {
+ const FormatStyle &Style = getGoogleJSStyleWithColumns(20);
+ verifyFormat("type LongTypeIsReallyUnreasonablyLong =\n"
+ " string;\n",
+ "type LongTypeIsReallyUnreasonablyLong = string;\n",
+ Style);
+ verifyFormat(
+ "interface AbstractStrategyFactoryProvider {\n"
+ " a: number\n"
+ "}\n",
+ "interface AbstractStrategyFactoryProvider { a: number }\n",
+ Style);
+}
+
TEST_F(FormatTestJS, Modules) {
verifyFormat("import SomeThing from 'some/module.js';");
verifyFormat("import {X, Y} from 'some/module.js';");
@@ -1351,6 +1433,62 @@ TEST_F(FormatTestJS, TemplateStrings) {
"var y;");
// Escaped dollar.
verifyFormat("var x = ` \\${foo}`;\n");
+
+ // The token stream can contain two string_literals in sequence, but that
+ // doesn't mean that they are implicitly concatenated in JavaScript.
+ verifyFormat("var f = `aaaa ${a ? 'a' : 'b'}`;");
+
+ // Ensure that scopes are appropriately set around evaluated expressions in
+ // template strings.
+ verifyFormat("var f = `aaaaaaaaaaaaa:${aaaaaaa.aaaaa} aaaaaaaa\n"
+ " aaaaaaaaaaaaa:${aaaaaaa.aaaaa} aaaaaaaa`;",
+ "var f = `aaaaaaaaaaaaa:${aaaaaaa. aaaaa} aaaaaaaa\n"
+ " aaaaaaaaaaaaa:${ aaaaaaa. aaaaa} aaaaaaaa`;");
+ verifyFormat("var x = someFunction(`${})`) //\n"
+ " .oooooooooooooooooon();");
+ verifyFormat("var x = someFunction(`${aaaa}${\n"
+ " aaaaa( //\n"
+ " aaaaa)\n"
+ " })`);");
+}
+
+TEST_F(FormatTestJS, TemplateStringMultiLineExpression) {
+ verifyFormat("var f = `aaaaaaaaaaaaaaaaaa: ${\n"
+ " aaaaa + //\n"
+ " bbbb\n"
+ " }`;",
+ "var f = `aaaaaaaaaaaaaaaaaa: ${aaaaa + //\n"
+ " bbbb}`;");
+ verifyFormat("var f = `\n"
+ " aaaaaaaaaaaaaaaaaa: ${\n"
+ " aaaaa + //\n"
+ " bbbb\n"
+ " }`;",
+ "var f = `\n"
+ " aaaaaaaaaaaaaaaaaa: ${ aaaaa + //\n"
+ " bbbb }`;");
+ verifyFormat("var f = `\n"
+ " aaaaaaaaaaaaaaaaaa: ${\n"
+ " someFunction(\n"
+ " aaaaa + //\n"
+ " bbbb)\n"
+ " }`;",
+ "var f = `\n"
+ " aaaaaaaaaaaaaaaaaa: ${someFunction (\n"
+ " aaaaa + //\n"
+ " bbbb)}`;");
+
+ // It might be preferable to wrap before "someFunction".
+ verifyFormat("var f = `\n"
+ " aaaaaaaaaaaaaaaaaa: ${someFunction({\n"
+ " aaaa: aaaaa,\n"
+ " bbbb: bbbbb,\n"
+ " })}`;",
+ "var f = `\n"
+ " aaaaaaaaaaaaaaaaaa: ${someFunction ({\n"
+ " aaaa: aaaaa,\n"
+ " bbbb: bbbbb,\n"
+ " })}`;");
}
TEST_F(FormatTestJS, TemplateStringASI) {
@@ -1387,6 +1525,9 @@ TEST_F(FormatTestJS, CastSyntax) {
verifyFormat("x = x as {a: string};");
verifyFormat("x = x as (string);");
verifyFormat("x = x! as (string);");
+ verifyFormat("var x = something.someFunction() as\n"
+ " something;",
+ getGoogleJSStyleWithColumns(40));
}
TEST_F(FormatTestJS, TypeArguments) {
@@ -1460,6 +1601,58 @@ TEST_F(FormatTestJS, JSDocAnnotations) {
" * @export {this.is.a.long.path.to.a.Type}\n"
" */",
getGoogleJSStyleWithColumns(20));
+ verifyFormat("/**\n"
+ " * @mods {this.is.a.long.path.to.a.Type}\n"
+ " */",
+ "/**\n"
+ " * @mods {this.is.a.long.path.to.a.Type}\n"
+ " */",
+ getGoogleJSStyleWithColumns(20));
+ verifyFormat("/**\n"
+ " * @param {this.is.a.long.path.to.a.Type}\n"
+ " */",
+ "/**\n"
+ " * @param {this.is.a.long.path.to.a.Type}\n"
+ " */",
+ getGoogleJSStyleWithColumns(20));
+ verifyFormat("/**\n"
+ " * @see http://very/very/long/url/is/long\n"
+ " */",
+ "/**\n"
+ " * @see http://very/very/long/url/is/long\n"
+ " */",
+ getGoogleJSStyleWithColumns(20));
+ verifyFormat(
+ "/**\n"
+ " * @param This is a\n"
+ " * long comment but\n"
+ " * no type\n"
+ " */",
+ "/**\n"
+ " * @param This is a long comment but no type\n"
+ " */",
+ getGoogleJSStyleWithColumns(20));
+ // Don't break @param line, but reindent it and reflow unrelated lines.
+ verifyFormat("{\n"
+ " /**\n"
+ " * long long long\n"
+ " * long\n"
+ " * @param {this.is.a.long.path.to.a.Type} a\n"
+ " * long long long\n"
+ " * long long\n"
+ " */\n"
+ " function f(a) {}\n"
+ "}",
+ "{\n"
+ "/**\n"
+ " * long long long long\n"
+ " * @param {this.is.a.long.path.to.a.Type} a\n"
+ " * long long long long\n"
+ " * long\n"
+ " */\n"
+ " function f(a) {}\n"
+ "}",
+ getGoogleJSStyleWithColumns(20));
}
TEST_F(FormatTestJS, RequoteStringsSingle) {
@@ -1532,6 +1725,12 @@ TEST_F(FormatTestJS, NonNullAssertionOperator) {
verifyFormat("let x = (foo)!;\n");
verifyFormat("let x = foo! - 1;\n");
verifyFormat("let x = {foo: 1}!;\n");
+ verifyFormat(
+ "let x = hello.foo()!\n"
+ " .foo()!\n"
+ " .foo()!\n"
+ " .foo()!;\n",
+ getGoogleJSStyleWithColumns(20));
}
TEST_F(FormatTestJS, Conditional) {
@@ -1548,6 +1747,5 @@ TEST_F(FormatTestJS, ImportComments) {
getGoogleJSStyleWithColumns(25));
verifyFormat("// taze: x from 'location'", getGoogleJSStyleWithColumns(10));
}
-
} // end namespace tooling
} // end namespace clang