예제 #1
0
 internal override int ExecCommandOnNode(Guid cmdGroup, uint cmd, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
 {
     if (cmdGroup == Guids.NodejsCmdSet)
     {
         switch (cmd)
         {
         case PkgCmdId.cmdidOpenReplWindow:
             NodejsPackage.Instance.OpenReplWindow();
             return(VSConstants.S_OK);
         }
     }
     else if (cmdGroup == Guids.NodejsNpmCmdSet)
     {
         try
         {
             NpmHelpers.GetPathToNpm(
                 Nodejs.GetAbsoluteNodeExePath(
                     this.ProjectHome,
                     this.Project.GetNodejsProject().GetProjectProperty(NodeProjectProperty.NodeExePath)
                     ));
         }
         catch (NpmNotFoundException)
         {
             Nodejs.ShowNodejsNotInstalled();
             return(VSConstants.S_OK);
         }
     }
     return(base.ExecCommandOnNode(cmdGroup, cmd, nCmdexecopt, pvaIn, pvaOut));
 }
예제 #2
0
        protected override int ExecCommandThatDependsOnSelectedNodes(Guid cmdGroup, uint cmdId, uint cmdExecOpt, IntPtr vaIn, IntPtr vaOut, CommandOrigin commandOrigin, IList <HierarchyNode> selectedNodes, out bool handled)
        {
            if (cmdGroup == Guids.NodejsNpmCmdSet)
            {
                try {
                    NpmHelpers.GetPathToNpm(this.Project.GetNodejsProject().GetProjectProperty(NodejsConstants.NodeExePath));
                } catch (NpmNotFoundException) {
                    Nodejs.ShowNodejsNotInstalled();
                    handled = true;
                    return(VSConstants.S_OK);
                }

                switch (cmdId)
                {
                case PkgCmdId.cmdidNpmManageModules:
                    if (!ShowManageModulesCommandOnNode(selectedNodes))
                    {
                        ModulesNode.ManageModules();
                        handled = true;
                        return(VSConstants.S_OK);
                    }

                    var node = selectedNodes[0] as AbstractNpmNode;
                    if (node != null)
                    {
                        var abstractNpmNode = node;
                        abstractNpmNode.ManageNpmModules();
                        handled = true;
                        return(VSConstants.S_OK);
                    }
                    break;
                }
            }
            return(base.ExecCommandThatDependsOnSelectedNodes(cmdGroup, cmdId, cmdExecOpt, vaIn, vaOut, commandOrigin, selectedNodes, out handled));
        }
예제 #3
0
        public static string GetPathToNpm(string nodePath = null)
        {
            var path = GetNpmPathFromNodePath(nodePath);

            if (!string.IsNullOrEmpty(path))
            {
                return(path);
            }

            string executable = "npm.cmd";

            path = Nodejs.GetPathToNodeExecutableFromEnvironment(executable);

            if (string.IsNullOrEmpty(path))
            {
                throw new NpmNotFoundException(
                          string.Format(CultureInfo.CurrentCulture,
                                        "Cannot find {0} in the registry, your path, or under " +
                                        "program files in the nodejs folder.  Ensure Node.js is installed.",
                                        executable
                                        )
                          );
            }

            return(path);
        }
예제 #4
0
        private int Start(string file, bool debug)
        {
            var nodePath = GetNodePath();

            if (nodePath == null)
            {
                Nodejs.ShowNodejsNotInstalled();
                return(VSConstants.S_OK);
            }

            bool startBrowser = ShouldStartBrowser();

#if !DEV15
            bool useWebKitDebugger = false;
#else
            bool useWebKitDebugger = NodejsPackage.Instance.GeneralOptionsPage.UseWebKitDebugger;
#endif

            if (debug && !useWebKitDebugger)
            {
                StartWithDebugger(file);
            }
            else if (debug && useWebKitDebugger)
            {
                StartAndAttachDebugger(file, nodePath);
            }
            else
            {
                StartNodeProcess(file, nodePath, startBrowser);
            }

            return(VSConstants.S_OK);
        }
        public int LaunchFile(string file, bool debug)
        {
            var nodePath = GetNodePath();

            if (nodePath == null)
            {
                Nodejs.ShowNodejsNotInstalled();
                return(VSConstants.S_OK);
            }

            var nodeVersion  = Nodejs.GetNodeVersion(nodePath);
            var startBrowser = ShouldStartBrowser();

            // The call to Version.ToString() is safe, since changes to the ToString method are very unlikely, as the current output is widely documented.
            if (debug)
            {
                if (nodeVersion >= new Version(8, 0))
                {
                    StartWithChromeV2Debugger(file, nodePath, startBrowser);
                    TelemetryHelper.LogDebuggingStarted("ChromeV2", nodeVersion.ToString());
                }
                else
                {
                    StartWithDebugger(file);
                    TelemetryHelper.LogDebuggingStarted("Node6", nodeVersion.ToString());
                }
            }
            else
            {
                StartNodeProcess(file, nodePath, startBrowser);
                TelemetryHelper.LogDebuggingStarted("None", nodeVersion.ToString());
            }

            return(VSConstants.S_OK);
        }
예제 #6
0
        protected override int ExecCommandThatDependsOnSelectedNodes(Guid cmdGroup, uint cmdId, uint cmdExecOpt, IntPtr vaIn, IntPtr vaOut, CommandOrigin commandOrigin, IList <HierarchyNode> selectedNodes, out bool handled)
        {
            if (cmdGroup == Guids.NodejsNpmCmdSet)
            {
                try {
                    NpmHelpers.GetPathToNpm(
                        Nodejs.GetAbsoluteNodeExePath(
                            ProjectHome,
                            Project.GetNodejsProject().GetProjectProperty(NodeProjectProperty.NodeExePath)
                            ));
                } catch (NpmNotFoundException) {
                    Nodejs.ShowNodejsNotInstalled();
                    handled = true;
                    return(VSConstants.S_OK);
                }

                switch (cmdId)
                {
                case PkgCmdId.cmdidNpmManageModules:
                    if (!ShowManageModulesCommandOnNode(selectedNodes))
                    {
                        ModulesNode.ManageModules();
                        handled = true;
                        return(VSConstants.S_OK);
                    }

                    var node = selectedNodes[0] as AbstractNpmNode;
                    if (node != null)
                    {
                        var abstractNpmNode = node;
                        abstractNpmNode.ManageNpmModules();
                        handled = true;
                        return(VSConstants.S_OK);
                    }
                    break;
                }
            }
            else if (cmdGroup == Guids.NodejsCmdSet)
            {
                switch (cmdId)
                {
                case PkgCmdId.cmdidSetAsNodejsStartupFile:
                    // Set the StartupFile project property to the Url of this node
                    SetProjectProperty(
                        CommonConstants.StartupFile,
                        CommonUtils.GetRelativeFilePath(ProjectHome, selectedNodes[0].Url)
                        );
                    handled = true;
                    return(VSConstants.S_OK);

                case PkgCmdId.cmdidAddFileCommand:
                    NewFileMenuGroup.NewFileUtilities.CreateNewFile(projectNode: this, containerId: selectedNodes[0].ID);
                    handled = true;
                    return(VSConstants.S_OK);
                }
            }

            return(base.ExecCommandThatDependsOnSelectedNodes(cmdGroup, cmdId, cmdExecOpt, vaIn, vaOut, commandOrigin, selectedNodes, out handled));
        }
예제 #7
0
        public void CheckAbsoluteNodeExePathResolution()
        {
            var path = @"C:\Program Files\node.exe";
            var dir  = Path.GetDirectoryName(path);

            Assert.AreEqual(path, Nodejs.GetAbsoluteNodeExePath(@"C:\myprojectpath", path),
                            "Resolution should use absolute path if specified");
        }
예제 #8
0
        private int Start(string file, bool debug)
        {
            string nodePath = GetNodePath();

            if (nodePath == null)
            {
                Nodejs.ShowNodejsNotInstalled();
                return(VSConstants.S_OK);
            }

            bool startBrowser = ShouldStartBrowser();

            if (debug)
            {
                StartWithDebugger(file);
            }
            else
            {
                var psi = new ProcessStartInfo();
                psi.UseShellExecute = false;

                psi.FileName         = nodePath;
                psi.Arguments        = GetFullArguments(file);
                psi.WorkingDirectory = _project.GetWorkingDirectory();

                string webBrowserUrl = GetFullUrl();
                Uri    uri           = null;
                if (!String.IsNullOrWhiteSpace(webBrowserUrl))
                {
                    uri = new Uri(webBrowserUrl);

                    psi.EnvironmentVariables["PORT"] = uri.Port.ToString();
                }

                foreach (var nameValue in GetEnvironmentVariables())
                {
                    psi.EnvironmentVariables[nameValue.Key] = nameValue.Value;
                }

                var process = NodeProcess.Start(
                    psi,
                    NodejsPackage.Instance.GeneralOptionsPage.WaitOnAbnormalExit,
                    NodejsPackage.Instance.GeneralOptionsPage.WaitOnNormalExit);
                _project.OnDispose += process.ResponseToTerminateEvent;

                if (startBrowser && uri != null)
                {
                    OnPortOpenedHandler.CreateHandler(
                        uri.Port,
                        shortCircuitPredicate: () => process.HasExited,
                        action: () => {
                        VsShellUtilities.OpenBrowser(webBrowserUrl, (uint)__VSOSPFLAGS.OSP_LaunchNewBrowser);
                    }
                        );
                }
            }
            return(VSConstants.S_OK);
        }
예제 #9
0
 private void NodeExePathChanged(object sender, EventArgs e)
 {
     if (!string.IsNullOrEmpty(this._nodeExePath.Text) &&
         !this._nodeExePath.Text.Contains("$(") &&
         !File.Exists(Nodejs.GetAbsoluteNodeExePath(this._propPage.Project.ProjectHome, this._nodeExePath.Text)))
     {
         DisplayWarning(Resources.NodeExePathNotFound);
     }
     Changed(sender, e);
 }
예제 #10
0
 private void NodeExePathChanged(object sender, EventArgs e)
 {
     if (string.IsNullOrEmpty(this._nodeExePath.Text) || this._nodeExePath.Text.Contains("$(") ||
         File.Exists(Nodejs.GetAbsoluteNodeExePath(this._propPage.Project.ProjectHome, this._nodeExePath.Text)))
     {
         this._nodeExeErrorProvider.SetError(this._nodeExePath, string.Empty);
     }
     else
     {
         this._nodeExeErrorProvider.SetError(this._nodeExePath, Resources.NodeExePathNotFound);
     }
     Changed(sender, e);
 }
예제 #11
0
 private void NodeExePathChanged(object sender, EventArgs e)
 {
     if (String.IsNullOrEmpty(_nodeExePath.Text) || _nodeExePath.Text.Contains("$(") ||
         File.Exists(Nodejs.GetAbsoluteNodeExePath(_propPage.Project.ProjectHome, _nodeExePath.Text)))
     {
         _nodeExeErrorProvider.SetError(_nodeExePath, String.Empty);
     }
     else
     {
         _nodeExeErrorProvider.SetError(_nodeExePath, SR.GetString(SR.NodeExePathNotFound));
     }
     Changed(sender, e);
 }
예제 #12
0
        public void SetupDebugTargetInfo(ref VsDebugTargetInfo vsDebugTargetInfo, DebugLaunchActionContext debugLaunchContext)
        {
            var nodeExe = debugLaunchContext.LaunchConfiguration.GetValue(NodeExeKey, defaultValue: Nodejs.GetPathToNodeExecutableFromEnvironment());

            var nodeVersion = Nodejs.GetNodeVersion(nodeExe);

            if (nodeVersion >= new Version(8, 0) || NodejsProjectLauncher.CheckDebugProtocolOption())
            {
                SetupDebugTargetInfoForWebkitV2Protocol(ref vsDebugTargetInfo, debugLaunchContext, nodeExe);
                TelemetryHelper.LogDebuggingStarted("ChromeV2", nodeVersion.ToString(), isProject: false);
            }
            else
            {
                this.SetupDebugTargetInfoForNodeProtocol(ref vsDebugTargetInfo, debugLaunchContext, nodeExe);
                TelemetryHelper.LogDebuggingStarted("Node6", nodeVersion.ToString(), isProject: false);
            }
        }
        void IVsDebugLaunchTargetProvider.SetupDebugTargetInfo(ref VsDebugTargetInfo vsDebugTargetInfo, DebugLaunchActionContext debugLaunchContext)
        {
            var nodeExe = CheckNodeInstalledAndWarn(debugLaunchContext);

            var nodeVersion = Nodejs.GetNodeVersion(nodeExe);

            if (nodeVersion >= new Version(8, 0))
            {
                this.SetupDebugTargetInfoForInspectProtocol(ref vsDebugTargetInfo, debugLaunchContext, nodeExe);
                TelemetryHelper.LogDebuggingStarted("ChromeV2", nodeVersion.ToString(), isProject: false);
            }
            else
            {
                this.SetupDebugTargetInfoForNodeProtocol(ref vsDebugTargetInfo, debugLaunchContext, nodeExe);
                TelemetryHelper.LogDebuggingStarted("Node6", nodeVersion.ToString(), isProject: false);
            }
        }
예제 #14
0
        private string GetNodeExePath()
        {
            var    startupProject = this.site.GetStartupProject();
            string nodeExePath;

            if (startupProject != null)
            {
                nodeExePath = Nodejs.GetAbsoluteNodeExePath(
                    startupProject.ProjectHome,
                    startupProject.GetProjectProperty(NodeProjectProperty.NodeExePath)
                    );
            }
            else
            {
                nodeExePath = Nodejs.NodeExePath;
            }
            return(nodeExePath);
        }
예제 #15
0
        public void CheckRelativeNodeExePathResolution()
        {
            var path     = @"C:\mynodepath\node.exe";
            var dir      = Path.GetDirectoryName(path);
            var filename = Path.GetFileName(path);

            Assert.AreEqual(path, Nodejs.GetAbsoluteNodeExePath(dir, filename),
                            "Resolution failed when relative filename is surrounded by quotes");
            Assert.AreEqual(path, Nodejs.GetAbsoluteNodeExePath(dir, "\"" + filename + "\""),
                            "Resolution failed when relative filename is surrounded by quotes");
            Assert.AreEqual(path, Nodejs.GetAbsoluteNodeExePath(dir, "./" + filename),
                            "Resolution failed when relative filename begins with ./");
            Assert.AreEqual(path, Nodejs.GetAbsoluteNodeExePath(dir, "../" + Path.GetFileName(dir) + "/" + filename),
                            "Resolution failed when relative filename begins with ../");
            Assert.AreEqual(".", Nodejs.GetAbsoluteNodeExePath(null, "."),
                            "Resolution should return relative path on failure");
            Assert.AreEqual(Nodejs.NodeExePath, Nodejs.GetAbsoluteNodeExePath(dir, null),
                            "Resolution should fall back to environment path if no relative path is specified");
        }
예제 #16
0
        private int Start(string file, bool debug)
        {
            var nodePath = GetNodePath();

            if (nodePath == null)
            {
                Nodejs.ShowNodejsNotInstalled();
                return(VSConstants.S_OK);
            }

            var nodeVersion            = Nodejs.GetNodeVersion(nodePath);
            var chromeProtocolRequired = nodeVersion >= new Version(8, 0) || CheckDebugProtocolOption();
            var startBrowser           = ShouldStartBrowser();

            // The call to Version.ToString() is safe, since changes to the ToString method are very unlikely, as the current output is widely documented.
            if (debug && !chromeProtocolRequired)
            {
                StartWithDebugger(file);
                TelemetryHelper.LogDebuggingStarted("Node6", nodeVersion.ToString());
            }
            else if (debug && chromeProtocolRequired)
            {
                if (CheckUseNewChromeDebugProtocolOption())
                {
                    StartWithChromeV2Debugger(file, nodePath, startBrowser);
                    TelemetryHelper.LogDebuggingStarted("ChromeV2", nodeVersion.ToString());
                }
                else
                {
                    StartAndAttachDebugger(file, nodePath, startBrowser);
                    TelemetryHelper.LogDebuggingStarted("Chrome", nodeVersion.ToString());
                }
            }
            else
            {
                StartNodeProcess(file, nodePath, startBrowser);
                TelemetryHelper.LogDebuggingStarted("None", nodeVersion.ToString());
            }

            return(VSConstants.S_OK);
        }
예제 #17
0
        public override void Load(string filename, string location, string name, uint flags, ref Guid iidProject, out int canceled)
        {
            base.Load(filename, location, name, flags, ref iidProject, out canceled);

            // check the property
            var nodeProperty = GetProjectProperty(NodeProjectProperty.NodeExePath);

            if (!string.IsNullOrEmpty(nodeProperty))
            {
                return;
            }

            // see if we can locate the Node.js runtime from the environment
            if (!string.IsNullOrEmpty(Nodejs.GetPathToNodeExecutableFromEnvironment()))
            {
                return;
            }

            // show info bar
            MissingNodeInfoBar.Show(this);
        }
        internal void DiscoverTestFiles(string packageJsonPath, IMessageLogger logger, ITestCaseDiscoverySink discoverySink)
        {
            logger.SendMessage(TestMessageLevel.Informational, $"Parsing '{packageJsonPath}'.");

            var packageJson = PackageJsonFactory.Create(packageJsonPath);

            if (string.IsNullOrEmpty(packageJson.TestRoot))
            {
                logger.SendMessage(TestMessageLevel.Informational, "No vsTestOptions|testRoot specified.");
                return;
            }

            var workingDir     = Path.GetDirectoryName(packageJsonPath);
            var testFolderPath = Path.Combine(workingDir, packageJson.TestRoot);

            if (!Directory.Exists(testFolderPath))
            {
                logger.SendMessage(TestMessageLevel.Error, $"Testroot '{packageJson.TestRoot}' doesn't exist.");
                return;
            }

            var testFx = default(TestFramework);

            foreach (var dep in packageJson.AllDependencies)
            {
                testFx = this.frameworkDiscoverer.GetFramework(dep.Name);
                if (testFx != null)
                {
                    break;
                }
            }
            testFx = testFx ?? this.frameworkDiscoverer.GetFramework(TestFrameworkDirectories.ExportRunnerFrameworkName);

            var nodeExePath = Nodejs.GetPathToNodeExecutableFromEnvironment();
            var worker      = new TestDiscovererWorker(packageJsonPath, nodeExePath);

            worker.DiscoverTests(testFolderPath, testFx, logger, discoverySink, nameof(PackageJsonTestDiscoverer));
        }
예제 #19
0
        public async Task TestNpmReplCommandProcessExitSucceeds()
        {
            var npmPath = Nodejs.GetPathToNodeExecutableFromEnvironment("npm.cmd");

            using (var eval = ProjectlessEvaluator()) {
                var mockWindow = new MockReplWindow(eval)
                {
                    ShowAnsiCodes = true
                };
                mockWindow.ClearScreen();
                var redirector = new NpmReplCommand.NpmReplRedirector(mockWindow);

                for (int j = 0; j < 200; j++)
                {
                    await NpmReplCommand.ExecuteNpmCommandAsync(
                        redirector,
                        npmPath,
                        null,
                        new[] { "config", "get", "registry" },
                        null);
                }
            }
        }
        private int Start(string file, bool debug)
        {
            var nodePath = GetNodePath();

            if (nodePath == null)
            {
                Nodejs.ShowNodejsNotInstalled();
                return(VSConstants.S_OK);
            }

            var chromeProtocolRequired = Nodejs.GetNodeVersion(nodePath) >= new Version(8, 0);

#if !DEV15
            if (chromeProtocolRequired)
            {
                Nodejs.ShowNodeVersionNotSupported();
                return(VSConstants.S_OK);
            }
#endif

            bool startBrowser = ShouldStartBrowser();

            if (debug && !chromeProtocolRequired)
            {
                StartWithDebugger(file);
            }
            else if (debug && chromeProtocolRequired)
            {
                StartAndAttachDebugger(file, nodePath);
            }
            else
            {
                StartNodeProcess(file, nodePath, startBrowser);
            }

            return(VSConstants.S_OK);
        }
예제 #21
0
        public void SetupDebugTargetInfo(ref VsDebugTargetInfo vsDebugTargetInfo, DebugLaunchActionContext debugLaunchContext)
        {
            var nodeExe = debugLaunchContext.LaunchConfiguration.GetValue(NodeExeKey, defaultValue: Nodejs.GetPathToNodeExecutableFromEnvironment());

            if (string.IsNullOrEmpty(nodeExe))
            {
                var workspace = this.WorkspaceService.CurrentWorkspace;
                workspace.JTF.Run(async() =>
                {
                    await workspace.JTF.SwitchToMainThreadAsync();

                    VsShellUtilities.ShowMessageBox(this.ServiceProvider,
                                                    string.Format(Resources.NodejsNotInstalledAnyCode, LaunchConfigurationConstants.LaunchJsonFileName),
                                                    Resources.NodejsNotInstalledShort,
                                                    OLEMSGICON.OLEMSGICON_CRITICAL,
                                                    OLEMSGBUTTON.OLEMSGBUTTON_OK,
                                                    OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
                });

                // This isn't pretty but the only way to not get an additional
                // dialog box, after the one we show.
                throw new TaskCanceledException();
            }

            var nodeVersion = Nodejs.GetNodeVersion(nodeExe);

            if (nodeVersion >= new Version(8, 0) || NodejsProjectLauncher.CheckDebugProtocolOption())
            {
                SetupDebugTargetInfoForWebkitV2Protocol(ref vsDebugTargetInfo, debugLaunchContext, nodeExe);
                TelemetryHelper.LogDebuggingStarted("ChromeV2", nodeVersion.ToString(), isProject: false);
            }
            else
            {
                this.SetupDebugTargetInfoForNodeProtocol(ref vsDebugTargetInfo, debugLaunchContext, nodeExe);
                TelemetryHelper.LogDebuggingStarted("Node6", nodeVersion.ToString(), isProject: false);
            }
        }
예제 #22
0
        public void DiscoverTests(IEnumerable <string> sources, IDiscoveryContext discoveryContext, IMessageLogger logger, ITestCaseDiscoverySink discoverySink)
        {
            // we can ignore the sources argument since this will be a collection of assemblies.
            ValidateArg.NotNull(discoverySink, nameof(discoverySink));
            ValidateArg.NotNull(logger, nameof(logger));

            // extract the project file from the discovery context.
            var unitTestSettings = new UnitTestSettings(discoveryContext.RunSettings);

            if (string.IsNullOrEmpty(unitTestSettings.TestSource))
            {
                // no need to log since the test executor will report 'no tests'
                return;
            }

            if (string.IsNullOrEmpty(unitTestSettings.TestFrameworksLocation))
            {
                logger.SendMessage(TestMessageLevel.Error, "Failed to locate the test frameworks.");
                return;
            }

            sources = new[] { unitTestSettings.TestSource };
            var frameworkDiscoverer = new FrameworkDiscoverer(unitTestSettings.TestFrameworksLocation);

            var projects = new List <(string projectFilePath, IEnumerable <XElement> propertyGroup)>();

            // There's an issue when loading the project using the .NET Core msbuild bits,
            // so we load the xml, and extract the properties we care about.
            // Downside is we only have the raw contents of the XmlElements, i.e. we don't
            // expand any variables.
            // The issue we encountered is that the msbuild implementation was not able to
            // locate the SDK targets/props files. See: https://github.com/Microsoft/msbuild/issues/3434
            try
            {
                foreach (var source in sources)
                {
                    var cleanPath = source.Trim('\'', '"');

                    // we only support project files, e.g. csproj, vbproj, etc.
                    if (!cleanPath.EndsWith("proj", StringComparison.OrdinalIgnoreCase))
                    {
                        continue;
                    }

                    var project = XDocument.Load(cleanPath);

                    // structure looks like Project/PropertyGroup/JsTestRoot and Project/PropertyGroup/JsTestFramework
                    var propertyGroup = project.Descendants("Project").Descendants("PropertyGroup");

                    projects.Add((cleanPath, propertyGroup));
                }

                foreach (var(projectFile, propertyGroup) in projects)
                {
                    var testFramework = propertyGroup.Descendants(NodeProjectProperty.TestFramework).FirstOrDefault()?.Value;
                    var testRoot      = propertyGroup.Descendants(NodeProjectProperty.TestRoot).FirstOrDefault()?.Value;
                    var outDir        = propertyGroup.Descendants(NodeProjectProperty.TypeScriptOutDir).FirstOrDefault()?.Value ?? "";

                    if (string.IsNullOrEmpty(testRoot) || string.IsNullOrEmpty(testFramework))
                    {
                        logger.SendMessage(TestMessageLevel.Warning, $"No TestRoot or TestFramework specified for '{Path.GetFileName(projectFile)}'.");
                        continue;
                    }

                    var projectHome = Path.GetDirectoryName(projectFile);
                    var testItems   = new HashSet <string>(StringComparer.OrdinalIgnoreCase);
                    var testFolder  = Path.Combine(projectHome, testRoot);

                    if (!Directory.Exists(testFolder))
                    {
                        logger.SendMessage(TestMessageLevel.Warning, $"Test folder path '{testFolder}' doesn't exist.");
                        continue;
                    }

                    // grab all files, we try for .ts files first, and only parse the .js files if we don't find any
                    foreach (var file in Directory.EnumerateFiles(testFolder, "*.ts", SearchOption.AllDirectories))
                    {
                        ProcessFiles(file);
                    }

                    if (!testItems.Any())
                    {
                        foreach (var file in Directory.EnumerateFiles(testFolder, "*.js", SearchOption.AllDirectories))
                        {
                            ProcessFiles(file);
                        }
                    }

                    if (testItems.Any())
                    {
                        var testFx = frameworkDiscoverer.GetFramework(testFramework);
                        if (testFx == null)
                        {
                            logger.SendMessage(TestMessageLevel.Warning, $"Ignoring unsupported test framework '{testFramework}'.");
                            return;
                        }

                        var nodeExePath = Nodejs.GetAbsoluteNodeExePath(projectHome, propertyGroup.Descendants(NodeProjectProperty.NodeExePath).FirstOrDefault()?.Value);
                        if (string.IsNullOrEmpty(nodeExePath))
                        {
                            // if nothing specified in the project fallback to environment
                            nodeExePath = Nodejs.GetPathToNodeExecutableFromEnvironment();
                        }

                        this.DiscoverTests(testItems, testFx, discoverySink, logger, nodeExePath, projectFile);
                    }

                    void ProcessFiles(string fileAbsolutePath)
                    {
                        var typeScriptTest = TypeScript.TypeScriptHelpers.IsTypeScriptFile(fileAbsolutePath);

                        if (typeScriptTest)
                        {
                            fileAbsolutePath = TypeScript.TypeScriptHelpers.GetTypeScriptBackedJavaScriptFile(projectHome, outDir, fileAbsolutePath);
                        }
                        else if (!StringComparer.OrdinalIgnoreCase.Equals(Path.GetExtension(fileAbsolutePath), ".js"))
                        {
                            return;
                        }

                        testItems.Add(fileAbsolutePath);
                    }
                }
            }
            catch (Exception ex)
            {
                logger.SendMessage(TestMessageLevel.Error, ex.Message);
                throw;
            }
        }
예제 #23
0
        internal void DiscoverTestFiles(string packageJsonPath, IMessageLogger logger, ITestCaseDiscoverySink discoverySink)
        {
            logger.SendMessage(TestMessageLevel.Informational, $"Parsing '{packageJsonPath}'.");

            var packageJson = PackageJsonFactory.Create(packageJsonPath);

            if (string.IsNullOrEmpty(packageJson.TestRoot))
            {
                logger.SendMessage(TestMessageLevel.Informational, "No vsTestOptions|testRoot specified.");
                return;
            }

            var workingDir     = Path.GetDirectoryName(packageJsonPath);
            var testFolderPath = Path.Combine(workingDir, packageJson.TestRoot);

            if (!Directory.Exists(testFolderPath))
            {
                logger.SendMessage(TestMessageLevel.Error, $"Testroot '{packageJson.TestRoot}' doesn't exist.");
                return;
            }

            TestFramework testFx = null;

            foreach (var dep in packageJson.AllDependencies)
            {
                testFx = FrameworkDiscoverer.Instance.Get(dep.Name);
                if (testFx != null)
                {
                    break;
                }
            }
            testFx = testFx ?? FrameworkDiscoverer.Instance.Get("ExportRunner");

            var nodeExePath = Nodejs.GetPathToNodeExecutableFromEnvironment();

            if (!File.Exists(nodeExePath))
            {
                logger.SendMessage(TestMessageLevel.Error, "Node.exe was not found. Please install Node.js before running tests.");
                return;
            }

            var fileList = Directory.GetFiles(testFolderPath, "*.js", SearchOption.AllDirectories);
            var files    = string.Join(";", fileList);

            logger.SendMessage(TestMessageLevel.Informational, $"Processing: {files}");

            var discoveredTestCases = testFx.FindTests(fileList, nodeExePath, logger, projectRoot: workingDir);

            if (!discoveredTestCases.Any())
            {
                logger.SendMessage(TestMessageLevel.Warning, "Discovered 0 testcases.");
                return;
            }

            foreach (var discoveredTest in discoveredTestCases)
            {
                var          qualifiedName = discoveredTest.FullyQualifiedName;
                const string indent        = "  ";
                logger.SendMessage(TestMessageLevel.Informational, $"{indent}Creating TestCase:{qualifiedName}");
                //figure out the test source info such as line number
                var filePath = CommonUtils.GetAbsoluteFilePath(workingDir, discoveredTest.TestFile);

                var testcase = new TestCase(qualifiedName, NodejsConstants.PackageJsonExecutorUri, packageJsonPath)
                {
                    CodeFilePath = filePath,
                    LineNumber   = discoveredTest.SourceLine,
                    DisplayName  = discoveredTest.TestName
                };

                testcase.SetPropertyValue(JavaScriptTestCaseProperties.TestFramework, testFx.Name);
                testcase.SetPropertyValue(JavaScriptTestCaseProperties.WorkingDir, workingDir);
                testcase.SetPropertyValue(JavaScriptTestCaseProperties.ProjectRootDir, workingDir);
                testcase.SetPropertyValue(JavaScriptTestCaseProperties.NodeExePath, nodeExePath);
                testcase.SetPropertyValue(JavaScriptTestCaseProperties.TestFile, filePath);

                discoverySink.SendTestCase(testcase);
            }

            logger.SendMessage(TestMessageLevel.Informational, $"Processing finished for framework '{testFx.Name}'.");
        }
예제 #24
0
        public async Task <ExecutionResult> Execute(IReplWindow window, string arguments)
        {
            string projectPath  = string.Empty;
            string npmArguments = arguments.Trim(' ', '\t');

            // Parse project name/directory in square brackets
            if (npmArguments.StartsWith("[", StringComparison.Ordinal))
            {
                var match = Regex.Match(npmArguments, @"(?:[[]\s*\""?\s*)(.*?)(?:\s*\""?\s*[]]\s*)");
                projectPath  = match.Groups[1].Value;
                npmArguments = npmArguments.Substring(match.Length);
            }

            // Include spaces on either side of npm arguments so that we can more simply detect arguments
            // at beginning and end of string (e.g. '--global')
            npmArguments = string.Format(CultureInfo.InvariantCulture, " {0} ", npmArguments);

            // Prevent running `npm init` without the `-y` flag since it will freeze the repl window,
            // waiting for user input that will never come.
            if (npmArguments.Contains(" init ") && !(npmArguments.Contains(" -y ") || npmArguments.Contains(" --yes ")))
            {
                window.WriteError(SR.GetString(SR.ReplWindowNpmInitNoYesFlagWarning));
                return(ExecutionResult.Failure);
            }

            var solution = Package.GetGlobalService(typeof(SVsSolution)) as IVsSolution;
            IEnumerable <IVsProject> loadedProjects = solution.EnumerateLoadedProjects(onlyNodeProjects: false);

            var projectNameToDirectoryDictionary = new Dictionary <string, Tuple <string, IVsHierarchy> >(StringComparer.OrdinalIgnoreCase);

            foreach (IVsProject project in loadedProjects)
            {
                var    hierarchy = (IVsHierarchy)project;
                object extObject;

                var projectResult = hierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ExtObject, out extObject);
                if (!ErrorHandler.Succeeded(projectResult))
                {
                    continue;
                }

                EnvDTE.Project dteProject = extObject as EnvDTE.Project;
                if (dteProject == null)
                {
                    continue;
                }

                string projectName = dteProject.Name;
                if (string.IsNullOrEmpty(projectName))
                {
                    continue;
                }

                // Try checking the `ProjectHome` property first
                EnvDTE.Properties properties = dteProject.Properties;
                if (dteProject.Properties != null)
                {
                    EnvDTE.Property projectHome = null;
                    try {
                        projectHome = properties.Item("ProjectHome");
                    } catch (ArgumentException) {
                        // noop
                    }

                    if (projectHome != null)
                    {
                        var projectHomeDirectory = projectHome.Value as string;
                        if (!string.IsNullOrEmpty(projectHomeDirectory))
                        {
                            projectNameToDirectoryDictionary.Add(projectName, Tuple.Create(projectHomeDirectory, hierarchy));
                            continue;
                        }
                    }
                }

                // Otherwise, fall back to using fullname
                string projectDirectory = string.IsNullOrEmpty(dteProject.FullName) ? null : Path.GetDirectoryName(dteProject.FullName);
                if (!string.IsNullOrEmpty(projectDirectory))
                {
                    projectNameToDirectoryDictionary.Add(projectName, Tuple.Create(projectDirectory, hierarchy));
                }
            }

            Tuple <string, IVsHierarchy> projectInfo;

            if (string.IsNullOrEmpty(projectPath) && projectNameToDirectoryDictionary.Count == 1)
            {
                projectInfo = projectNameToDirectoryDictionary.Values.First();
            }
            else
            {
                projectNameToDirectoryDictionary.TryGetValue(projectPath, out projectInfo);
            }

            NodejsProjectNode nodejsProject = null;

            if (projectInfo != null)
            {
                projectPath = projectInfo.Item1;
                if (projectInfo.Item2 != null)
                {
                    nodejsProject = projectInfo.Item2.GetProject().GetNodejsProject();
                }
            }

            bool isGlobalCommand = false;

            if (string.IsNullOrWhiteSpace(npmArguments) ||
                npmArguments.Contains(" -g ") || npmArguments.Contains(" --global "))
            {
                projectPath     = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
                isGlobalCommand = true;
            }

            // In case someone copies filename
            string projectDirectoryPath = File.Exists(projectPath) ? Path.GetDirectoryName(projectPath) : projectPath;

            if (!isGlobalCommand && !Directory.Exists(projectDirectoryPath))
            {
                window.WriteError("Please specify a valid Node.js project or project directory. If your solution contains multiple projects, specify a target project using .npm [ProjectName or ProjectDir] <npm arguments> For example: .npm [MyApp] list");
                return(ExecutionResult.Failure);
            }

            string npmPath;

            try {
                npmPath = NpmHelpers.GetPathToNpm(
                    nodejsProject != null ?
                    Nodejs.GetAbsoluteNodeExePath(
                        nodejsProject.ProjectHome,
                        nodejsProject.GetProjectProperty(NodeProjectProperty.NodeExePath))
                        : null);
            } catch (NpmNotFoundException) {
                Nodejs.ShowNodejsNotInstalled();
                return(ExecutionResult.Failure);
            }

            var npmReplRedirector = new NpmReplRedirector(window);

            await ExecuteNpmCommandAsync(
                npmReplRedirector,
                npmPath,
                projectDirectoryPath,
                new[] { npmArguments },
                null);

            if (npmReplRedirector.HasErrors)
            {
                window.WriteError(SR.GetString(SR.NpmReplCommandCompletedWithErrors, arguments));
            }
            else
            {
                window.WriteLine(SR.GetString(SR.NpmSuccessfullyCompleted, arguments));
            }

            if (nodejsProject != null)
            {
                await nodejsProject.CheckForLongPaths(npmArguments);
            }

            return(ExecutionResult.Success);
        }
