Skip to content

Commit b8a1cad

Browse files
fix: network conditions emulation logics
1 parent 516e953 commit b8a1cad

File tree

9 files changed

+243
-169
lines changed

9 files changed

+243
-169
lines changed

Readme.md

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,6 @@
1515
<h3 align="center">A comprehensive performance metrics collector for Playwright tests. Collect and analyze web vital metrics, network timing, and resource usage in your Playwright tests.</h3>
1616
<img src="img\performance-UI.png" alt="Performance-UI" />
1717

18-
19-
## Features
20-
21-
- Collect Web Vitals (LCP, CLS)
22-
- Network timing metrics (TTFB, resource timing)
23-
- Network condition emulation (3G, 4G, WiFi)
24-
- Resource usage tracking
25-
- TypeScript support
26-
- Easy integration with Playwright tests
27-
2818
## Concept
2919

3020
The playwright-performance-metrics plugin introduces a powerful way to measure and assert on web performance metrics directly in your Playwright tests. Unlike traditional end-to-end testing that focuses on functionality, this plugin enables teams to catch performance regressions early and maintain high performance standards through automated testing.
@@ -59,13 +49,16 @@ Both plugins focus on performance testing, but they serve different purposes:
5949
**Key Features**
6050

6151
- Real-time performance metrics collection during test execution
52+
- Collect Web Vitals (LCP, CLS)
53+
- Network timing metrics (TTFB, resource timing)
54+
- Network condition emulation (3G, 4G, WiFi)
6255
- Built-in retry mechanisms for reliable measurements
63-
- Support for Core Web Vitals and other key performance indicators
6456
- Seamless integration with existing Playwright tests
6557
- Type definitions for TypeScript support
6658
- Configurable thresholds and timing options
59+
- Resource usage tracking
6760

68-
The collectMetrics returns the object containing the collected performance metrics:
61+
The **collectMetrics** method returns the object containing the collected performance metrics:
6962

7063
```
7164
PerformanceMetrics {
@@ -88,6 +81,11 @@ The collectMetrics returns the object containing the collected performance metri
8881
}
8982
```
9083

84+
The **initialize** method accepts the predefined network condition preset (provided via **DefaultNetworkPresets**) or custom options and applies it to the current test run.
85+
86+
87+
88+
9189
**Available Metrics**
9290

9391
| **Metric** | **Description** | **Typical Threshold** |
@@ -125,10 +123,9 @@ import { test } from '@playwright/test';
125123
import { PerformanceMetricsCollector } from 'playwright-performance-metrics';
126124

127125
test('measure page performance', async ({ page }) => {
128-
const collector = new PerformanceMetricsCollector(page);
129-
126+
const collector = new PerformanceMetricsCollector();
130127
await page.goto('https://example.com', { waitUntil: 'networkidle' })
131-
const metrics = await collector.collectMetrics({
128+
const metrics = await collector.collectMetrics(page, {
132129
timeout: 10000,
133130
initialDelay: 1000
134131
});
@@ -153,7 +150,7 @@ test('measure page performance', async ({ page }) => {
153150
expect(metrics.timeToFirstByte.redirect, 'Redirect time is less than 100ms').toBeLessThan(100)
154151
expect(metrics.timeToFirstByte.tls, 'TLS time is less than 100ms').toBeLessThan(100)
155152
expect(metrics.timeToFirstByte.connection, 'Connection time is less than 100ms').toBeLessThan(100)
156-
});
153+
})
157154
```
158155

159156
**With network emulation:**
@@ -162,21 +159,23 @@ Library provides predefined presets for network conditions.
162159

163160
- REGULAR_4G
164161
- SLOW_3G
162+
- FAST_3G
165163
- FAST_WIFI
166164

167165
```typescript
168166
import { DefaultNetworkPresets } from 'playwright-performance-metrics';
169167

170168
test('measure performance with slow 3G', async ({ page }) => {
171-
const collector = new PerformanceMetricsCollector(page);
172-
await page.goto('https://example.com', { waitUntil: 'networkidle' })
173-
const metrics = await collector.collectMetrics({
174-
networkConditions: DefaultNetworkPresets.SLOW_3G,
169+
const collector = new PerformanceMetricsCollector();
170+
await collector.initialize(page, DefaultNetworkPresets.SLOW_3G)
171+
await page.goto('https://example.com')
172+
const metrics = await collector.collectMetrics(page, {
175173
timeout: 30000
176-
});
177-
178-
console.log('Slow 3G metrics:', metrics);
179-
});
174+
})
175+
await collector.cleanup()
176+
expect(slowNetworkMetrics.domCompleteTiming).toBeLessThan(25000)
177+
console.log('Slow 3G metrics:', metrics)
178+
})
180179
```
181180

182181
**With fixtures:**
@@ -189,7 +188,7 @@ import { PerformanceMetricsCollector } from 'playwright-performance-metrics'
189188

190189
const test = base.extend({
191190
collector: async ({ page }, use) => {
192-
const collector = new PerformanceMetricsCollector(page)
191+
const collector = new PerformanceMetricsCollector()
193192
await use(collector)
194193
}
195194
})
@@ -202,50 +201,61 @@ import { expect, test } from './fixture.ts'
202201

203202
test('measure page performance', async ({ page, collector }) => {
204203
await page.goto('https://example.com', { waitUntil: 'networkidle' })
205-
const metrics = await collector.collectMetrics()
204+
const metrics = await collector.collectMetrics(page)
206205
expect(metrics.domCompleteTiming, 'DOM Complete is less than 900ms').toBeLessThan(900)
207206
expect(metrics.paint?.firstContentfulPaint, 'First Contentful Paint is less than 2000ms').toBeLessThan(2000)})
208207
})
209208
```
209+
For more examples see tests in the package repository - https://github.com/Valiantsin2021/playwright-performance-metrics
210210
## API Reference
211211

212212
### PerformanceMetricsCollector
213213

214214
Main class for collecting performance metrics.
215215

216-
#### Constructor
216+
Provides following interfaces:
217+
218+
- DefaultNetworkPresets - network presets object
219+
- initialize method
220+
- cleanup method
221+
- collectMetrics method
222+
223+
**initialize**
217224

218225
```typescript
219-
constructor(page: Page)
226+
/**
227+
* Initialize the collector with optional network conditions.
228+
* @param page - Playwright Page instance to collect metrics from.
229+
* @param networkConditions - Network conditions to emulate.
230+
* @throws Error if CDP session creation fails.
231+
*/
220232
```
221233

222-
#### Methods
223-
224-
- ##### collectMetrics(options?: PerformanceOptions): Promise<PerformanceMetrics>
234+
**collectMetrics**
225235

226236
```typescript
227237
/**
228238
* Collect performance metrics from the page.
239+
* @param page - Playwright page instance
229240
* @param options - Options for metric collection.
230241
* @returns Collected performance metrics.
231242
*/
232243
```
233-
234-
- ##### options:
244+
options:
245+
235246
```typescript
236247
/** Maximum time to wait for metrics collection (ms) */
237248
timeout?: number
238249
/** Maximum time to retry collecting metrics (ms) */
239250
retryTimeout?: number
240-
/** Network emulation settings */
241-
networkConditions?: NetworkConditions
242251
```
243252

244253
### Network Presets
245254

246255
Available network presets:
247256
- `REGULAR_4G`
248257
- `SLOW_3G`
258+
- `FAST_3G`
249259
- `FAST_WIFI`
250260

251261
## Contributing

