@NotNull
  private TestNode getOrCreateTestNode(@NotNull TestPath testPath) {
    ConfigNode configNode = getCurrentConfigNode();
    BrowserNode browserNode = configNode.findChildByName(testPath.getBrowserDisplayName());
    if (browserNode == null) {
      browserNode = new BrowserNode(testPath.getBrowserDisplayName(), configNode);
      configNode.addChild(browserNode);
    }

    TestCaseNode testCaseNode = browserNode.findChildByName(testPath.getTestCaseName());
    if (testCaseNode == null) {
      testCaseNode =
          new TestCaseNode(
              testPath.getTestCaseName(), testPath.getJsTestFileAbsolutePath(), browserNode);
      browserNode.addChild(testCaseNode);
    }

    TestNode testNode = testCaseNode.findChildByName(testPath.getTestName());
    if (testNode == null) {
      testNode = new TestNode(testPath.getTestName(), testCaseNode);
      testCaseNode.addChild(testNode);
    }

    return testNode;
  }
 public void onJstdConfigRunningFinished(
     @Nullable Exception testsRunException, @NotNull TestFileScope testFileScope) {
   ConfigNode configNode = getCurrentConfigNode();
   for (BrowserNode browserNode : configNode.getChildren()) {
     for (TestCaseNode testCaseNode : browserNode.getChildren()) {
       for (TestNode testNode : testCaseNode.getChildren()) {
         TCMessage testFailedMessage = TC.newTestFailedMessage(testNode);
         String reason = testsRunException != null ? "JsTestDriver crash" : "unknown reason";
         testFailedMessage.addAttribute(
             TCAttribute.EXCEPTION_MESSAGE, "Can't execute test due to " + reason + ".");
         testFailedMessage.addAttribute(TCAttribute.IS_TEST_ERROR, "yes");
         printTCMessage(testFailedMessage);
       }
       TCMessage testCaseFinishedMessage = TC.newTestSuiteFinishedMessage(testCaseNode);
       printTCMessage(testCaseFinishedMessage);
     }
     TCMessage browserFinishedMessage = TC.newTestSuiteFinishedMessage(browserNode);
     printTCMessage(browserFinishedMessage);
   }
   if (testsRunException != null) {
     ConfigErrorNode configErrorNode = new ConfigErrorNode(configNode);
     TCMessage startedMessage = configErrorNode.createStartedMessage();
     printTCMessage(startedMessage);
     TCMessage finishedMessage = TC.newConfigErrorFinishedMessage(configErrorNode);
     String fullMessage =
         formatMessage(testsRunException.getMessage(), testsRunException.getCause());
     finishedMessage.addAttribute(TCAttribute.EXCEPTION_MESSAGE, fullMessage);
     printTCMessage(finishedMessage);
   } else if (configNode.getChildren().isEmpty()) {
     final String message;
     Map.Entry<String, Set<String>> testCaseEntry = testFileScope.getSingleTestCaseEntry();
     if (testCaseEntry != null) {
       Set<String> testMethodNames = testCaseEntry.getValue();
       if (testMethodNames.isEmpty()) {
         message = "No '" + testCaseEntry.getKey() + "' test case found or it has no tests.";
       } else {
         message = "No '" + testFileScope.humanize() + "' test method found.";
       }
     } else {
       message = "No tests found. Please check 'test:' section of the configuration file.";
     }
     myErrStream.println(message);
   }
   TCMessage configFinishedMessage = TC.newTestSuiteFinishedMessage(configNode);
   printTCMessage(configFinishedMessage);
 }