예제 #25
0
        private void DiscoverTests(Dictionary <string, List <TestFileEntry> > testItems, MSBuild.Project proj, ITestCaseDiscoverySink discoverySink, IMessageLogger logger)
        {
            var result      = new List <TestFrameworks.NodejsTestInfo>();
            var projectHome = Path.GetFullPath(Path.Combine(proj.DirectoryPath, "."));
            var projSource  = proj.FullPath;

            var nodeExePath =
                Nodejs.GetAbsoluteNodeExePath(
                    projectHome,
                    proj.GetPropertyValue(NodeProjectProperty.NodeExePath));

            if (!File.Exists(nodeExePath))
            {
                logger.SendMessage(TestMessageLevel.Error, string.Format(CultureInfo.CurrentCulture, "Node.exe was not found.  Please install Node.js before running tests."));
                return;
            }

            var testCount = 0;

            foreach (var testFx in testItems.Keys)
            {
                var testFramework = GetTestFrameworkObject(testFx);
                if (testFramework == null)
                {
                    logger.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, "Ignoring unsupported test framework {0}", testFx));
                    continue;
                }

                var fileList = testItems[testFx];
                var files    = string.Join(";", fileList.Select(p => p.File));
                logger.SendMessage(TestMessageLevel.Informational, string.Format(CultureInfo.CurrentCulture, "Processing: {0}", files));

                var discoveredTestCases = testFramework.FindTests(fileList.Select(p => p.File), nodeExePath, logger, projectHome);
                testCount += discoveredTestCases.Count;
                foreach (var discoveredTest in discoveredTestCases)
                {
                    var qualifiedName = discoveredTest.FullyQualifiedName;
                    logger.SendMessage(TestMessageLevel.Informational, string.Format(CultureInfo.CurrentCulture, "  " /*indent*/ + "Creating TestCase:{0}", qualifiedName));
                    //figure out the test source info such as line number
                    var filePath           = discoveredTest.ModulePath;
                    var entry              = fileList.Find(p => p.File.Equals(filePath, StringComparison.OrdinalIgnoreCase));
                    FunctionInformation fi = null;
                    if (entry.IsTypeScriptTest)
                    {
                        fi = SourceMapper.MaybeMap(new FunctionInformation(string.Empty,
                                                                           discoveredTest.TestName,
                                                                           discoveredTest.SourceLine,
                                                                           entry.File));
                    }
                    discoverySink.SendTestCase(
                        new TestCase(qualifiedName, TestExecutor.ExecutorUri, projSource)
                    {
                        CodeFilePath = (fi != null) ? fi.Filename : filePath,
                        LineNumber   = (fi != null && fi.LineNumber.HasValue) ? fi.LineNumber.Value : discoveredTest.SourceLine,
                        DisplayName  = discoveredTest.TestName
                    });
                }
                logger.SendMessage(TestMessageLevel.Informational, string.Format(CultureInfo.CurrentCulture, "Processing finished for framework of {0}", testFx));
            }
            if (testCount == 0)
            {
                logger.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, "Discovered 0 testcases."));
            }
        }
