Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,16 @@ export function harFromMessages(messages, options) {
_resourceType: params.type ? params.type.toLowerCase() : undefined
};

// CDP `renderBlockingStatus` (Chrome 108+) — values are PascalCase
// (`Blocking`, `NonBlocking`, `InBodyParserBlocking`, `Potentially…`).
// waterfall-tools' WPT-style renderer matches `_renderBlocking ===
// 'blocking'` to draw the orange ⊗ marker, so lowercase the value
// here. Other variants ride along verbatim-lowercased for any
// consumer that wants to inspect them.
if (params.renderBlockingStatus) {
entry._renderBlocking = params.renderBlockingStatus.toLowerCase();
}

// The object initiator change according to its type
switch (params.initiator.type) {
case 'parser': {
Expand Down
140 changes: 140 additions & 0 deletions test/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,146 @@ test('Parses IPv6 address', t => {
);
});

test('Lifts CDP renderBlockingStatus onto entries as `_renderBlocking`', t => {
// Minimal synthetic CDP stream: one render-blocking document + one
// non-blocking subresource. The renderer in waterfall-tools matches
// `_renderBlocking === 'blocking'` (lowercase) to draw the orange ⊗
// marker, so we assert the casing here too.
const frameId = 'F1';
const baseMessages = (requestId, url, renderBlockingStatus) => [
{
method: 'Network.requestWillBeSent',
params: {
requestId,
frameId,
loaderId: 'L1',
documentURL: 'https://example.com/',
request: {
url,
method: 'GET',
headers: {},
initialPriority: 'High'
},
timestamp: 1,
wallTime: 1_700_000_000,
initiator: { type: 'other' },
type: requestId === '1' ? 'Document' : 'Script',
...(renderBlockingStatus ? { renderBlockingStatus } : {})
}
},
{
method: 'Network.responseReceived',
params: {
requestId,
frameId,
loaderId: 'L1',
timestamp: 2,
type: requestId === '1' ? 'Document' : 'Script',
response: {
url,
status: 200,
statusText: 'OK',
headers: { 'content-type': 'text/html' },
mimeType: 'text/html',
fromDiskCache: false,
fromServiceWorker: false,
encodedDataLength: 100,
protocol: 'http/1.1',
connectionId: 1,
remoteIPAddress: '127.0.0.1',
timing: {
requestTime: 1,
sendStart: 0,
sendEnd: 1,
receiveHeadersEnd: 2
}
}
}
},
{
method: 'Network.loadingFinished',
params: { requestId, timestamp: 3, encodedDataLength: 100 }
}
];
const messages = [
{
method: 'Page.frameStartedLoading',
params: { frameId }
},
...baseMessages('1', 'https://example.com/', 'Blocking'),
...baseMessages('2', 'https://example.com/app.js', 'NonBlocking')
];
const har = harFromMessages(messages);
const byUrl = Object.fromEntries(
har.log.entries.map(e => [e.request.url, e])
);
t.is(byUrl['https://example.com/']._renderBlocking, 'blocking');
t.is(byUrl['https://example.com/app.js']._renderBlocking, 'nonblocking');
});

test('Omits `_renderBlocking` when CDP did not report it', t => {
// Older Chrome builds don't emit renderBlockingStatus at all — make sure
// we don't materialise the field as `undefined` or an empty string in
// that case. Absent input → absent output.
const messages = [
{ method: 'Page.frameStartedLoading', params: { frameId: 'F1' } },
{
method: 'Network.requestWillBeSent',
params: {
requestId: '1',
frameId: 'F1',
loaderId: 'L1',
documentURL: 'https://example.com/',
request: {
url: 'https://example.com/',
method: 'GET',
headers: {},
initialPriority: 'High'
},
timestamp: 1,
wallTime: 1_700_000_000,
initiator: { type: 'other' },
type: 'Document'
}
},
{
method: 'Network.responseReceived',
params: {
requestId: '1',
frameId: 'F1',
loaderId: 'L1',
timestamp: 2,
type: 'Document',
response: {
url: 'https://example.com/',
status: 200,
statusText: 'OK',
headers: { 'content-type': 'text/html' },
mimeType: 'text/html',
fromDiskCache: false,
fromServiceWorker: false,
encodedDataLength: 100,
protocol: 'http/1.1',
connectionId: 1,
remoteIPAddress: '127.0.0.1',
timing: {
requestTime: 1,
sendStart: 0,
sendEnd: 1,
receiveHeadersEnd: 2
}
}
}
},
{
method: 'Network.loadingFinished',
params: { requestId: '1', timestamp: 3, encodedDataLength: 100 }
}
];
const har = harFromMessages(messages);
t.false('_renderBlocking' in har.log.entries[0]);
});

test('Forwards the resource type value', t => {
const perflogPath = perflog('www.google.ru.json');
const expected = {
Expand Down