/**
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
*
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.tools.rhinodo.rhino;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mozilla.javascript.*;
import org.mozilla.javascript.commonjs.module.ModuleScope;
import org.mozilla.javascript.commonjs.module.ModuleScript;
import org.mozilla.javascript.commonjs.module.ModuleScriptProvider;
import org.mule.tools.rhinodo.api.NodeModule;
import org.mule.tools.rhinodo.impl.ExitCallbackExecutor;
import org.mule.tools.rhinodo.impl.NodeModuleProviderImpl;
import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Queue;
import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class NodeRequireTest {
@Rule
public TemporaryFolder folder = new TemporaryFolder();
/**
* Require of a file 'tree.js' when there is a directory 'tree/'
* and it's imported like require('path/to/file/tree').
*/
@Test
public void testSameNameFolderAndFolderName() throws Exception {
final File base = folder.newFolder("base");
final File tree = new File(base, "tree");
tree.mkdir();
final File treeJavascriptFile = new File(base, "tree.js");
FileUtils.write(treeJavascriptFile, "hello");
final Queue asyncCallback = mock(Queue.class);
final ScriptableObject globalScope = new NodeJsGlobal();
final ModuleScriptProvider moduleScriptProvider = mock(ModuleScriptProvider.class);
ModuleScript moduleScript = mock(ModuleScript.class);
when(moduleScript.getUri()).thenReturn(treeJavascriptFile.toURI());
when(moduleScript.getBase()).thenReturn(treeJavascriptFile.toURI());
when(moduleScript.getScript()).thenReturn(new Script() {
@Override
public Object exec(Context cx, Scriptable scope) {
return null;
}
});
when(moduleScriptProvider.getModuleScript(any(Context.class), eq(treeJavascriptFile.getAbsolutePath()),
any(URI.class), any(URI.class), any(Scriptable.class))).thenReturn(moduleScript);
final Script pre = mock(Script.class);
final Script post = mock(Script.class);
final ExitCallbackExecutor exitCallbackExecutor =
mock(ExitCallbackExecutor.class);
ContextFactory contextFactory = new ContextFactory();
contextFactory.call(new ContextAction() {
@Override
public Object run(Context cx) {
cx.initStandardObjects(globalScope);
NodeRequire nodeRequire = new NodeRequire(asyncCallback,cx.newObject(globalScope), cx ,globalScope,
moduleScriptProvider, pre, post, false, exitCallbackExecutor);
try {
Object call = nodeRequire.call(cx, globalScope, globalScope, new Object[]{tree.getAbsolutePath()});
} catch (EvaluatorException e) {
fail("It should work and open the .js file!");
}
return null;
}
});
}
/**
* Require of a directory fails
*/
@Test(expected = JavaScriptException.class)
public void testDirectoryRequire() throws Exception {
final File tree = folder.newFolder("tree");
final Queue asyncCallback = mock(Queue.class);
final ScriptableObject globalScope = new NodeJsGlobal();
final NodeJsUrlModuleSourceProvider moduleSourceProvider = mock(NodeJsUrlModuleSourceProvider.class);
final ModuleScriptProvider moduleScriptProvider = mock(ModuleScriptProvider.class);
final Script pre = mock(Script.class);
final Script post = mock(Script.class);
final ExitCallbackExecutor exitCallbackExecutor =
mock(ExitCallbackExecutor.class);
ContextFactory contextFactory = new ContextFactory();
contextFactory.call(new ContextAction() {
@Override
public Object run(Context cx) {
cx.initStandardObjects(globalScope);
NodeRequire nodeRequire = new NodeRequire(asyncCallback,cx.newObject(globalScope), cx,globalScope,
moduleScriptProvider, pre, post, false, exitCallbackExecutor);
nodeRequire.call(cx,globalScope,globalScope, new Object[]{tree.getAbsolutePath()});
fail();
return null;
}
});
}
/**
* Require of a directory that contains a package.json file.
*/
@Test
public void testRequirePackageJsonDirectory() throws Exception {
final File module = folder.newFolder("my_module");
final File packageJSON = new File(module, "package.json");
final File otherModule = folder.newFolder("my_module", "node_modules", "other_module");
final File mainJs = new File(module, "main.js");
final File otherModulePackageJSON = new File(otherModule, "package.json");
final File otherModuleMain = new File(otherModule, "main.js");
final ObjectMapper objectMapper = new ObjectMapper();
HashMap<String, Object> packageJSONMap = new HashMap<String, Object>();
packageJSONMap.put("main", "./main.js");
HashMap<String, String> dependencies = new HashMap<String, String>();
dependencies.put("other_module", "*");
packageJSONMap.put("dependencies", dependencies);
objectMapper.writeValue(packageJSON, packageJSONMap);
HashMap<String, Object> otherModulePackageJSONMap = new HashMap<String, Object>();
otherModulePackageJSONMap.put("main", "./main");
otherModulePackageJSONMap.put("name", "other_module");
objectMapper.writeValue(otherModulePackageJSON, otherModulePackageJSONMap);
FileUtils.write(mainJs, "exports.hello = 'bye';" +
"exports.other = require('other_module');");
FileUtils.write(otherModuleMain, "exports.text = 'second';");
final Queue asyncCallback = mock(Queue.class);
final NodeJsGlobal globalScope = new NodeJsGlobal();
final ExitCallbackExecutor exitCallbackExecutor =
mock(ExitCallbackExecutor.class);
ContextFactory contextFactory = new ContextFactory();
contextFactory.call(new ContextAction() {
@Override
public Object run(Context cx) {
cx.initStandardObjects(globalScope);
NodeRequireBuilder rb = new NodeRequireBuilder(asyncCallback, exitCallbackExecutor);
globalScope.installNodeJsRequire(cx,cx.newObject(globalScope),new NodeModuleProviderImpl(
new ArrayList<NodeModule>()),
rb, false);
Function nodeRequire = ScriptableObject.getTypedProperty(
globalScope, "require", Function.class);
NativeObject main = (NativeObject) nodeRequire.call(cx, globalScope, globalScope,
new Object[]{module.getAbsolutePath()});
assertNotNull(main);
assertEquals("bye", ScriptableObject.getTypedProperty(main, "hello", String.class));
assertFalse(ScriptableObject.NOT_FOUND.equals(main));
NativeObject other = ScriptableObject.getTypedProperty(main,
"other", NativeObject.class);
assertNotNull(other);
assertEquals("second", ScriptableObject.getProperty(other, "text"));
return null;
}
});
}
@Test
public void testRelativeRequire() throws Exception {
final File module = folder.newFolder("my_module");
final File otherFile = folder.newFolder("my_module","other_file");
final File mainJs = new File(module, "my_module.js");
final File otherModuleMain = new File(module, "other_file.js");
FileUtils.write(mainJs, "exports.hello = 'bye';" +
"exports.other = require('./other_file');");
FileUtils.write(otherModuleMain, "exports.text = 'second';");
final ExitCallbackExecutor exitCallbackExecutor =
mock(ExitCallbackExecutor.class);
final Queue asyncCallback = mock(Queue.class);
final NodeJsGlobal globalScope = new NodeJsGlobal();
ContextFactory contextFactory = new ContextFactory();
contextFactory.call(new ContextAction() {
@Override
public Object run(Context cx) {
cx.initStandardObjects(globalScope);
NodeRequireBuilder rb = new NodeRequireBuilder(asyncCallback, exitCallbackExecutor);
globalScope.installNodeJsRequire(cx,cx.newObject(globalScope),new NodeModuleProviderImpl(
new ArrayList<NodeModule>()),
rb, false);
Function nodeRequire = ScriptableObject.getTypedProperty(
globalScope, "require", Function.class);
String absolutePath = mainJs.getAbsolutePath();
NativeObject main = (NativeObject) nodeRequire.call(cx, globalScope, globalScope,
new Object[]{absolutePath.substring(0, absolutePath.length() - 3)});
assertNotNull(main);
assertEquals("bye", ScriptableObject.getTypedProperty(main, "hello", String.class));
assertFalse(ScriptableObject.NOT_FOUND.equals(main));
NativeObject other = ScriptableObject.getTypedProperty(main,
"other", NativeObject.class);
assertNotNull(other);
assertEquals("second", ScriptableObject.getProperty(other, "text"));
return null;
}
});
}
/**
* Test require.extensions existence
*/
@Test
public void extensions() throws Exception {
ContextFactory contextFactory = new ContextFactory();
contextFactory.call(new ContextAction() {
@Override
public Object run(Context cx) {
final Queue asyncCallback = mock(Queue.class);
final ScriptableObject globalScope = new NodeJsGlobal(cx);
final ModuleScriptProvider moduleScriptProvider = mock(ModuleScriptProvider.class);
final Script pre = mock(Script.class);
final Script post = mock(Script.class);
final ExitCallbackExecutor exitCallbackExecutor =
mock(ExitCallbackExecutor.class);
NodeRequire nodeRequire = new NodeRequire(asyncCallback, cx.newObject(globalScope),
cx, globalScope, moduleScriptProvider,
pre, post, false, exitCallbackExecutor);
NativeObject extensions = ScriptableObject.getTypedProperty(nodeRequire, "extensions",
NativeObject.class);
assertNotNull(extensions);
assertTrue(ScriptableObject.hasProperty(extensions, ".js"));
return null;
}
});
}
/**
* Test adding a new filetype to require.extensions
*/
@Test
public void testAddNewExtension() throws Exception {
final File fileDotPose = folder.newFile("hello.pose");
FileUtils.write(fileDotPose, "This is a .pose file");
final ExitCallbackExecutor exitCallbackExecutor =
mock(ExitCallbackExecutor.class);
ContextFactory contextFactory = new ContextFactory();
contextFactory.call(new ContextAction() {
@Override
public Object run(Context cx) {
final Queue asyncCallback = mock(Queue.class);
final ScriptableObject globalScope = new NodeJsGlobal();
cx.initStandardObjects(globalScope);
final NodeJsUrlModuleSourceProvider moduleSourceProvider = mock(NodeJsUrlModuleSourceProvider.class);
final ModuleScriptProvider moduleScriptProvider = mock(ModuleScriptProvider.class);
final Script pre = mock(Script.class);
final Script post = mock(Script.class);
NodeRequire nodeRequire = new NodeRequire(asyncCallback, cx.newObject(globalScope), cx, globalScope,
moduleScriptProvider, pre, post, false, exitCallbackExecutor);
NativeObject extensions = ScriptableObject.getTypedProperty(nodeRequire, "extensions",
NativeObject.class);
Function compileExtension = mock(Function.class);
assertNotNull(extensions);
ScriptableObject.putProperty(extensions, ".pose", compileExtension);
assertTrue(ScriptableObject.hasProperty(extensions, ".js"));
assertTrue(ScriptableObject.hasProperty(extensions, ".pose"));
nodeRequire.call(cx,globalScope,globalScope,new Object[]{fileDotPose.getAbsolutePath()});
return null;
}
});
}
@Test
public void testTryExtensions() throws Exception {
final File file = folder.newFile("hello.pose");
FileUtils.write(file, "This is a .pose file");
final ExitCallbackExecutor exitCallbackExecutor =
mock(ExitCallbackExecutor.class);
ContextFactory contextFactory = new ContextFactory();
contextFactory.call(new ContextAction() {
@Override
public Object run(Context cx) {
final Queue asyncCallback = mock(Queue.class);
final ScriptableObject globalScope = new NodeJsGlobal();
cx.initStandardObjects(globalScope);
final ModuleScriptProvider moduleScriptProvider = mock(ModuleScriptProvider.class);
final Script pre = mock(Script.class);
final Script post = mock(Script.class);
NodeRequire nodeRequire = new NodeRequire(asyncCallback, cx.newObject(globalScope), cx, globalScope,
moduleScriptProvider, pre, post, false, exitCallbackExecutor);
Scriptable extensions = ScriptableObject.getTypedProperty(nodeRequire, "extensions", Scriptable.class);
BaseFunction myExtensionCallback = new BaseFunction() {
};
ScriptableObject.putProperty(extensions, ".pose", myExtensionCallback);
ModuleScope moduleScope = mock(ModuleScope.class);
when(moduleScope.getUri()).thenReturn(file.getParentFile().toURI());
when(moduleScope.getBase()).thenReturn(file.getParentFile().toURI());
NodeRequire.TryExtensionsResult extensionFound =
nodeRequire.tryExtensions(FilenameUtils.concat(file.getParent(),
FilenameUtils.getBaseName(file.getAbsolutePath())), moduleScope);
assertNotNull(extensionFound);
assertEquals(".pose", extensionFound.getExtensionAsString());
assertSame(myExtensionCallback, extensionFound.getCallback());
return null;
}
});
}
@Test
public void dotSlash() throws Exception {
final File file = folder.newFolder("myfolder");
final File file1 = new File(file, "file1.js");
final File index = new File(file, "index.js");
FileUtils.write(index, "exports.msg = 'bar';");
FileUtils.write(file1, "var x = require('./'); exports.foo = x.msg;");
final ExitCallbackExecutor exitCallbackExecutor =
mock(ExitCallbackExecutor.class);
final Queue asyncCallback = mock(Queue.class);
final NodeJsGlobal globalScope = new NodeJsGlobal();
ContextFactory contextFactory = new ContextFactory();
contextFactory.call(new ContextAction() {
@Override
public Object run(Context cx) {
cx.initStandardObjects(globalScope);
NodeRequireBuilder rb = new NodeRequireBuilder(asyncCallback, exitCallbackExecutor);
globalScope.installNodeJsRequire(cx,cx.newObject(globalScope),new NodeModuleProviderImpl(
new ArrayList<NodeModule>()),
rb, false);
Function nodeRequire = ScriptableObject.getTypedProperty(
globalScope, "require", Function.class);
String absolutePath = file1.getAbsolutePath();
Scriptable file1Object = (Scriptable) nodeRequire.call(cx, globalScope, globalScope,
new Object[]{absolutePath.substring(0, absolutePath.length() - 3)});
assertNotNull(file1Object);
assertEquals("bar", ScriptableObject.getTypedProperty(file1Object, "foo", String.class));
return Undefined.instance;
}
});
}
}