Skip to main content

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}