Skip to content

[Feature] advanced configurations for routeFromHAR #21405

Open
@NoamGaash

Description

@NoamGaash

Background / Use cases

The routeFromHAR functionality encapsulates two behaviors. When update is true, it behaves as a custom network recorder, while when update is false (default) it serves as a network communication mock.
I would like to suggest several improvements:

saving minimal data

today, each HAR entry contains a lot of unnecessary data - timing, HTTP version, request headers, and more.
while this data might be useful for analysis, most of it is not being used for network traffic replay.
Despite that omitting the unnecessary fields would make a deviation from the formal HAR specifications, I believe it is worth it in terms of clarity, bundle size, concise git differences, output predictability, and maintainability.

example

before:

      {
        "startedDateTime": "2023-03-05T05:09:17.557Z",
        "time": 8.897,
        "request": {
          "method": "GET",
          "url": "http://localhost:10102/api/pages?accountId=<my_id>",
          "httpVersion": "HTTP/1.1",
          "cookies": [],
          "headers": [
            { "name": "Accept", "value": "application/json, text/plain, */*" },
            { "name": "Accept-Language", "value": "en-US" },
            { "name": "Cookie", "value": "cookies....." },
            { "name": "Expect", "value": "202+location" },
            { "name": "Referer", "value": "http://localhost:port/path?accountId=id },
            { "name": "User-Agent", "value": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/110.0.5481.177 Safari/537.36" },
            { "name": "sec-ch-ua", "value": "" },
            { "name": "sec-ch-ua-mobile", "value": "?0" },
            { "name": "sec-ch-ua-platform", "value": "" },
            { "name": "x-client-request-id", "value": "8a887b6a-7638-4e0a-bf04-b4c97a78199e--4" }
          ],
          "queryString": [
            {
              "name": "accountId",
              "value": "<my id>"
            }
          ],
          "headersSize": -1,
          "bodySize": -1
        },
        "response": {
          "status": 200,
          "statusText": "OK",
          "httpVersion": "HTTP/1.1",
          "cookies": [],
          "headers": [
            { "name": "X-Powered-By", "value": "Express" },
            { "name": "cache-control", "value": "no-store" },
            { "name": "connection", "value": "close" },
            { "name": "content-encoding", "value": "gzip" },
            { "name": "content-type", "value": "application/json; charset=utf-8" },
            { "name": "date", "value": "Sun, 05 Mar 2023 05:09:17 GMT" },
            { "name": "strict-transport-security", "value": "max-age=31536000" },
            { "name": "transfer-encoding", "value": "chunked" },
            { "name": "vary", "value": "Accept-Encoding" },
            { "name": "x-content-type-options", "value": "nosniff" },
            { "name": "x-permitted-cross-domain-policies", "value": "None" },
            { "name": "x-xss-protection", "value": "1; mode=block" }
          ],
          "content": {
            "size": -1,
            "mimeType": "application/json; charset=utf-8",
            "text": "{\"pages\":[{\"id\":\"1234\",\"name\":\"a new created test project\",\"createdAt\":\"2023-03-05T05:09:18.0612396+00:00\",\"updatedAt\":\"2023-03-05T05:09:18.0612396+00:00\",\"source\":\"filesystem\",\"status\":\"New\",\"metadata\":{}},{\"id\":\"00000251727294129115\",\"name\":\"with implementation\",\"createdAt\":\"2023-01-29T15:37:50.8846963+00:00\",\"updatedAt\":\"2023-01-29T15:38:07.5206989+00:00\",\"source\":\"filesystem\",\"status\":\"None\",\"metadata\":{}},{\"id\":\"00000251727302447488\",\"name\":\"no implementation\",\"createdAt\":\"2023-01-29T13:19:12.5111176+00:00\",\"updatedAt\":\"2023-01-29T15:37:23.8938755+00:00\",\"source\":\"filesystem\",\"status\":\"None\",\"metadata\":{}}]}"
          },
          "headersSize": -1,
          "bodySize": -1,
          "redirectURL": ""
        },
        "cache": {},
        "timings": { "send": -1, "wait": -1, "receive": 8.897 }
      }

after:

      {
        "request": {
          "method": "GET",
          "url": "http://localhost:10102/api/pages?accountId=<my_id>"
          ]
        },
        "response": {
          "status": 200,
          "statusText": "OK",
          "httpVersion": "HTTP/1.1",
          "cookies": [],
          "headers": [
            { "name": "X-Powered-By", "value": "Express" },
            { "name": "cache-control", "value": "no-store" },
            { "name": "connection", "value": "close" },
            { "name": "content-encoding", "value": "gzip" },
            { "name": "content-type", "value": "application/json; charset=utf-8" },
            { "name": "date", "value": "Sun, 05 Mar 2023 05:09:17 GMT" },
            { "name": "strict-transport-security", "value": "max-age=31536000" },
            { "name": "transfer-encoding", "value": "chunked" },
            { "name": "vary", "value": "Accept-Encoding" },
            { "name": "x-content-type-options", "value": "nosniff" },
            { "name": "x-permitted-cross-domain-policies", "value": "None" },
            { "name": "x-xss-protection", "value": "1; mode=block" }
          ],
          "content": {
            "mimeType": "application/json; charset=utf-8",
            "text": "{\"pages\":[{\"id\":\"1234\",\"name\":\"a new created test project\",\"createdAt\":\"2023-03-05T05:09:18.0612396+00:00\",\"updatedAt\":\"2023-03-05T05:09:18.0612396+00:00\",\"source\":\"filesystem\",\"status\":\"New\",\"metadata\":{}},{\"id\":\"00000251727294129115\",\"name\":\"with implementation\",\"createdAt\":\"2023-01-29T15:37:50.8846963+00:00\",\"updatedAt\":\"2023-01-29T15:38:07.5206989+00:00\",\"source\":\"filesystem\",\"status\":\"None\",\"metadata\":{}},{\"id\":\"00000251727302447488\",\"name\":\"no implementation\",\"createdAt\":\"2023-01-29T13:19:12.5111176+00:00\",\"updatedAt\":\"2023-01-29T15:37:23.8938755+00:00\",\"source\":\"filesystem\",\"status\":\"None\",\"metadata\":{}}]}"
          }
        }
      }

excludeUrl

today, the page.routeFromHAR method receives URL option that defines which URLs should be included in the resulting HAR file.
I suggest we should have excludeUrl property. the property would allow users to exclude specific path(s) from recording.

example

before:

page.routeFromHAR(
        `__tests__/networks_cache/${step}.har`,
        {
            url: /api|css|png|tff/, // user tries to list all URLs that could be memorized in the HAR file
        }
    );

after

page.routeFromHAR(
        `__tests__/networks_cache/${step}.har`,
        {
            url: /.*/, // user can use a wildcard
            excludeUrl: "http://localhost/bundle.js" // user exclude specific path
        }
    );

smart matching algorithm

Playwright docs state that:

HAR replay matches URL and HTTP method strictly. For POST requests, it also matches POST payloads strictly. If multiple recordings match a request, the one with the most matching headers is picked

Would it be possible to grant users more control over he match algorithm?

example

page.routeFromHAR(
        `__tests__/networks_cache/${step}.har`,
        {
            url: /api/,
            urlMatching: {
                "GET": {
                    matchLocation: "ignoreOrigin",
                    matchHeaders: false,
                    
                },
                "POST": {
                    matchPayload: false
                },
                "delete": {
                    match: (harEntry, currRequest) => {
                        return userDefinedMatchScoringFunction(harEntry, currRequest);
                    }
                }
            }
        }
    );

summery

Thank you for considering my suggestions!
Please note that my first suggestion is a breaking change - it would omit data from the HAR file, and there is a chance that some users rely on the timing/headers data in there.
I would love to hear your opinion and get some feedback before implementing any changes or submitting PRs.

Thank you for maintaining Playwright! I love this tool.
Noam

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions