playwright_rs/protocol/coverage.rs
1// Coverage — Chromium-only code coverage collection
2//
3// See: https://playwright.dev/docs/api/class-coverage
4
5use crate::error::Result;
6use crate::protocol::page::Page;
7
8/// Options for `Coverage::start_js_coverage`.
9///
10/// See: <https://playwright.dev/docs/api/class-coverage#coverage-start-js-coverage>
11#[derive(Debug, Default, Clone)]
12#[non_exhaustive]
13pub struct StartJSCoverageOptions {
14 /// Whether to reset coverage on every navigation.
15 ///
16 /// Defaults to `true`.
17 pub reset_on_navigation: Option<bool>,
18
19 /// Whether to report anonymous scripts generated by the page.
20 ///
21 /// Defaults to `false`.
22 pub report_anonymous_scripts: Option<bool>,
23}
24
25impl StartJSCoverageOptions {
26 /// Whether to reset coverage on every navigation. Defaults to `true`.
27 pub fn reset_on_navigation(mut self, reset_on_navigation: bool) -> Self {
28 self.reset_on_navigation = Some(reset_on_navigation);
29 self
30 }
31
32 /// Whether to report anonymous scripts generated by the page. Defaults
33 /// to `false`.
34 pub fn report_anonymous_scripts(mut self, report_anonymous_scripts: bool) -> Self {
35 self.report_anonymous_scripts = Some(report_anonymous_scripts);
36 self
37 }
38}
39
40/// Options for `Coverage::start_css_coverage`.
41///
42/// See: <https://playwright.dev/docs/api/class-coverage#coverage-start-css-coverage>
43#[derive(Debug, Default, Clone)]
44#[non_exhaustive]
45pub struct StartCSSCoverageOptions {
46 /// Whether to reset coverage on every navigation.
47 ///
48 /// Defaults to `true`.
49 pub reset_on_navigation: Option<bool>,
50}
51
52impl StartCSSCoverageOptions {
53 /// Whether to reset coverage on every navigation. Defaults to `true`.
54 pub fn reset_on_navigation(mut self, reset_on_navigation: bool) -> Self {
55 self.reset_on_navigation = Some(reset_on_navigation);
56 self
57 }
58}
59
60/// A byte-offset range within a CSS stylesheet.
61///
62/// Used in [`CSSCoverageEntry`].
63#[derive(Debug, Clone, serde::Deserialize)]
64#[non_exhaustive]
65pub struct CoverageRange {
66 /// Start byte offset (inclusive).
67 pub start: usize,
68 /// End byte offset (exclusive).
69 pub end: usize,
70}
71
72/// A byte-offset range with hit count within a JavaScript function.
73///
74/// Used in [`JSFunctionCoverage`].
75#[derive(Debug, Clone, serde::Deserialize)]
76#[non_exhaustive]
77pub struct JSCoverageRange {
78 /// Start byte offset (inclusive).
79 #[serde(rename = "startOffset")]
80 pub start_offset: usize,
81 /// End byte offset (exclusive).
82 #[serde(rename = "endOffset")]
83 pub end_offset: usize,
84 /// Number of times this range was executed.
85 pub count: i64,
86}
87
88/// Per-function coverage data within a [`JSCoverageEntry`].
89#[derive(Debug, Clone, serde::Deserialize)]
90#[non_exhaustive]
91pub struct JSFunctionCoverage {
92 /// The function name (empty string for anonymous functions).
93 #[serde(rename = "functionName")]
94 pub function_name: String,
95 /// Whether this is block-level coverage (true) or function-level (false).
96 #[serde(rename = "isBlockCoverage")]
97 pub is_block_coverage: bool,
98 /// Covered byte-offset ranges within this function.
99 pub ranges: Vec<JSCoverageRange>,
100}
101
102/// A JavaScript coverage entry returned by `Coverage::stop_js_coverage`.
103///
104/// See: <https://playwright.dev/docs/api/class-coverage#coverage-stop-js-coverage>
105#[derive(Debug, Clone, serde::Deserialize)]
106#[non_exhaustive]
107pub struct JSCoverageEntry {
108 /// The URL of the script.
109 pub url: String,
110 /// The V8 script ID.
111 #[serde(rename = "scriptId")]
112 pub script_id: String,
113 /// The script source text.
114 pub source: Option<String>,
115 /// Per-function coverage data.
116 pub functions: Vec<JSFunctionCoverage>,
117}
118
119/// A CSS coverage entry returned by `Coverage::stop_css_coverage`.
120///
121/// See: <https://playwright.dev/docs/api/class-coverage#coverage-stop-css-coverage>
122#[derive(Debug, Clone, serde::Deserialize)]
123#[non_exhaustive]
124pub struct CSSCoverageEntry {
125 /// The URL of the stylesheet.
126 pub url: String,
127 /// The stylesheet source text.
128 pub text: Option<String>,
129 /// Byte-offset ranges of used CSS rules.
130 pub ranges: Vec<CoverageRange>,
131}
132
133/// Provides JavaScript and CSS code coverage collection (Chromium only).
134///
135/// Access via [`Page::coverage`].
136///
137/// Coverage collection is only supported in Chromium. Calling these methods on
138/// Firefox or WebKit will return an error.
139///
140/// See: <https://playwright.dev/docs/api/class-coverage>
141#[derive(Clone)]
142pub struct Coverage {
143 page: Page,
144}
145
146impl Coverage {
147 pub(crate) fn new(page: Page) -> Self {
148 Self { page }
149 }
150
151 /// Starts collecting JavaScript coverage.
152 ///
153 /// Must be called before navigating to the page(s) you want to measure.
154 ///
155 /// # Errors
156 ///
157 /// Returns error if coverage is already started, if the browser is not
158 /// Chromium, or if the RPC call fails.
159 ///
160 /// See: <https://playwright.dev/docs/api/class-coverage#coverage-start-js-coverage>
161 pub async fn start_js_coverage(
162 &self,
163 options: impl Into<Option<StartJSCoverageOptions>>,
164 ) -> Result<()> {
165 let options = options.into();
166 self.page.coverage_start_js(options).await
167 }
168
169 /// Stops JavaScript coverage collection and returns the entries.
170 ///
171 /// # Errors
172 ///
173 /// Returns error if coverage was not started or if the RPC call fails.
174 ///
175 /// See: <https://playwright.dev/docs/api/class-coverage#coverage-stop-js-coverage>
176 pub async fn stop_js_coverage(&self) -> Result<Vec<JSCoverageEntry>> {
177 self.page.coverage_stop_js().await
178 }
179
180 /// Starts collecting CSS coverage.
181 ///
182 /// # Errors
183 ///
184 /// Returns error if coverage is already started, if the browser is not
185 /// Chromium, or if the RPC call fails.
186 ///
187 /// See: <https://playwright.dev/docs/api/class-coverage#coverage-start-css-coverage>
188 pub async fn start_css_coverage(
189 &self,
190 options: impl Into<Option<StartCSSCoverageOptions>>,
191 ) -> Result<()> {
192 let options = options.into();
193 self.page.coverage_start_css(options).await
194 }
195
196 /// Stops CSS coverage collection and returns the entries.
197 ///
198 /// # Errors
199 ///
200 /// Returns error if coverage was not started or if the RPC call fails.
201 ///
202 /// See: <https://playwright.dev/docs/api/class-coverage#coverage-stop-css-coverage>
203 pub async fn stop_css_coverage(&self) -> Result<Vec<CSSCoverageEntry>> {
204 self.page.coverage_stop_css().await
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 #[test]
213 fn js_coverage_setters_write_their_own_fields() {
214 let opts = StartJSCoverageOptions::default()
215 .reset_on_navigation(false)
216 .report_anonymous_scripts(true);
217 assert_eq!(opts.reset_on_navigation, Some(false));
218 assert_eq!(opts.report_anonymous_scripts, Some(true));
219 }
220
221 #[test]
222 fn css_coverage_setter_writes_the_field() {
223 let opts = StartCSSCoverageOptions::default().reset_on_navigation(false);
224 assert_eq!(opts.reset_on_navigation, Some(false));
225 }
226}