/*
Copyright 2013-2016 Jason Leyba
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.github.jsdossier;
import static com.google.common.truth.Truth.assertThat;
import com.github.jsdossier.annotations.Input;
import com.github.jsdossier.annotations.ModulePrefix;
import com.github.jsdossier.annotations.Output;
import com.github.jsdossier.annotations.SourcePrefix;
import com.github.jsdossier.jscomp.Module;
import com.github.jsdossier.jscomp.NominalType;
import com.github.jsdossier.jscomp.TypeRegistry;
import com.github.jsdossier.testing.CompilerUtil;
import com.github.jsdossier.testing.GuiceRule;
import com.google.common.base.Joiner;
import com.google.javascript.jscomp.CompilerOptions;
import com.google.javascript.jscomp.SourceFile;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.nio.file.FileSystem;
import java.nio.file.Path;
import javax.inject.Inject;
/**
* Tests for {@link DossierFileSystem}.
*/
@RunWith(JUnit4.class)
public class DossierFileSystemTest {
@Rule
public final GuiceRule guice = GuiceRule.builder(this)
.setSourcePrefix("/input/src")
.setModulePrefix("/input/module")
.setModules("one.js", "two.js", "foo/bar.js", "foo/bar/index.js")
.setOutputDir("/out")
.setModuleNamingConvention(ModuleNamingConvention.ES6)
.setLanguageIn(CompilerOptions.LanguageMode.ECMASCRIPT6_STRICT)
.build();
@Inject
@Input
private FileSystem fs;
@Inject
@SourcePrefix
private Path srcPrefix;
@Inject
@ModulePrefix
private Path modulePrefix;
@Inject
private TypeRegistry typeRegistry;
@Inject
@Output
private Path outputRoot;
@Inject
private CompilerUtil util;
@Inject
private DossierFileSystem sut;
@Test
public void canGetThePathToARenderedSourceFile() {
Path path = sut.getPath(srcPrefix.resolve("foo/bar/baz.js"));
assertThat(path.toString()).isEqualTo(
outputRoot.resolve("source/foo/bar/baz.js.src.html").toString());
}
@Test
public void getSourceRelativePath() {
Path path = sut.getSourceRelativePath(srcPrefix.resolve("foo/bar/baz.js"));
assertThat(path.toString()).isEqualTo("foo/bar/baz.js");
}
@Test
public void getPathToAMarkdownPage() {
MarkdownPage page = new MarkdownPage("Readme",
outputRoot.getFileSystem().getPath("Readme.html"));
Path path = sut.getPath(page);
assertThat(path.toString()).isEqualTo("/out/page/Readme.html");
path = sut.getJsonPath(page);
assertThat(path.toString()).isEqualTo("/out/data/page/Readme.json");
}
@Test
public void canGetThePathToANominalType() {
util.compile(fs.getPath("foo.js"),
"goog.provide('foo.bar.Baz');",
"foo.bar.Baz = class {};");
NominalType type = typeRegistry.getType("foo.bar.Baz");
assertThat(sut.getPath(type).toString()).isEqualTo(
outputRoot.resolve("foo.bar.Baz.html").toString());
}
@Test
public void canGetThePathToAClosureModule() {
util.compile(fs.getPath("/input/src/foo.js"),
"goog.module('a.b.c');",
"exports.D = class {};");
Module module = typeRegistry.getModule("module$exports$a$b$c");
assertThat(sut.getPath(module).toString())
.isEqualTo(outputRoot.resolve("a.b.c.html").toString());
NominalType type = typeRegistry.getType(module.getId());
assertThat(sut.getPath(type).toString())
.isEqualTo(outputRoot.resolve("a.b.c.html").toString());
type = typeRegistry.getType(module.getId() + ".D");
assertThat(sut.getPath(type).toString())
.isEqualTo(outputRoot.resolve("a.b.c.D.html").toString());
}
@Test
public void canGetThePathToAModule_index() {
Path path = fs.getPath("/input/module/foo/bar/index.js");
util.compile(path, "exports.x = 1;");
Module module = typeRegistry.getModule(path);
assertThat(sut.getPath(module).toString())
.isEqualTo(outputRoot.resolve("module/foo/bar/index.html").toString());
}
@Test
public void canGetThePathToAModule() {
Path path = fs.getPath("/input/module/foo/bar.js");
util.compile(path, "exports.x = 1;");
Module module = typeRegistry.getModule(path);
assertThat(sut.getPath(module).toString())
.isEqualTo(outputRoot.resolve("module/foo/bar.html").toString());
}
@Test
public void canGetThePathToAModuleExportedType() {
util.compile(fs.getPath("/input/module/foo/bar.js"),
"exports.Clazz = class {};");
NominalType type = typeRegistry.getType("module$exports$module$$input$module$foo$bar.Clazz");
Path path = sut.getPath(type);
assertThat(path.toString())
.isEqualTo(outputRoot.resolve("module/foo/bar_exports_Clazz.html").toString());
}
@Test
public void canGetThePathToAModuleExportedType_exportedFromIndex() {
util.compile(fs.getPath("/input/module/foo/bar/index.js"),
"exports.Clazz = class {};");
NominalType type =
typeRegistry.getType("module$exports$module$$input$module$foo$bar$index.Clazz");
Path path = sut.getPath(type);
assertThat(path.toString())
.isEqualTo(outputRoot.resolve("module/foo/bar/index_exports_Clazz.html").toString());
}
@Test
public void getModuleDisplayName_indexWithNodeConventions() {
setNamingConvention(ModuleNamingConvention.NODE);
Path path = fs.getPath("/input/module/foo/bar/index.js");
util.compile(path, "exports.Clazz = class {};");
Module module = typeRegistry.getModule(path);
assertThat(sut.getDisplayName(module)).isEqualTo("foo/bar");
}
@Test
public void getModuleDisplayName_indexWithEs6Conventions() {
setNamingConvention(ModuleNamingConvention.ES6);
Path path = fs.getPath("/input/module/foo/bar/index.js");
util.compile(path, "export class Clazz {}");
Module module = typeRegistry.getModule(path);
assertThat(sut.getDisplayName(module)).isEqualTo("foo/bar/index");
}
@Test
public void getModuleDisplayName_notIndex_nodeConventions() {
setNamingConvention(ModuleNamingConvention.NODE);
Path path = fs.getPath("/input/module/foo/bar.js");
util.compile(path, "exports.Clazz = class {};");
Module module = typeRegistry.getModule(path);
assertThat(sut.getDisplayName(module)).isEqualTo("foo/bar");
}
@Test
public void getModuleDisplayName_notIndex_es6Conventions() {
setNamingConvention(ModuleNamingConvention.ES6);
Path path = fs.getPath("/input/module/foo/bar.js");
util.compile(path, "export class Clazz {}");
Module module = typeRegistry.getModule(path);
assertThat(sut.getDisplayName(module)).isEqualTo("foo/bar");
}
@Test
public void getModuleDisplayName_indexClashesWithSiblingInParentDir_es6Conventions() {
setNamingConvention(ModuleNamingConvention.ES6);
Path p1 = fs.getPath("/input/module/foo/bar.js");
Path p2 = fs.getPath("/input/module/foo/bar/index.js");
util.compile(
createSourceFile(p1, "export class X {}"),
createSourceFile(p2, "export class Y {}"));
assertThat(sut.getDisplayName(typeRegistry.getModule(p1))).isEqualTo("foo/bar");
assertThat(sut.getDisplayName(typeRegistry.getModule(p2))).isEqualTo("foo/bar/index");
}
@Test
public void getModuleDisplayName_indexClashesWithSiblingInParentDir_nodeConventions() {
setNamingConvention(ModuleNamingConvention.NODE);
Path p1 = fs.getPath("/input/module/foo/bar.js");
Path p2 = fs.getPath("/input/module/foo/bar/index.js");
util.compile(
createSourceFile(p1, "export class X {}"),
createSourceFile(p2, "export class Y {}"));
assertThat(sut.getDisplayName(typeRegistry.getModule(p1))).isEqualTo("foo/bar");
assertThat(sut.getDisplayName(typeRegistry.getModule(p2))).isEqualTo("foo/bar/");
}
@Test
public void getGoogModuleDisplayName() {
util.compile(fs.getPath("foo.js"),
"goog.module('foo.bar.baz');");
Module module = typeRegistry.getModule("module$exports$foo$bar$baz");
assertThat(sut.getDisplayName(module)).isEqualTo("foo.bar.baz");
}
@Test
public void getGoogModuleDisplayName_withLegacyNamespace_1() {
util.compile(fs.getPath("foo.js"),
"goog.module('foo.bar');",
"goog.module.declareLegacyNamespace();");
NominalType type = typeRegistry.getType("foo.bar");
assertThat(sut.getDisplayName(type)).isEqualTo("foo.bar");
}
@Test
public void getGoogModuleDisplayName_withLegacyNamespace_2() {
util.compile(fs.getPath("foo.js"),
"goog.module('foo.bar.baz');",
"goog.module.declareLegacyNamespace();");
NominalType type = typeRegistry.getType("foo.bar.baz");
assertThat(sut.getDisplayName(type)).isEqualTo("foo.bar.baz");
}
@Test
public void getModuleExportedTypeDisplayName_es6Module() {
Path path = fs.getPath("/input/module/foo/bar.js");
util.compile(path, "export class Foo {}");
NominalType type = typeRegistry.getType("module$$input$module$foo$bar.Foo");
assertThat(sut.getDisplayName(type)).isEqualTo("Foo");
}
@Test
public void getModuleExportedTypeDisplayName_nodeModule() {
Path path = fs.getPath("/input/module/foo/bar.js");
util.compile(path, "exports.Foo = class {};");
NominalType type = typeRegistry.getType("module$exports$module$$input$module$foo$bar.Foo");
assertThat(sut.getDisplayName(type)).isEqualTo("Foo");
}
@Test
public void getGoogModuleExportedTypeDisplayName() {
util.compile(fs.getPath("foo.js"),
"goog.module('foo.bar');",
"exports.Baz = class {};");
NominalType type = typeRegistry.getType("module$exports$foo$bar.Baz");
assertThat(sut.getDisplayName(type)).isEqualTo("Baz");
}
@Test
public void getGoogModuleExportedTypeDisplayName_moduleHasLegacyNamespace() {
util.compile(fs.getPath("foo.js"),
"goog.module('foo.bar');",
"goog.module.declareLegacyNamespace();",
"exports.Baz = class {};");
NominalType type = typeRegistry.getType("foo.bar.Baz");
assertThat(sut.getDisplayName(type)).isEqualTo("Baz");
}
@Test
public void getRelativePath_fromGlobalType() {
util.compile(fs.getPath("foo.js"),
"class Baz {}");
NominalType type = typeRegistry.getType("Baz");
Path path = sut.getPath(srcPrefix.resolve("foo/bar/baz.js"));
assertThat(sut.getRelativePath(type, path).toString()).isEqualTo(
"source/foo/bar/baz.js.src.html");
}
@Test
public void getRelativePath_fromModuleType() {
util.compile(fs.getPath("/input/module/foo/bar/baz.js"),
"export class Baz {}");
NominalType type = typeRegistry.getType("module$$input$module$foo$bar$baz.Baz");
Path path = sut.getPath(srcPrefix.resolve("foo/bar/baz.js"));
assertThat(sut.getRelativePath(type, path).toString()).isEqualTo(
"../../../source/foo/bar/baz.js.src.html");
}
@Test
public void getRelativePath_betweenTypesExportedByTheSameModule() {
util.compile(fs.getPath("/input/module/foo/bar/baz.js"),
"export class One {}",
"export class Two {}");
NominalType a = typeRegistry.getType("module$$input$module$foo$bar$baz.One");
NominalType b = typeRegistry.getType("module$$input$module$foo$bar$baz.Two");
assertThat(sut.getRelativePath(a, b).toString()).isEqualTo("baz_exports_Two.html");
assertThat(sut.getRelativePath(b, a).toString()).isEqualTo("baz_exports_One.html");
}
@Test
public void getRelativePath_betweenTypesExportedByModulesInTheSameDirectory() {
util.compile(
createSourceFile(fs.getPath("/input/module/one.js"), "export class One {}"),
createSourceFile(fs.getPath("/input/module/two.js"), "export class Two {}"));
NominalType a = typeRegistry.getType("module$$input$module$one.One");
NominalType b = typeRegistry.getType("module$$input$module$two.Two");
assertThat(sut.getRelativePath(a, b).toString()).isEqualTo("two_exports_Two.html");
assertThat(sut.getRelativePath(b, a).toString()).isEqualTo("one_exports_One.html");
}
@Test
public void getRelativePath_betweenTypesExportedByModulesInDifferentDirectories() {
util.compile(
createSourceFile(fs.getPath("/input/module/one.js"), "export class One {}"),
createSourceFile(fs.getPath("/input/module/bar/two.js"), "export class Two {}"));
NominalType a = typeRegistry.getType("module$$input$module$one.One");
NominalType b = typeRegistry.getType("module$$input$module$bar$two.Two");
assertThat(sut.getRelativePath(a, b).toString()).isEqualTo("bar/two_exports_Two.html");
assertThat(sut.getRelativePath(b, a).toString()).isEqualTo("../one_exports_One.html");
}
@Test
public void getQualifiedDisplayName_globalType() {
util.compile(fs.getPath("foo.js"),
"class One {}");
NominalType type = typeRegistry.getType("One");
assertThat(sut.getDisplayName(type)).isEqualTo("One");
assertThat(sut.getQualifiedDisplayName(type)).isEqualTo("One");
}
@Test
public void getQualifiedDisplayName_namespacedType() {
util.compile(fs.getPath("foo.js"),
"goog.provide('one.two.Three');",
"one.two.Three = class {};");
NominalType type = typeRegistry.getType("one.two.Three");
assertThat(sut.getDisplayName(type)).isEqualTo("one.two.Three");
assertThat(sut.getQualifiedDisplayName(type)).isEqualTo("one.two.Three");
}
@Test
public void getQualifiedDisplayName_closureModuleType() {
util.compile(fs.getPath("foo.js"),
"goog.module('one.two');",
"exports.Three = class {};");
NominalType type = typeRegistry.getType("module$exports$one$two.Three");
assertThat(sut.getDisplayName(type)).isEqualTo("Three");
assertThat(sut.getQualifiedDisplayName(type)).isEqualTo("one.two.Three");
}
@Test
public void getQualifiedDisplayName_nodeModuleType() {
util.compile(fs.getPath("/input/module/foo/bar.js"),
"exports.Three = class {};");
NominalType type = typeRegistry.getType("module$exports$module$$input$module$foo$bar.Three");
assertThat(sut.getDisplayName(type)).isEqualTo("Three");
assertThat(sut.getQualifiedDisplayName(type)).isEqualTo("foo/bar.Three");
}
@Test
public void getQualifiedDisplayName_nodeIndexModuleType() {
setNamingConvention(ModuleNamingConvention.NODE);
util.compile(fs.getPath("/input/module/foo/bar/index.js"),
"exports.Three = class {};");
NominalType type =
typeRegistry.getType("module$exports$module$$input$module$foo$bar$index.Three");
assertThat(sut.getDisplayName(type)).isEqualTo("Three");
assertThat(sut.getQualifiedDisplayName(type)).isEqualTo("foo/bar.Three");
}
@Test
public void getQualifiedDisplayName_es6ModuleType() {
util.compile(fs.getPath("/input/module/foo/bar.js"),
"export class Three {}");
NominalType type = typeRegistry.getType("module$$input$module$foo$bar.Three");
assertThat(sut.getDisplayName(type)).isEqualTo("Three");
assertThat(sut.getQualifiedDisplayName(type)).isEqualTo("foo/bar.Three");
}
@Test
public void getQualifiedDisplayName_es6IndexModuleType() {
util.compile(fs.getPath("/input/module/foo/bar/index.js"),
"export class Three {}");
NominalType type = typeRegistry.getType("module$$input$module$foo$bar$index.Three");
assertThat(sut.getDisplayName(type)).isEqualTo("Three");
assertThat(sut.getQualifiedDisplayName(type)).isEqualTo("foo/bar/index.Three");
}
@Test
public void moduleExportDefault_hasOtherName() {
util.compile(modulePrefix.resolve("a/b/c.js"),
"export default class Foo {}");
NominalType type = typeRegistry.getType("module$$input$module$a$b$c.default");
Module module = type.getModule().get();
assertThat(module.getExportedNames()).containsEntry("default", "Foo");
assertThat(sut.getDisplayName(type)).isEqualTo("default");
assertThat(sut.getQualifiedDisplayName(type)).isEqualTo("a/b/c.default");
assertThat(sut.getPath(type).toString())
.isEqualTo(outputRoot.resolve("module/a/b/c_exports_default.html").toString());
}
@Test
public void moduleExportDefault_isInternalSymbol1() {
util.compile(modulePrefix.resolve("a/b/c.js"),
"class Foo {}",
"export default Foo");
NominalType type = typeRegistry.getType("module$$input$module$a$b$c.default");
Module module = type.getModule().get();
assertThat(module.getExportedNames()).containsEntry("default", "Foo");
assertThat(sut.getDisplayName(type)).isEqualTo("default");
assertThat(sut.getQualifiedDisplayName(type)).isEqualTo("a/b/c.default");
assertThat(sut.getPath(type).toString())
.isEqualTo(outputRoot.resolve("module/a/b/c_exports_default.html").toString());
}
@Test
public void moduleExportDefault_isInternalSymbol2() {
util.compile(modulePrefix.resolve("a/b/c.js"),
"class Foo {}",
"export {Foo as default}");
NominalType type = typeRegistry.getType("module$$input$module$a$b$c.default");
Module module = type.getModule().get();
assertThat(module.getExportedNames()).containsEntry("default", "Foo");
assertThat(sut.getDisplayName(type)).isEqualTo("default");
assertThat(sut.getQualifiedDisplayName(type)).isEqualTo("a/b/c.default");
assertThat(sut.getPath(type).toString())
.isEqualTo(outputRoot.resolve("module/a/b/c_exports_default.html").toString());
}
@Test
public void moduleExportDefault_isAnonymousClass() {
util.compile(modulePrefix.resolve("a/b/c.js"),
"export default class {}");
NominalType type = typeRegistry.getType("module$$input$module$a$b$c.default");
Module module = type.getModule().get();
assertThat(module.getExportedNames()).isEmpty();
assertThat(sut.getDisplayName(type)).isEqualTo("default");
assertThat(sut.getQualifiedDisplayName(type)).isEqualTo("a/b/c.default");
assertThat(sut.getPath(type).toString())
.isEqualTo(outputRoot.resolve("module/a/b/c_exports_default.html").toString());
}
private void setNamingConvention(ModuleNamingConvention convention) {
guice.toBuilder()
.setModuleNamingConvention(convention)
.build()
.createInjector()
.injectMembers(this);
}
public static SourceFile createSourceFile(Path path, String... lines) {
return SourceFile.fromCode(path.toString(), Joiner.on("\n").join(lines));
}
}