dist/performance-metrics.d.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,47 @@ export declare const DefaultNetworkPresets: NetworkPresets
88
* Collector for gathering web performance metrics using Playwright.
99
*/
1010
export declare class PerformanceMetricsCollector implements IPerformanceMetricsCollector {
11-
private page
1211
private cdpSession
1312
/**
1413
* Create a new PerformanceMetricsCollector.
15-
* @param page - Playwright Page instance to collect metrics from.
1614
*/
17-
constructor(page: Page)
15+
constructor()
1816
/**
1917
* Initialize the collector with optional network conditions.
18+
* @param page - Playwright Page instance to collect metrics from.
2019
* @param networkConditions - Network conditions to emulate.
2120
* @throws Error if CDP session creation fails.
21+
* @example
22+
* const collector = new PerformanceMetricsCollector()
23+
* await collector.initialize(page, DefaultNetworkPresets.SLOW_3G)
24+
* await page.goto('')
25+
* const slowNetworkMetrics = await collector.collectMetrics(page, {
26+
* timeout: 30000
27+
* })
28+
* @example
29+
* const collector = new PerformanceMetricsCollector()
2230
*/
23-
initialize(networkConditions?: NetworkConditions): Promise<void>
31+
initialize(page: Page, networkConditions?: NetworkConditions): Promise<void>
2432
/**
25-
* Clean up collector resources.
33+
* Closes cdp session.
2634
*/
2735
cleanup(): Promise<void>
2836
/**
2937
* Collect performance metrics from the page.
38+
* @param page - Playwright Page instance to collect metrics from.
3039
* @param options - Options for metric collection.
3140
* @returns Collected performance metrics.
41+
* @example
42+
* const collector = new PerformanceMetricsCollector()
43+
* await page.goto('')
44+
* const metrics = await collector.collectMetrics(page)
45+
* expect(metrics.paint?.firstContentfulPaint).toBeLessThan(2000)
46+
* expect(metrics.largestContentfulPaint).toBeLessThan(2500)
47+
* expect(metrics.cumulativeLayoutShift).toBeLessThan(0.1)
48+
* expect(metrics.totalBlockingTime).toBeLessThan(500)
49+
* expect(metrics.timeToFirstByte?.total).toBeLessThan(900)
3250
*/
33-
collectMetrics(options?: PerformanceOptions): Promise<PerformanceMetrics>
51+
collectMetrics(page: Page, options?: PerformanceOptions): Promise<PerformanceMetrics>
3452
/**
3553
* Validate collected metrics to ensure completeness.
3654
* @param results - Collected performance metrics.

dist/performance-metrics.js

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ exports.DefaultNetworkPresets = {
1717
uploadThroughput: (500 * 1024) / 8, // 500 Kbps
1818
latency: 100 // ms
1919
},
20+
FAST_3G: {
21+
offline: false,
22+
downloadThroughput: (1.5 * 1024 * 1024) / 8, // 1.5 Mbps
23+
uploadThroughput: (750 * 1024) / 8, // 750 Kbps
24+
latency: 150
25+
},
2026
FAST_WIFI: {
2127
offline: false,
2228
downloadThroughput: (400 * 1024 * 1024) / 8, // 400 Mbps
@@ -30,26 +36,33 @@ exports.DefaultNetworkPresets = {
3036
class PerformanceMetricsCollector {
3137
/**
3238
* Create a new PerformanceMetricsCollector.
33-
* @param page - Playwright Page instance to collect metrics from.
3439
*/
35-
constructor(page) {
36-
this.page = page
37-
}
40+
constructor() {}
3841
/**
3942
* Initialize the collector with optional network conditions.
43+
* @param page - Playwright Page instance to collect metrics from.
4044
* @param networkConditions - Network conditions to emulate.
4145
* @throws Error if CDP session creation fails.
46+
* @example
47+
* const collector = new PerformanceMetricsCollector()
48+
* await collector.initialize(page, DefaultNetworkPresets.SLOW_3G)
49+
* await page.goto('')
50+
* const slowNetworkMetrics = await collector.collectMetrics(page, {
51+
* timeout: 30000
52+
* })
53+
* @example
54+
* const collector = new PerformanceMetricsCollector()
4255
*/
43-
async initialize(networkConditions) {
56+
async initialize(page, networkConditions) {
4457
if (networkConditions) {
45-
const context = this.page.context()
46-
this.cdpSession = await context.newCDPSession(this.page)
58+
const context = page.context()
59+
this.cdpSession = await context.newCDPSession(page)
4760
await this.cdpSession.send('Network.enable')
4861
await this.cdpSession.send('Network.emulateNetworkConditions', networkConditions)
4962
}
5063
}
5164
/**
52-
* Clean up collector resources.
65+
* Closes cdp session.
5366
*/
5467
async cleanup() {
5568
if (this.cdpSession) {
@@ -58,20 +71,26 @@ class PerformanceMetricsCollector {
5871
}
5972
/**
6073
* Collect performance metrics from the page.
74+
* @param page - Playwright Page instance to collect metrics from.
6175
* @param options - Options for metric collection.
6276
* @returns Collected performance metrics.
77+
* @example
78+
* const collector = new PerformanceMetricsCollector()
79+
* await page.goto('')
80+
* const metrics = await collector.collectMetrics(page)
81+
* expect(metrics.paint?.firstContentfulPaint).toBeLessThan(2000)
82+
* expect(metrics.largestContentfulPaint).toBeLessThan(2500)
83+
* expect(metrics.cumulativeLayoutShift).toBeLessThan(0.1)
84+
* expect(metrics.totalBlockingTime).toBeLessThan(500)
85+
* expect(metrics.timeToFirstByte?.total).toBeLessThan(900)
6386
*/
64-
async collectMetrics(options = {}) {
65-
const { timeout = 10000, retryTimeout = 5000, networkConditions } = options
66-
// Initialize network conditions if provided
67-
if (networkConditions) {
68-
await this.initialize(networkConditions)
69-
}
87+
async collectMetrics(page, options = {}) {
88+
const { timeout = 10000, retryTimeout = 5000 } = options
7089
// Wait for page to load
71-
await this.page.waitForLoadState('networkidle', { timeout })
90+
await page.waitForLoadState('networkidle', { timeout })
7291
const results = {}
7392
// Collect navigation timing metrics
74-
const navigationTiming = await this.page.evaluate(() => {
93+
const navigationTiming = await page.evaluate(() => {
7594
const timing = performance.getEntriesByType('navigation')[0]
7695
if (timing) {
7796
return {
@@ -96,17 +115,17 @@ class PerformanceMetricsCollector {
96115
}
97116
// Collect resource timing data
98117
results.resourceTiming = resource => {
99-
return this.page.evaluate(resourceName => {
118+
return page.evaluate(resourceName => {
100119
return performance.getEntriesByType('resource').find(entry => entry.name.includes(resourceName))
101120
}, resource)
102121
}
103122
// Calculate total bytes
104-
results.totalBytes = await this.page.evaluate(() => {
123+
results.totalBytes = await page.evaluate(() => {
105124
/// @ts-ignore
106125
return performance.getEntriesByType('resource').reduce((acc, entry) => acc + entry.encodedBodySize, 0)
107126
})
108127
// Collect performance observer metrics
109-
const observerMetrics = await this.page.evaluate(
128+
const observerMetrics = await page.evaluate(
110129
({ timeout }) => {
111130
return new Promise(resolve => {
112131
const metrics = {}
@@ -169,11 +188,7 @@ class PerformanceMetricsCollector {
169188
if (this.hasValidMetrics(results)) {
170189
break
171190
}
172-
await this.page.waitForTimeout(100)
173-
}
174-
// Cleanup if we initialized network conditions
175-
if (networkConditions) {
176-
await this.cleanup()
191+
await page.waitForTimeout(100)
177192
}
178193
return results
179194
}

0 commit comments

Comments
 (0)