All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.seleniumtests.browserfactory.chrome.ChromeUtils Maven / Gradle / Ivy

The newest version!
package com.seleniumtests.browserfactory.chrome;

import com.seleniumtests.driver.WebUIDriver;
import com.seleniumtests.reporter.logger.TestStep;
import com.seleniumtests.util.har.*;
import com.seleniumtests.util.logging.SeleniumRobotLogger;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.json.JSONException;
import org.json.JSONObject;
import org.openqa.selenium.logging.LogEntry;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

public class ChromeUtils {

    private static final Logger logger = SeleniumRobotLogger.getLogger(WebUIDriver.class);

    public static Har parsePerformanceLogs(List logEntries, List testSteps) {
        Map> requests = new LinkedHashMap<>();

        Har har = new Har();
        Log log = har.getLog();

        Map pageStart = new LinkedHashMap<>();
        Map usedPages = new LinkedHashMap<>();
        int id = 0;
        for (TestStep testStep: testSteps) {
            Instant instant = Instant.ofEpochMilli(testStep.getStartDate().getTime());
            String pageId = String.format("page_" + id++);
            Page page = new Page(instant.atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME), pageId, testStep.getName());
            pageStart.put(testStep.getStartDate().getTime(), page);
            usedPages.put(page, false);
        }

        for (LogEntry line: logEntries) {
            JSONObject jsonObject = null;

            // only read "Network.responseReceived" messages as they contain everything
            try {
                jsonObject = new JSONObject(line.getMessage());
                JSONObject messageObject = jsonObject.getJSONObject("message");
                String method = messageObject.getString("method");
                switch (method) {
                    // message format: {"message":{"method":"Network.requestWillBeSent","params":{"documentURL":"http://10.25.4.70:53669/test.html","frameId":"145E40AEF6F7A76C61973C3946CA0992","hasUserGesture":false,"initiator":{"columnNumber":180,"lineNumber":59,"type":"parser","url":"http://10.25.4.70:53669/test.html"},"loaderId":"6AAED31A84393CDE33A22E12ACA3924B","redirectHasExtraInfo":false,"request":{"headers":{"Referer":"http://10.25.4.70:53669/test.html","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36"},"initialPriority":"Medium","isSameSite":true,"method":"GET","mixedContentType":"none","referrerPolicy":"strict-origin-when-cross-origin","url":"http://10.25.4.70:53669/googleSearch.png"},"requestId":"34060.2","timestamp":171300.445026,"type":"Image","wallTime":1739344558.303674}},"webview":"145E40AEF6F7A76C61973C3946CA0992"}
                    case "Network.requestWillBeSent" -> {
                        String requestId = messageObject.getJSONObject("params").getString("requestId");
                        requests.putIfAbsent(requestId, new HashMap<>());
                        requests.get(requestId).put("requestWillBeSent", messageObject);
                    }
                    case "Network.requestWillBeSentExtraInfo" -> {
                        String requestId = messageObject.getJSONObject("params").getString("requestId");
                        requests.putIfAbsent(requestId, new HashMap<>());
                        requests.get(requestId).put("requestWillBeSentExtraInfo", messageObject);
                    }
                    // message format: {"message":{"method":"Network.responseReceived","params":{"frameId":"8C5E01A9EE0BD7556C532FCFBE04EE5D","hasExtraInfo":true,"loaderId":"3149B492109FC8E15361AE661A188A05","requestId":"3149B492109FC8E15361AE661A188A05","response":{"alternateProtocolUsage":"unspecifiedReason","charset":"","connectionId":109,"connectionReused":true,"encodedDataLength":101,"fromDiskCache":false,"fromPrefetchCache":false,"fromServiceWorker":false,"headers":{"Content-Length":"124","Date":"Tue, 11 Feb 2025 09:02:20 GMT","Server":"Jetty(11.0.24)"},"mimeType":"text/html","protocol":"http/1.1","remoteIPAddress":"10.200.38.44","remotePort":51230,"responseTime":1.739264540255014e+12,"securityState":"insecure","status":200,"statusText":"OK","timing":{"connectEnd":-1,"connectStart":-1,"dnsEnd":-1,"dnsStart":-1,"proxyEnd":4.019,"proxyStart":2.804,"pushEnd":0,"pushStart":0,"receiveHeadersEnd":11.977,"receiveHeadersStart":5.365,"requestTime":91274.541644,"sendEnd":4.238,"sendStart":4.129,"sslEnd":-1,"sslStart":-1,"workerFetchStart":-1,"workerReady":-1,"workerRespondWithSettled":-1,"workerStart":-1},"url":"http://10.200.38.44:51230/testIFrame3.html"},"timestamp":91274.558756,"type":"Document"}},"webview":"0327E68CEF262C8D77818DC5C8B14339"}
                    case "Network.responseReceived" -> {
                        String requestId = messageObject.getJSONObject("params").getString("requestId");
                        requests.putIfAbsent(requestId, new HashMap<>());
                        requests.get(requestId).put("responseReceived", messageObject);
                    }
                    // {"message":{"method":"Network.loadingFinished","params":{"encodedDataLength":924,"requestId":"28364.2","timestamp":192318.813115}},"webview":"D6FD686EEEFF1CF429E60E5D8F6A8D71"}
                    case "Network.loadingFinished" -> {
                        String requestId = messageObject.getJSONObject("params").getString("requestId");
                        requests.putIfAbsent(requestId, new HashMap<>());
                        requests.get(requestId).put("loadingFinished", messageObject);
                    }
                    case "Network.loadingFailed" -> {
                        String requestId = messageObject.getJSONObject("params").getString("requestId");
                        requests.putIfAbsent(requestId, new HashMap<>());
                        requests.get(requestId).put("loadingFailed", messageObject);
                    }
                    // {"method":"Network.responseReceivedExtraInfo","params":{"blockedCookies":[],"cookiePartitionKey":{"hasCrossSiteAncestor":false,"topLevelSite":"http://127.0.0.1"},"cookiePartitionKeyOpaque":false,"exemptedCookies":[],"headers":{"Content-Length":"2538","Date":"Fri, 14 Feb 2025 15:42:13 GMT","Server":"Jetty(11.0.24)"},"headersText":"HTTP/1.1 200 OK\r\nDate: Fri, 14 Feb 2025 15:42:13 GMT\r\nContent-Length: 2538\r\nServer: Jetty(11.0.24)\r\n\r\n","requestId":"28364.5","resourceIPAddressSpace":"Local","statusCode":200}},"webview":"D6FD686EEEFF1CF429E60E5D8F6A8D71"}
                    // use to get the real status code (in case of cache loading)
                    case "Network.responseReceivedExtraInfo" -> {
                        String requestId = messageObject.getJSONObject("params").getString("requestId");
                        requests.putIfAbsent(requestId, new HashMap<>());
                        requests.get(requestId).put("responseReceivedExtraInfo", messageObject);
                    }
                }

            } catch (Exception e) {
                logger.error("Error reading event " + line.getMessage());
            }
        }