예제 #26
0
        private string GetNodePath()
        {
            var overridePath = this._project.GetProjectProperty(NodeProjectProperty.NodeExePath);

            return(Nodejs.GetAbsoluteNodeExePath(this._project.ProjectHome, overridePath));
        }
        /// <summary>
        /// ITestDiscover, Given a list of test sources this method pulls out the test cases
        /// </summary>
        /// <param name="sources">List of test sources passed from client (Client can be VS or command line)</param>
        /// <param name="logger">Used to relay messages to registered loggers</param>
        /// <param name="discoverySink">Callback used to notify client upon discovery of test cases</param>
        private void DiscoverTestsCore(IEnumerable <string> sources, IMessageLogger logger, ITestCaseDiscoverySink discoverySink)
        {
            ValidateArg.NotNull(sources, nameof(sources));
            ValidateArg.NotNull(discoverySink, nameof(discoverySink));
            ValidateArg.NotNull(logger, nameof(logger));

            var env  = new Dictionary <string, string>();
            var root = Environment.GetEnvironmentVariable("VSINSTALLDIR");

#if DEBUG
            logger.SendMessage(TestMessageLevel.Informational, $"VSINSTALLDIR: {root}");
#endif

            if (!string.IsNullOrEmpty(root))
            {
                env["VsInstallRoot"]           = root;
                env["MSBuildExtensionsPath32"] = Path.Combine(root, "MSBuild");
            }

            using (var buildEngine = new MSBuild.ProjectCollection(env))
            {
                try
                {
                    // Load all the test containers passed in (.njsproj msbuild files)
                    foreach (var source in sources)
                    {
                        var cleanPath = source.Trim('\'', '"');
                        buildEngine.LoadProject(cleanPath);
                    }

                    FrameworkDiscoverer frameworkDiscoverer = null;
                    foreach (var proj in buildEngine.LoadedProjects)
                    {
                        var projectHome = Path.GetFullPath(Path.Combine(proj.DirectoryPath, "."));

                        var testItems = TestFrameworkFactory.GetTestItems(projectHome, proj);

                        if (testItems.Any())
                        {
                            frameworkDiscoverer = frameworkDiscoverer ?? new FrameworkDiscoverer();

                            var nodeExePath = Nodejs.GetAbsoluteNodeExePath(projectHome, proj.GetPropertyValue(NodeProjectProperty.NodeExePath));
                            if (string.IsNullOrEmpty(nodeExePath))
                            {
                                // if nothing specified in the project fallback to environment
                                nodeExePath = Nodejs.GetPathToNodeExecutableFromEnvironment();
                            }

                            this.DiscoverTests(testItems, frameworkDiscoverer, discoverySink, logger, nodeExePath, proj.FullPath);
                        }
                    }
                }
                catch (Exception ex)
                {
                    logger.SendMessage(TestMessageLevel.Error, ex.Message);
                    throw;
                }
                finally
                {
                    // Disposing buildEngine does not clear the document cache in
                    // VS 2013, so manually unload all projects before disposing.
                    buildEngine.UnloadAllProjects();
                }
            }
        }
