Skip to content

[Feature]: Port coverage API to playwright-java #1923

@DF-Nico

Description

@DF-Nico

🚀 Feature Request

Recently, playwright in its node version proposes a coverage API to help extracting coverage reports.
Such API could also be helpful in java when using PW in E2E testing and having coverage report on .ts files

Example

Previous code would look like:

public abstract class AbtractE2ETest {
    @BeforeEach
    void prepare() {
        // create browserContext/page...
        page.coverage().startJSCoverage();
    }

    @AfterEach
    void tearDown(TestInfo testInfo) {
        var result = page.coverage().stopJSCoverage();
        // Serialize result.entries to a .json file for extraction and conversion to lcov with another tool later
    }
}

Motivation

Recently, istanbul was used for frontend coverage, and something like: var covReport = page.evaluate("window.__coverage__"); was enough.
However, a migration to v8 coverage engine made this approach more complex.
Having a clear and "easy" API to achieve this would be great. Current state after migration on my side would be something like:

public abstract class AbtractE2ETest {
    private final Set<String> scriptsId = new HashSet<>();
    private final Map<String, JsonElement> scriptsSources = new HashMap<>();
    /*
     * ...
     * /

    @BeforeEach
    void prepare() {
        // create browserContext/page...
        scriptsId.clear();
        scriptsSources.clear();

        cdpSession = browserContext.newCDPSession(page);
        cdpSession.on("Debugger.scriptParsed", this::onScriptParsed);
        // Profiler
        cdpSession.send("Profiler.enable");
        var profilerParams = new JsonObject();
        profilerParams.addProperty("callCount", true);
        profilerParams.addProperty("detailed", true);
        cdpSession.send("Profiler.startPreciseCoverage", profilerParams);

        // Debugger
        cdpSession.send("Debugger.enable");
        var debuggerParam = new JsonObject();
        debuggerParam.addProperty("skip", true);
        cdpSession.send("Debugger.setSkipAllPauses", debuggerParam);
    }

    private void onScriptParsed(JsonObject event) {
        var scriptId = event.get("scriptId").getAsString();
        var scriptUrl = event.get("url").getAsString();
        if (!scriptsId.contains(scriptId) && scriptUrl != null && !scriptUrl.isEmpty()) {
            scriptsId.add(scriptId);
            var scriptParams = new JsonObject();
            scriptParams.addProperty("scriptId", scriptId);
            var scriptResult = cdpSession.send("Debugger.getScriptSource", scriptParams);
            var scriptSource = scriptResult.get("scriptSource");
            if (scriptSource != null) {
                scriptsSources.put(scriptId, scriptSource);
            }
        }
  }

    @AfterEach
    void tearDown(TestInfo testInfo) {
        try {
            var result = cdpSession.send("Profiler.takePreciseCoverage");
            cdpSession.send("Profiler.stopPreciseCoverage");
            cdpSession.send("Profiler.disable");
            cdpSession.send("Debugger.disable");
            cdpSession.off("Debugger.scriptParsed", this::onScriptParsed);
            cdpSession.detach();

            var scripts = result.getAsJsonArray("result");
            var entries = new JsonArray();
            if (scripts != null && !scripts.isEmpty()) {
                for (var element : scripts) {
                    var entry = element.getAsJsonObject();
                    var scriptId = entry.get("scriptId").getAsString();
                if (!scriptsSources.containsKey(scriptId)) {
                    continue;
                }
                entry.add("source", scriptsSources.get(scriptId));
                entries.add(entry);
            }
            var json = entries.toString();
            FileUtils.writeStringToFile(new File("../.nyc_output/" + testInfo.getDisplayName() + ".json"), json, StandardCharsets.UTF_8);
          }
        } catch (Exception e) {
          log.error("Can not extract coverage for test : {}", testInfo, e);
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions