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; } }