package de.is24.util.karmatestrunner;


import org.json.simple.parser.ContainerFactory;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.junit.runner.Description;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
import java.util.*;

/**
 * The TestReporter is receives the test results and other infos from the
 * {@link de.is24.util.karmatestrunner.jetty.ResultReceiverServer} and transforms
 * them into JUnit results (via {@link RunNotifier}).
 */
public class TestReporter {

    private HashMap<String, String> browsers = new HashMap<>();
    private JSONParser parser = new JSONParser();
    private RunNotifier notifier;
    private Class<?> testClass;
    private Description suiteDescription;

    /**
     * @param notifier JUnit notifier for transformed test results
     */
    TestReporter(RunNotifier notifier) {
        this.notifier = notifier;
    }


    /**
     * Receives the test results and transforms them into JUnit events.
     *
     * @param data test results as JSON string
     */
    public void handleMessage(String data) {
        try {
            Map message = (Map) parser.parse(data, containerFactory);
            String message_type = (String) message.get("type");
            if (message_type.equalsIgnoreCase("test")) {
                reportTestResult(message);
            } else if (message_type.equalsIgnoreCase("browsers")) {
                setBrowsers(message);
            } else if (message_type.equalsIgnoreCase("runComplete")) {
                finishReportingForTestSuite();
            } // other messages will be ignored
        } catch (ParseException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
    }

    public void setTestClass(Class<?> testClass) {
        this.testClass = testClass;
    }

    private void finishReportingForTestSuite() {
        notifier.fireTestFinished(suiteDescription);
    }

    private ContainerFactory containerFactory = new ContainerFactory() {
        public List creatArrayContainer() {
            return new LinkedList();
        }

        public Map createObjectContainer() {
            return new LinkedHashMap();
        }

    };

    private void setBrowsers(Map browsers) {
        for (int i = 0; i < ((List) browsers.get("list")).size(); i++) {
            Map browser = (Map) ((List) browsers.get("list")).get(i);

            this.browsers.put(
                    (String) browser.get("browserId"),
                    (String) browser.get("name")
            );

            suiteDescription = Description.createSuiteDescription(testClass);
           notifier.fireTestStarted(suiteDescription);
        }
    }

    private void reportTestResult(Map message) {
        String browserId = (String) message.get("browserId");
        Map result = (Map) message.get("result");
        Long time = (Long) result.get("time");
        Boolean skipped = (Boolean) result.get("skipped");
        String label = (String) result.get("description");
        Boolean success = (Boolean) result.get("success");
        String suite = result.get("suite").toString();


        Description description = describeChild(testClass,browsers.get(browserId) + ": " + suite + " " + label);

        try {
            if (skipped) {
                notifier.fireTestIgnored(description);
            } else {
                notifier.fireTestStarted(description);
            }

            if (!success) {

                String log = result.get("log").toString();
                JSTestFailure failure = new JSTestFailure(description, label,
                        "Failures: " + log);
                notifier.fireTestFailure(failure);
            }

        } finally {
            notifier.fireTestFinished(description);

        }

    }

    private Description describeChild(Class browser, String description) {

        return Description
                .createTestDescription(browser, description);
    }

    /**
     * A JavaScript execution failure object.
     */
    private static class JSTestFailure extends Failure {
        private final String name;

        public JSTestFailure(Description description, String name, String message) {
            super(description, new RuntimeException(message));
            this.name = name;
        }

        @Override
        public String getTestHeader() {
            return name;
        }

        @Override
        public String getTrace() {
            // The stack means nothing here.
            return getMessage();
        }

        @Override
        public String toString() {
            return getTestHeader();
        }

    }


}

@@ -1614,24 +1614,141 @@
 estSuite();%0A
+            %7D else if (message_type.equalsIgnoreCase(%22browserError%22)) %7B%0A                reportBrowserError(message);%0A
             
@@ -2899,32 +2899,568 @@
        %7D%0A    %7D%0A%0A
+    private void reportBrowserError(Map message)%7B%0A        String browserId = (String) message.get(%22browserId%22);%0A%0A        Description description = describeChild(testClass,browsers.get(browserId)+ %22: Browser Error%22 );%0A        String errorMsg = message.get(%22error%22).toString();%0A        JSTestFailure failure = new JSTestFailure(description, %22Error%22,%0A                %22Failures: %22 + errorMsg);%0A%0A        notifier.fireTestStarted(description);%0A        notifier.fireTestFailure(failure);%0A        notifier.fireTestFinished(description);%0A    %7D%0A%0A
     private void

test reporter notifies onBrowser errors (syntax errors) and let JUnit failing the test suite