예제 #28
0
        private string GetNodePath()
        {
            var overridePath = _project.GetProjectProperty(NodejsConstants.NodeExePath);

            return(Nodejs.GetAbsoluteNodeExePath(_project.ProjectHome, overridePath));
        }
예제 #29
0
        private void RunTestCases(IEnumerable <TestCase> tests, IRunContext runContext, IFrameworkHandle frameworkHandle, NodejsProjectSettings settings)
        {
            // May be null, but this is handled by RunTestCase if it matters.
            // No VS instance just means no debugging, but everything else is
            // okay.
            if (tests.Count() == 0)
            {
                return;
            }

            using (var app = VisualStudioApp.FromEnvironmentVariable(NodejsConstants.NodeToolsProcessIdEnvironmentVariable))
            {
                var port     = 0;
                var nodeArgs = new List <string>();
                // .njsproj file path -> project settings
                var sourceToSettings = new Dictionary <string, NodejsProjectSettings>();
                var testObjects      = new List <TestCaseObject>();

                if (!File.Exists(settings.NodeExePath))
                {
                    frameworkHandle.SendMessage(TestMessageLevel.Error, "Interpreter path does not exist: " + settings.NodeExePath);
                    return;
                }

                // All tests being run are for the same test file, so just use the first test listed to get the working dir
                var testInfo   = new NodejsTestInfo(tests.First().FullyQualifiedName);
                var workingDir = Path.GetDirectoryName(CommonUtils.GetAbsoluteFilePath(settings.WorkingDir, testInfo.ModulePath));

                var nodeVersion = Nodejs.GetNodeVersion(settings.NodeExePath);

                // We can only log telemetry when we're running in VS.
                // Since the required assemblies are not on disk if we're not running in VS, we have to reference them in a separate method
                // this way the .NET framework only tries to load the assemblies when we actually need them.
                if (app != null)
                {
                    LogTelemetry(tests.Count(), nodeVersion, runContext.IsBeingDebugged);
                }

                foreach (var test in tests)
                {
                    if (_cancelRequested.WaitOne(0))
                    {
                        break;
                    }

                    if (settings == null)
                    {
                        frameworkHandle.SendMessage(
                            TestMessageLevel.Error,
                            $"Unable to determine interpreter to use for {test.Source}.");
                        frameworkHandle.RecordEnd(test, TestOutcome.Failed);
                    }

                    var args = new List <string>();
                    args.AddRange(GetInterpreterArgs(test, workingDir, settings.ProjectRootDir));

                    // Fetch the run_tests argument for starting node.exe if not specified yet
                    if (nodeArgs.Count == 0 && args.Count > 0)
                    {
                        nodeArgs.Add(args[0]);
                    }

                    testObjects.Add(new TestCaseObject(args[1], args[2], args[3], args[4], args[5]));
                }

                if (runContext.IsBeingDebugged && app != null)
                {
                    app.GetDTE().Debugger.DetachAll();
                    // Ensure that --debug-brk is the first argument
                    nodeArgs.InsertRange(0, GetDebugArgs(out port));
                }

                _nodeProcess = ProcessOutput.Run(
                    settings.NodeExePath,
                    nodeArgs,
                    settings.WorkingDir,
                    /* env */ null,
                    /* visible */ false,
                    /* redirector */ new TestExecutionRedirector(this.ProcessTestRunnerEmit),
                    /* quote args */ false);

                if (runContext.IsBeingDebugged && app != null)
                {
                    try
                    {
                        //the '#ping=0' is a special flag to tell VS node debugger not to connect to the port,
                        //because a connection carries the consequence of setting off --debug-brk, and breakpoints will be missed.
                        var qualifierUri = string.Format("tcp://localhost:{0}#ping=0", port);
                        while (!app.AttachToProcess(_nodeProcess, NodejsRemoteDebugPortSupplierUnsecuredId, qualifierUri))
                        {
                            if (_nodeProcess.Wait(TimeSpan.FromMilliseconds(500)))
                            {
                                break;
                            }
                        }
#if DEBUG
                    }
                    catch (COMException ex)
                    {
                        frameworkHandle.SendMessage(TestMessageLevel.Error, "Error occurred connecting to debuggee.");
                        frameworkHandle.SendMessage(TestMessageLevel.Error, ex.ToString());
                        KillNodeProcess();
                    }
#else
                    } catch (COMException) {
예제 #30
0
        private void RunTestCases(IEnumerable <TestCaseResult> tests)
        {
            // May be null, but this is handled by RunTestCase if it matters.
            // No VS instance just means no debugging, but everything else is okay.
            if (!tests.Any())
            {
                return;
            }

            var startedFromVs = this.HasVisualStudioProcessId(out var vsProcessId);

            var nodeArgs = new List <string>();

            var testObjects = new List <TestCaseObject>();

            // All tests being run are for the same test file, so just use the first test listed to get the working dir
            var firstTest      = tests.First().TestCase;
            var testFramework  = firstTest.GetPropertyValue(JavaScriptTestCaseProperties.TestFramework, defaultValue: TestFrameworkDirectories.ExportRunnerFrameworkName);
            var workingDir     = firstTest.GetPropertyValue(JavaScriptTestCaseProperties.WorkingDir, defaultValue: Path.GetDirectoryName(firstTest.CodeFilePath));
            var nodeExePath    = firstTest.GetPropertyValue <string>(JavaScriptTestCaseProperties.NodeExePath, defaultValue: null);
            var projectRootDir = firstTest.GetPropertyValue(JavaScriptTestCaseProperties.ProjectRootDir, defaultValue: Path.GetDirectoryName(firstTest.CodeFilePath));

            if (string.IsNullOrEmpty(nodeExePath) || !File.Exists(nodeExePath))
            {
                this.frameworkHandle.SendMessage(TestMessageLevel.Error, "Interpreter path does not exist: " + nodeExePath);
                return;
            }

            var nodeVersion = Nodejs.GetNodeVersion(nodeExePath);

            foreach (var test in tests)
            {
                if (this.cancelRequested.WaitOne(0))
                {
                    break;
                }

                var args = this.GetInterpreterArgs(test.TestCase, workingDir, projectRootDir);

                // Fetch the run_tests argument for starting node.exe if not specified yet
                if (!nodeArgs.Any())
                {
                    nodeArgs.Add(args.RunTestsScriptFile);
                }

                testObjects.Add(new TestCaseObject(
                                    framework: args.TestFramework,
                                    fullyQualifiedName: args.fullyQualifiedName,
                                    testFile: args.TestFile,
                                    workingFolder: args.WorkingDirectory,
                                    projectFolder: args.ProjectRootDir));
            }

            var port = 0;

            if (this.runContext.IsBeingDebugged && startedFromVs)
            {
                this.DetachDebugger(vsProcessId);
                // Ensure that --debug-brk or --inspect-brk is the first argument
                nodeArgs.Insert(0, GetDebugArgs(nodeVersion, out port));
            }

            // make sure the tests completed is not signalled.
            this.testsCompleted.Reset();

            this.nodeProcess = ProcessOutput.Run(
                nodeExePath,
                nodeArgs,
                workingDir,
                env: null,
                visible: false,
                redirector: new TestExecutionRedirector(this.ProcessTestRunnerEmit),
                quoteArgs: false);

            if (this.runContext.IsBeingDebugged && startedFromVs)
            {
                this.AttachDebugger(vsProcessId, port, nodeVersion);
            }

            var serializedObjects = JsonConvert.SerializeObject(testObjects);

            // Send the process the list of tests to run and wait for it to complete
            this.nodeProcess.WriteInputLine(serializedObjects);

            // for node 8 the process doesn't automatically exit when debugging, so always detach
            WaitHandle.WaitAny(new[] { this.nodeProcess.WaitHandle, this.testsCompleted });
            if (this.runContext.IsBeingDebugged && startedFromVs)
            {
                this.DetachDebugger(vsProcessId);
            }

            // Automatically fail tests that haven't been run by this point (failures in before() hooks)
            foreach (var notRunTest in this.currentTests)
            {
                var result = new TestResult(notRunTest.TestCase)
                {
                    Outcome = TestOutcome.Failed
                };

                if (this.currentResultObject != null)
                {
                    result.Messages.Add(new TestResultMessage(TestResultMessage.StandardOutCategory, this.currentResultObject.stdout));
                    result.Messages.Add(new TestResultMessage(TestResultMessage.StandardErrorCategory, this.currentResultObject.stderr));
                }
                this.frameworkHandle.RecordResult(result);
                this.frameworkHandle.RecordEnd(notRunTest.TestCase, TestOutcome.Failed);
            }
        }