diff --git a/packages/plugin-lighthouse/src/lib/runner/details/__snapshots__/critical-request-chain.type.unit.test.ts.snap b/packages/plugin-lighthouse/src/lib/runner/details/__snapshots__/critical-request-chain.type.unit.test.ts.snap new file mode 100644 index 000000000..8d6fa43ff --- /dev/null +++ b/packages/plugin-lighthouse/src/lib/runner/details/__snapshots__/critical-request-chain.type.unit.test.ts.snap @@ -0,0 +1,108 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`parseCriticalRequestChainToAuditDetails > should convert chains to basic trees 1`] = ` +[ + { + "root": { + "children": [ + { + "children": [ + { + "name": "https://fonts.gstatic.com/s/googlesans/v62/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPjIUvbQoi-E.woff2", + "values": { + "duration": "48.083 ms", + "transferSize": "35.89 kB", + }, + }, + { + "name": "https://fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0me8iUI0.woff2", + "values": { + "duration": "63.943 ms", + "transferSize": "22.31 kB", + }, + }, + ], + "name": "https://fonts.googleapis.com/css?family=Google+Sans:400,500|Roboto:400,400italic,500,500italic,700,700italic|Roboto+Mono:400,500,700&display=swap", + "values": { + "duration": "50.656 ms", + "transferSize": "3.68 kB", + }, + }, + { + "children": [ + { + "name": "https://fonts.gstatic.com/s/materialicons/v143/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2", + "values": { + "duration": "86.765 ms", + "transferSize": "125.78 kB", + }, + }, + ], + "name": "https://fonts.googleapis.com/css2?family=Material+Icons&family=Material+Symbols+Outlined&display=block", + "values": { + "duration": "55.102 ms", + "transferSize": "615 B", + }, + }, + { + "name": "https://www.gstatic.com/devrel-devsite/prod/ve761bca974e16662f27aa8810df6d144acde5bdbeeca0dfd50e25f86621eaa19/chrome/css/app.css", + "values": { + "duration": "70.050 ms", + "transferSize": "133.58 kB", + }, + }, + { + "name": "https://www.gstatic.com/devrel-devsite/prod/ve761bca974e16662f27aa8810df6d144acde5bdbeeca0dfd50e25f86621eaa19/chrome/css/dark-theme.css", + "values": { + "duration": "69.755 ms", + "transferSize": "3.98 kB", + }, + }, + { + "name": "https://developer.chrome.com/extras.css", + "values": { + "duration": "199.327 ms", + "transferSize": "109 B", + }, + }, + ], + "name": "https://developer.chrome.com/docs/lighthouse/performance/critical-request-chains", + "values": { + "duration": "472.304 ms", + "transferSize": "18.66 kB", + }, + }, + "type": "basic", + }, +] +`; + +exports[`parseCriticalRequestChainToAuditDetails > should convert longest chain to table 1`] = ` +{ + "columns": [ + { + "align": "right", + "key": "duration", + "label": "Duration", + }, + { + "align": "right", + "key": "transferSize", + "label": "Transfer size", + }, + { + "align": "right", + "key": "length", + "label": "Length", + }, + ], + "rows": [ + { + "duration": "757.072 ms", + "length": 3, + "transferSize": "125.78 kB", + }, + ], + "title": "Longest chain", +} +`; diff --git a/packages/plugin-lighthouse/src/lib/runner/details/__snapshots__/treemap-data.type.unit.test.ts.snap b/packages/plugin-lighthouse/src/lib/runner/details/__snapshots__/treemap-data.type.unit.test.ts.snap new file mode 100644 index 000000000..d70fcf851 --- /dev/null +++ b/packages/plugin-lighthouse/src/lib/runner/details/__snapshots__/treemap-data.type.unit.test.ts.snap @@ -0,0 +1,102 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`parseTreemapDataToBasicTrees > should convert root nodes to basic trees 1`] = ` +[ + { + "root": { + "children": [ + { + "children": [ + { + "name": "failbot/failbot-error.ts", + "values": { + "resourceBytes": "237 B", + "unusedBytes": "113 B", + }, + }, + { + "name": "remove-child-patch/remove-child-patch.ts", + "values": { + "resourceBytes": "268 B", + }, + }, + { + "name": "fetch-overrides/fetch-overrides.ts", + "values": { + "resourceBytes": "376 B", + "unusedBytes": "263 B", + }, + }, + ], + "name": "ui/packages", + "values": { + "resourceBytes": "881 B", + "unusedBytes": "376 B", + }, + }, + { + "name": "app/assets/modules/environment.ts", + "values": { + "resourceBytes": "42 B", + }, + }, + { + "name": "(unmapped)", + "values": { + "resourceBytes": "515 B", + }, + }, + ], + "name": "https://github.githubassets.com/assets/environment-7b93e0f0c8ff.js", + "values": { + "resourceBytes": "1.4 kB", + "unusedBytes": "376 B", + }, + }, + "type": "basic", + }, + { + "root": { + "children": [ + { + "children": [ + { + "name": "commenting/edit.ts", + "values": { + "resourceBytes": "9.07 kB", + "unusedBytes": "8.03 kB", + }, + }, + { + "name": "html-validation.ts", + "values": { + "duplicatedNormalizedModuleName": "app/assets/modules/github/behaviors/html-validation.ts", + "resourceBytes": "2.03 kB", + "unusedBytes": "2.03 kB", + }, + }, + ], + "name": "app/assets/modules/github/behaviors", + "values": { + "resourceBytes": "11.1 kB", + "unusedBytes": "10.06 kB", + }, + }, + { + "name": "(unmapped)", + "values": { + "resourceBytes": "534 B", + "unusedBytes": "65 B", + }, + }, + ], + "name": "https://github.githubassets.com/assets/app_assets_modules_github_behaviors_commenting_edit_ts-app_assets_modules_github_behaviors_ht-83c235-fb43816ab83c.js", + "values": { + "resourceBytes": "11.62 kB", + "unusedBytes": "10.12 kB", + }, + }, + "type": "basic", + }, +] +`; diff --git a/packages/plugin-lighthouse/src/lib/runner/details/critical-request-chain.type.ts b/packages/plugin-lighthouse/src/lib/runner/details/critical-request-chain.type.ts new file mode 100644 index 000000000..927dbf346 --- /dev/null +++ b/packages/plugin-lighthouse/src/lib/runner/details/critical-request-chain.type.ts @@ -0,0 +1,73 @@ +import type Details from 'lighthouse/types/lhr/audit-details'; +import type { + AuditDetails, + BasicTree, + BasicTreeNode, + Table, +} from '@code-pushup/models'; +import { formatBytes, formatDuration } from '@code-pushup/utils'; + +const DURATION_DECIMALS = 3; + +export function parseCriticalRequestChainToAuditDetails( + details: Details.CriticalRequestChain, +): AuditDetails { + const trees = chainsToTrees(details); + const table = longestChainToTable(details); + return { table, trees }; +} + +function longestChainToTable(details: Details.CriticalRequestChain): Table { + const longestChain = { + duration: formatDuration(details.longestChain.duration, DURATION_DECIMALS), + transferSize: formatBytes(details.longestChain.transferSize), + length: details.longestChain.length, + }; + type ColumnKey = keyof typeof longestChain; + + return { + title: 'Longest chain', + columns: [ + { + key: 'duration' satisfies ColumnKey, + label: 'Duration', + align: 'right', + }, + { + key: 'transferSize' satisfies ColumnKey, + label: 'Transfer size', + align: 'right', + }, + { + key: 'length' satisfies ColumnKey, + label: 'Length', + align: 'right', + }, + ], + rows: [longestChain], + }; +} + +function chainsToTrees(details: Details.CriticalRequestChain): BasicTree[] { + return Object.values(details.chains) + .map(chainToTreeNode) + .map(root => ({ type: 'basic', root })); +} + +function chainToTreeNode( + chain: Details.SimpleCriticalRequestNode[string], +): BasicTreeNode { + return { + name: chain.request.url, + values: { + duration: formatDuration( + (chain.request.endTime - chain.request.startTime) * 1000, + DURATION_DECIMALS, + ), + transferSize: formatBytes(chain.request.transferSize), + }, + ...(chain.children && { + children: Object.values(chain.children).map(chainToTreeNode), + }), + }; +} diff --git a/packages/plugin-lighthouse/src/lib/runner/details/critical-request-chain.type.unit.test.ts b/packages/plugin-lighthouse/src/lib/runner/details/critical-request-chain.type.unit.test.ts new file mode 100644 index 000000000..9bbe96e58 --- /dev/null +++ b/packages/plugin-lighthouse/src/lib/runner/details/critical-request-chain.type.unit.test.ts @@ -0,0 +1,114 @@ +import type Details from 'lighthouse/types/lhr/audit-details.js'; +import { parseCriticalRequestChainToAuditDetails } from './critical-request-chain.type.js'; + +describe('parseCriticalRequestChainToAuditDetails', () => { + const details: Details.CriticalRequestChain = { + type: 'criticalrequestchain', + chains: { + C49E1A2533B1ECBA8C5DB9292950C3C1: { + request: { + url: 'https://developer.chrome.com/docs/lighthouse/performance/critical-request-chains', + startTime: 28_303.087_316, + endTime: 28_303.559_62, + responseReceivedTime: 28_303.558_19, + transferSize: 19_110, + }, + children: { + '52823.2': { + request: { + url: 'https://fonts.googleapis.com/css?family=Google+Sans:400,500|Roboto:400,400italic,500,500italic,700,700italic|Roboto+Mono:400,500,700&display=swap', + startTime: 28_303.570_333, + endTime: 28_303.620_989, + responseReceivedTime: 28_303.608_522, + transferSize: 3765, + }, + children: { + '52823.43': { + request: { + url: 'https://fonts.gstatic.com/s/googlesans/v62/4UasrENHsxJlGDuGo1OIlJfC6l_24rlCK1Yo_Iqcsih3SAyH6cAwhX9RPjIUvbQoi-E.woff2', + startTime: 28_303.757_482, + endTime: 28_303.805_565, + responseReceivedTime: 28_303.787_04, + transferSize: 36_754, + }, + }, + '52823.128': { + request: { + url: 'https://fonts.gstatic.com/s/robotomono/v23/L0xTDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vrtSM1J-gEPT5Ese6hmHSh0me8iUI0.woff2', + startTime: 28_303.757_727, + endTime: 28_303.821_67, + responseReceivedTime: 28_303.805_798, + transferSize: 22_850, + }, + }, + }, + }, + '52823.3': { + request: { + url: 'https://fonts.googleapis.com/css2?family=Material+Icons&family=Material+Symbols+Outlined&display=block', + startTime: 28_303.573_062, + endTime: 28_303.628_164, + responseReceivedTime: 28_303.626_091, + transferSize: 615, + }, + children: { + '52823.141': { + request: { + url: 'https://fonts.gstatic.com/s/materialicons/v143/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2', + startTime: 28_303.757_623, + endTime: 28_303.844_388, + responseReceivedTime: 28_303.820_133, + transferSize: 128_797, + }, + }, + }, + }, + '52823.4': { + request: { + url: 'https://www.gstatic.com/devrel-devsite/prod/ve761bca974e16662f27aa8810df6d144acde5bdbeeca0dfd50e25f86621eaa19/chrome/css/app.css', + startTime: 28_303.573_196, + endTime: 28_303.643_246, + responseReceivedTime: 28_303.593_019, + transferSize: 136_789, + }, + }, + '52823.5': { + request: { + url: 'https://www.gstatic.com/devrel-devsite/prod/ve761bca974e16662f27aa8810df6d144acde5bdbeeca0dfd50e25f86621eaa19/chrome/css/dark-theme.css', + startTime: 28_303.573_293_999_998, + endTime: 28_303.643_049, + responseReceivedTime: 28_303.641_657, + transferSize: 4072, + }, + }, + '52823.6': { + request: { + url: 'https://developer.chrome.com/extras.css', + startTime: 28_303.573_378, + endTime: 28_303.772_705, + responseReceivedTime: 28_303.771_657, + transferSize: 109, + }, + }, + }, + }, + }, + longestChain: { + duration: 757.072_000_000_625_8, + length: 3, + transferSize: 128_797, + }, + }; + + it('should convert chains to basic trees', () => { + expect( + parseCriticalRequestChainToAuditDetails(details).trees, + ).toMatchSnapshot(); + }); + + it('should convert longest chain to table', () => { + expect( + parseCriticalRequestChainToAuditDetails(details).table, + ).toMatchSnapshot(); + }); +}); diff --git a/packages/plugin-lighthouse/src/lib/runner/details/details.ts b/packages/plugin-lighthouse/src/lib/runner/details/details.ts index df54d238c..c58c7afe4 100644 --- a/packages/plugin-lighthouse/src/lib/runner/details/details.ts +++ b/packages/plugin-lighthouse/src/lib/runner/details/details.ts @@ -2,11 +2,13 @@ import { bold, yellow } from 'ansis'; import type { FormattedIcu } from 'lighthouse'; import type Details from 'lighthouse/types/lhr/audit-details'; import type { Result } from 'lighthouse/types/lhr/audit-result'; -import type { AuditDetails, Table } from '@code-pushup/models'; +import type { AuditDetails } from '@code-pushup/models'; import { ui } from '@code-pushup/utils'; import { PLUGIN_SLUG } from '../constants.js'; +import { parseCriticalRequestChainToAuditDetails } from './critical-request-chain.type.js'; import { parseOpportunityToAuditDetailsTable } from './opportunity.type.js'; import { parseTableToAuditDetailsTable } from './table.type.js'; +import { parseTreemapDataToBasicTrees } from './treemap-data.type.js'; export function toAuditDetails>( details: T | undefined, @@ -19,47 +21,49 @@ export function toAuditDetails>( switch (type) { case 'table': - const table: Table | undefined = parseTableToAuditDetailsTable(details); + const table = parseTableToAuditDetailsTable(details); return table ? { table } : {}; case 'opportunity': - const opportunity: Table | undefined = - parseOpportunityToAuditDetailsTable(details); + const opportunity = parseOpportunityToAuditDetailsTable(details); return opportunity ? { table: opportunity } : {}; + case 'criticalrequestchain': + return parseCriticalRequestChainToAuditDetails(details); + case 'treemap-data': + const trees = parseTreemapDataToBasicTrees(details); + return { trees }; + + // TODO: add 'list' once array of tables supported in audit details + + // TODO: add 'screenshot' and 'filmstrip' once images supported in audit details } return {}; } // @TODO implement all details -export const unsupportedDetailTypes = new Set([ +export const unsupportedDetailTypes = new Set([ + 'list', 'debugdata', - 'treemap-data', 'screenshot', 'filmstrip', - 'criticalrequestchain', ]); -export function logUnsupportedDetails( - lhrAudits: Result[], - { displayCount = 3 }: { displayCount?: number } = {}, -) { +export function logUnsupportedDetails(lhrAudits: Result[]) { const slugsWithDetailParsingErrors = [ ...new Set( lhrAudits - .filter(({ details }) => - unsupportedDetailTypes.has(details?.type as string), + .filter( + ({ details }) => details && unsupportedDetailTypes.has(details.type), ) .map(({ details }) => details?.type), ), ]; if (slugsWithDetailParsingErrors.length > 0) { - const postFix = (count: number) => - count > displayCount ? ` and ${count - displayCount} more.` : ''; ui().logger.debug( `${yellow('⚠')} Plugin ${bold( PLUGIN_SLUG, )} skipped parsing of unsupported audit details: ${bold( - slugsWithDetailParsingErrors.slice(0, displayCount).join(', '), - )}${postFix(slugsWithDetailParsingErrors.length)}`, + slugsWithDetailParsingErrors.join(', '), + )}.`, ); } } diff --git a/packages/plugin-lighthouse/src/lib/runner/details/details.unit.test.ts b/packages/plugin-lighthouse/src/lib/runner/details/details.unit.test.ts index 79be8d2db..7e6bf1106 100644 --- a/packages/plugin-lighthouse/src/lib/runner/details/details.unit.test.ts +++ b/packages/plugin-lighthouse/src/lib/runner/details/details.unit.test.ts @@ -3,24 +3,23 @@ import type { FormattedIcu } from 'lighthouse'; import type Details from 'lighthouse/types/lhr/audit-details'; import type { Result } from 'lighthouse/types/lhr/audit-result'; import { describe, expect, it } from 'vitest'; +import type { AuditDetails } from '@code-pushup/models'; import { ui } from '@code-pushup/utils'; import { logUnsupportedDetails, toAuditDetails } from './details.js'; describe('logUnsupportedDetails', () => { it('should log unsupported entries', () => { - logUnsupportedDetails([ - { details: { type: 'screenshot' } }, - ] as unknown as Result[]); + logUnsupportedDetails([{ details: { type: 'screenshot' } }] as Result[]); expect(ui()).toHaveLoggedTimes(1); expect(ui()).toHaveLogged( 'debug', `${yellow('⚠')} Plugin ${bold( 'lighthouse', - )} skipped parsing of unsupported audit details: ${bold('screenshot')}`, + )} skipped parsing of unsupported audit details: ${bold('screenshot')}.`, ); }); - it('should log only 3 details of unsupported entries', () => { + it('should log many unsupported entries', () => { logUnsupportedDetails([ { details: { type: 'table' } }, { details: { type: 'filmstrip' } }, @@ -29,7 +28,7 @@ describe('logUnsupportedDetails', () => { { details: { type: 'debugdata' } }, { details: { type: 'treemap-data' } }, { details: { type: 'criticalrequestchain' } }, - ] as unknown as Result[]); + ] as Result[]); expect(ui()).toHaveLoggedTimes(1); expect(ui()).toHaveLogged( 'debug', @@ -37,7 +36,7 @@ describe('logUnsupportedDetails', () => { 'lighthouse', )} skipped parsing of unsupported audit details: ${bold( 'filmstrip, screenshot, debugdata', - )} and 2 more.`, + )}.`, ); }); }); @@ -265,7 +264,7 @@ describe('toAuditDetails', () => { expect(outputs).toStrictEqual({}); }); - it('should inform that debugdata detail type is not supported yet', () => { + it('should ignore unsupported detail type debugdata', () => { const outputs = toAuditDetails({ type: 'debugdata', items: [ @@ -275,12 +274,10 @@ describe('toAuditDetails', () => { ], }); - // @TODO add check that cliui.logger is called. Resolve TODO after PR #487 is merged. - expect(outputs).toStrictEqual({}); }); - it('should inform that filmstrip detail type is not supported yet', () => { + it('should ignore unsupported detail type filmstrip', () => { const outputs = toAuditDetails({ type: 'filmstrip', scale: 3000, @@ -296,7 +293,7 @@ describe('toAuditDetails', () => { expect(outputs).toStrictEqual({}); }); - it('should inform that screenshot detail type is not supported yet', () => { + it('should ignore unsupported detail type screenshot', () => { const outputs = toAuditDetails({ type: 'screenshot', timing: 541, @@ -307,16 +304,35 @@ describe('toAuditDetails', () => { expect(outputs).toStrictEqual({}); }); - it('should inform that treemap-data detail type is not supported yet', () => { + it('should render audit details of type treemap', () => { const outputs = toAuditDetails({ type: 'treemap-data', - nodes: [], + nodes: [ + { + name: 'http://127.0.0.1:8000/js/index.js', + resourceBytes: 1_212_311, + unusedBytes: 634_854, + }, + ], }); - expect(outputs).toStrictEqual({}); + expect(outputs).toStrictEqual({ + trees: [ + { + type: 'basic', + root: { + name: 'http://127.0.0.1:8000/js/index.js', + values: { + resourceBytes: '1.16 MB', + unusedBytes: '619.97 kB', + }, + }, + }, + ], + }); }); - it('should inform that criticalrequestchain detail type is not supported yet', () => { + it('should render audit details of type criticalrequestchain', () => { const outputs = toAuditDetails({ type: 'criticalrequestchain', chains: { @@ -337,6 +353,34 @@ describe('toAuditDetails', () => { }, }); - expect(outputs).toStrictEqual({}); + expect(outputs).toStrictEqual({ + trees: [ + { + type: 'basic', + root: { + name: 'https://example.com/', + values: { + duration: '508.498 ms', + transferSize: '849 B', + }, + }, + }, + ], + table: { + title: 'Longest chain', + columns: [ + { key: 'duration', label: 'Duration', align: 'right' }, + { key: 'transferSize', label: 'Transfer size', align: 'right' }, + { key: 'length', label: 'Length', align: 'right' }, + ], + rows: [ + { + duration: '508.498 ms', + transferSize: '849 B', + length: 1, + }, + ], + }, + }); }); }); diff --git a/packages/plugin-lighthouse/src/lib/runner/details/treemap-data.type.ts b/packages/plugin-lighthouse/src/lib/runner/details/treemap-data.type.ts new file mode 100644 index 000000000..25886573f --- /dev/null +++ b/packages/plugin-lighthouse/src/lib/runner/details/treemap-data.type.ts @@ -0,0 +1,31 @@ +import type { Treemap } from 'lighthouse'; +import type Details from 'lighthouse/types/lhr/audit-details'; +import type { BasicTree, BasicTreeNode } from '@code-pushup/models'; +import { formatBytes } from '@code-pushup/utils'; + +export function parseTreemapDataToBasicTrees( + details: Details.TreemapData, +): BasicTree[] { + return details.nodes.map(node => ({ + type: 'basic', + root: treemapNodeToBasicTreeNode(node), + })); +} + +function treemapNodeToBasicTreeNode(node: Treemap.Node): BasicTreeNode { + return { + name: node.name, + values: { + resourceBytes: formatBytes(node.resourceBytes), + ...(node.unusedBytes != null && { + unusedBytes: formatBytes(node.unusedBytes), + }), + ...(node.duplicatedNormalizedModuleName && { + duplicatedNormalizedModuleName: node.duplicatedNormalizedModuleName, + }), + }, + ...(node.children && { + children: node.children.map(treemapNodeToBasicTreeNode), + }), + }; +} diff --git a/packages/plugin-lighthouse/src/lib/runner/details/treemap-data.type.unit.test.ts b/packages/plugin-lighthouse/src/lib/runner/details/treemap-data.type.unit.test.ts new file mode 100644 index 000000000..44cd36a78 --- /dev/null +++ b/packages/plugin-lighthouse/src/lib/runner/details/treemap-data.type.unit.test.ts @@ -0,0 +1,80 @@ +import { parseTreemapDataToBasicTrees } from './treemap-data.type.js'; + +describe('parseTreemapDataToBasicTrees', () => { + it('should convert root nodes to basic trees', () => { + expect( + parseTreemapDataToBasicTrees({ + type: 'treemap-data', + nodes: [ + { + name: 'https://github.githubassets.com/assets/environment-7b93e0f0c8ff.js', + resourceBytes: 1438, + unusedBytes: 376, + children: [ + { + name: 'ui/packages', + resourceBytes: 881, + unusedBytes: 376, + children: [ + { + name: 'failbot/failbot-error.ts', + resourceBytes: 237, + unusedBytes: 113, + }, + { + name: 'remove-child-patch/remove-child-patch.ts', + resourceBytes: 268, + }, + { + name: 'fetch-overrides/fetch-overrides.ts', + resourceBytes: 376, + unusedBytes: 263, + }, + ], + }, + { + name: 'app/assets/modules/environment.ts', + resourceBytes: 42, + }, + { + name: '(unmapped)', + resourceBytes: 515, + }, + ], + }, + { + name: 'https://github.githubassets.com/assets/app_assets_modules_github_behaviors_commenting_edit_ts-app_assets_modules_github_behaviors_ht-83c235-fb43816ab83c.js', + resourceBytes: 11_903, + unusedBytes: 10_362, + children: [ + { + name: 'app/assets/modules/github/behaviors', + resourceBytes: 11_369, + unusedBytes: 10_297, + children: [ + { + name: 'commenting/edit.ts', + resourceBytes: 9290, + unusedBytes: 8218, + }, + { + name: 'html-validation.ts', + resourceBytes: 2079, + unusedBytes: 2079, + duplicatedNormalizedModuleName: + 'app/assets/modules/github/behaviors/html-validation.ts', + }, + ], + }, + { + name: '(unmapped)', + resourceBytes: 534, + unusedBytes: 65, + }, + ], + }, + ], + }), + ).toMatchSnapshot(); + }); +});