        for (Map.Entry> requestsEntry: requests.entrySet()) {
            String requestId = requestsEntry.getKey();
            try {

                JSONObject jsonRequest = (JSONObject) requestsEntry.getValue().get("requestWillBeSent");
                // without request information, do nothing
                if (jsonRequest == null) {
                    continue;
                }
                Request request = buildRequest(requestsEntry, jsonRequest);

                JSONObject jsonResponse = (JSONObject) requestsEntry.getValue().get("responseReceived");
                Response response = buildResponse(requestsEntry, jsonResponse);

                Timing timings;
                double duration = -1;

                if (jsonResponse != null) {
                    double endLoadingTimestamp = jsonResponse.getJSONObject("params").getDouble("timestamp");
                    try {
                        JSONObject jsonTimings = jsonResponse.getJSONObject("params").getJSONObject("response").getJSONObject("timing");
                        double startLoadingTimestamp = jsonTimings.getDouble("requestTime");
                        if (requestsEntry.getValue().get("loadingFinished") != null) {
                            JSONObject jsonLoadingFinished = (JSONObject) requestsEntry.getValue().get("loadingFinished");
                            endLoadingTimestamp = jsonLoadingFinished.getJSONObject("params").getDouble("timestamp");
                        }

                        // for details about timings: https://chromedevtools.github.io/devtools-protocol/tot/Network/
                        timings = new Timing(
                                // assume that 'blocked' is the time between 'requestTime' and start of proxy negociation
                                Math.min(jsonTimings.getDouble("proxyStart"), jsonTimings.getDouble("sendStart")),
                                jsonTimings.getDouble("dnsEnd") == -1 ? -1 : jsonTimings.getDouble("dnsEnd") - jsonTimings.getDouble("dnsStart"),
                                jsonTimings.getDouble("connectEnd") == -1 ? -1 : jsonTimings.getDouble("connectEnd") - jsonTimings.getDouble("connectStart"),
                                jsonTimings.getDouble("sslEnd") == -1 ? -1 : jsonTimings.getDouble("sslEnd") - jsonTimings.getDouble("sslStart"),
                                jsonTimings.getDouble("sendEnd") - jsonTimings.getDouble("sendStart"),
                                jsonTimings.getDouble("receiveHeadersStart") - jsonTimings.getDouble("sendEnd"),
                                (endLoadingTimestamp - startLoadingTimestamp) * 1000 - jsonTimings.getDouble("receiveHeadersEnd")
                        );
                    } catch (JSONException e) {
                        // when timings are not found (case for files), create a stub object
                        timings = new Timing(
                                0, 0, 0, 0, 0, 0, 0
                        );
                    }

                    duration = (endLoadingTimestamp - jsonRequest.getJSONObject("params").getDouble("timestamp")) * 1000;
                } else {
                    timings = new Timing(
                            0, 0, 0, 0, 0, 0, 0
                    );
                }

                long entryDate = (long) (jsonRequest.getJSONObject("params").getDouble("wallTime") * 1000);

                Page pageRef = null;
                for (Map.Entry pageEntry : pageStart.entrySet()) {
                    if (entryDate > pageEntry.getKey() || pageRef == null) {
                        pageRef = pageEntry.getValue();
                    }
                }
                usedPages.replace(pageRef, true);

                Entry entry = new Entry(
                        pageRef == null ? "" : pageRef.getId(),
                        Instant.ofEpochMilli((long) (jsonRequest.getJSONObject("params").getDouble("wallTime") * 1000)).atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME),
                        request,
                        response,
                        timings,
                        (int) duration);
                log.addEntry(entry);
            } catch (JSONException e) {
                logger.error("Error parsing request " + requestId, e);
            }
        }

        // add only used pages
        for (Map.Entry pageEntry: usedPages.entrySet()) {
            //if (Boolean.TRUE.equals(pageEntry.getValue())) {
                log.addPage(pageEntry.getKey());
            //}
        }

        return har;
    }

    private static Response buildResponse(Map.Entry> requestsEntry, JSONObject jsonResponse) {
        Response response;
        // according to spec, responseReceived event may not be fired in case of CORS
        if (jsonResponse != null) {
            int statusCode = jsonResponse.getJSONObject("params").getJSONObject("response").getInt("status");

            if (requestsEntry.getValue().get("responseReceivedExtraInfo") != null) {
                JSONObject jsonResponseExtraInfo = (JSONObject) requestsEntry.getValue().get("responseReceivedExtraInfo");
                statusCode = jsonResponseExtraInfo.getJSONObject("params").getInt("statusCode");
            }

            JSONObject jsonResponseHeaders = jsonResponse.getJSONObject("params").getJSONObject("response").getJSONObject("headers");
            response = new Response(
                    statusCode,
                    jsonResponse.getJSONObject("params").getJSONObject("response").getString("statusText"),
                    jsonResponse.getJSONObject("params").getJSONObject("response").getString("protocol"),
                    jsonResponseHeaders.keySet().stream()
                            .filter(key -> !key.toLowerCase().contains("token"))
                            .map(key -> new Header(key, jsonResponseHeaders.getString(key)))
                            .collect(Collectors.toList()),
                    new ArrayList<>(), // cookies
                    new Content(
                            jsonResponse.getJSONObject("params").getJSONObject("response").getString("mimeType"),
                            jsonResponseHeaders.optInt("Content-Length", 0),
                            "_masked_"
                    ),
                    "",
                    jsonResponse.getJSONObject("params").getJSONObject("response").getInt("encodedDataLength"),
                    jsonResponseHeaders.optInt("Content-Length", 0)
            );
        } else {
            String error = "No response received";
            if (requestsEntry.getValue().get("loadingFailed") != null) {
                error = ((JSONObject)requestsEntry.getValue().get("loadingFailed")).getJSONObject("params").getString("errorText");
            }
            response = new Response(
                    0,
                    error,
                    "",
                    new ArrayList<>(),
                    new ArrayList<>(), // cookies
                    new Content("x-unknown", 0, ""),
                    "",
                    -1,
                    -1
            );
        }
        return response;
    }

    private static Request buildRequest(Map.Entry> requestsEntry, JSONObject jsonRequest) {
        JSONObject jsonRequestHeaders = jsonRequest.getJSONObject("params").getJSONObject("request").getJSONObject("headers");

        if (requestsEntry.getValue().get("requestWillBeSentExtraInfo") != null) {
            jsonRequestHeaders = ((JSONObject) requestsEntry.getValue().get("requestWillBeSentExtraInfo")).getJSONObject("params").getJSONObject("headers");
        }

        JSONObject finalJsonRequestHeaders = jsonRequestHeaders;
        Request request = new Request(0,
                jsonRequest.getJSONObject("params").getJSONObject("request").getString("method"),
                jsonRequest.getJSONObject("params").getJSONObject("request").getString("url"),
                "HTTP N/A",
                jsonRequestHeaders.keySet().stream()
                        .filter(key -> !"Authorization".equals(key))
                        .filter(key -> !key.toLowerCase().contains("token"))
                        .map(key -> new Header(key, finalJsonRequestHeaders.getString(key)))
                        .collect(Collectors.toList()),
                new ArrayList<>(), // cookies
                new ArrayList<>(),
                0
        );
        return request;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy