1use crate::result::{ProbarError, ProbarResult};
14use serde::{Deserialize, Serialize};
15use std::collections::HashMap;
16use std::sync::{Arc, Mutex};
17use std::time::Instant;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
21pub enum ContextState {
22 Creating,
24 Ready,
26 InUse,
28 Cleaning,
30 Closed,
32 Error,
34}
35
36#[derive(Debug, Clone, Default, Serialize, Deserialize)]
38pub struct StorageState {
39 pub cookies: Vec<Cookie>,
41 pub local_storage: HashMap<String, HashMap<String, String>>,
43 pub session_storage: HashMap<String, HashMap<String, String>>,
45}
46
47impl StorageState {
48 #[must_use]
50 pub fn new() -> Self {
51 Self::default()
52 }
53
54 #[must_use]
56 pub fn with_cookie(mut self, cookie: Cookie) -> Self {
57 self.cookies.push(cookie);
58 self
59 }
60
61 #[must_use]
63 pub fn with_local_storage(mut self, origin: &str, key: &str, value: &str) -> Self {
64 self.local_storage
65 .entry(origin.to_string())
66 .or_default()
67 .insert(key.to_string(), value.to_string());
68 self
69 }
70
71 #[must_use]
73 pub fn with_session_storage(mut self, origin: &str, key: &str, value: &str) -> Self {
74 self.session_storage
75 .entry(origin.to_string())
76 .or_default()
77 .insert(key.to_string(), value.to_string());
78 self
79 }
80
81 #[must_use]
83 pub fn is_empty(&self) -> bool {
84 self.cookies.is_empty() && self.local_storage.is_empty() && self.session_storage.is_empty()
85 }
86
87 pub fn clear(&mut self) {
89 self.cookies.clear();
90 self.local_storage.clear();
91 self.session_storage.clear();
92 }
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct Cookie {
98 pub name: String,
100 pub value: String,
102 pub domain: String,
104 pub path: String,
106 pub expires: Option<i64>,
108 pub http_only: bool,
110 pub secure: bool,
112 pub same_site: SameSite,
114}
115
116impl Cookie {
117 #[must_use]
119 pub fn new(name: &str, value: &str, domain: &str) -> Self {
120 Self {
121 name: name.to_string(),
122 value: value.to_string(),
123 domain: domain.to_string(),
124 path: "/".to_string(),
125 expires: None,
126 http_only: false,
127 secure: false,
128 same_site: SameSite::Lax,
129 }
130 }
131
132 #[must_use]
134 pub fn with_path(mut self, path: &str) -> Self {
135 self.path = path.to_string();
136 self
137 }
138
139 #[must_use]
141 pub const fn with_expires(mut self, expires: i64) -> Self {
142 self.expires = Some(expires);
143 self
144 }
145
146 #[must_use]
148 pub const fn http_only(mut self) -> Self {
149 self.http_only = true;
150 self
151 }
152
153 #[must_use]
155 pub const fn secure(mut self) -> Self {
156 self.secure = true;
157 self
158 }
159
160 #[must_use]
162 pub const fn with_same_site(mut self, same_site: SameSite) -> Self {
163 self.same_site = same_site;
164 self
165 }
166}
167
168#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
170pub enum SameSite {
171 Strict,
173 Lax,
175 None,
177}
178
179#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct ContextConfig {
182 pub name: String,
184 pub viewport_width: u32,
186 pub viewport_height: u32,
188 pub device_scale_factor: f64,
190 pub is_mobile: bool,
192 pub has_touch: bool,
194 pub user_agent: Option<String>,
196 pub locale: Option<String>,
198 pub timezone: Option<String>,
200 pub geolocation: Option<Geolocation>,
202 pub permissions: Vec<String>,
204 pub extra_headers: HashMap<String, String>,
206 pub offline: bool,
208 pub storage_state: Option<StorageState>,
210 pub accept_downloads: bool,
212 pub record_video: bool,
214 pub record_har: bool,
216 pub ignore_https_errors: bool,
218}
219
220impl Default for ContextConfig {
221 fn default() -> Self {
222 Self {
223 name: String::new(),
224 viewport_width: 1280,
225 viewport_height: 720,
226 device_scale_factor: 1.0,
227 is_mobile: false,
228 has_touch: false,
229 user_agent: None,
230 locale: None,
231 timezone: None,
232 geolocation: None,
233 permissions: Vec::new(),
234 extra_headers: HashMap::new(),
235 offline: false,
236 storage_state: None,
237 accept_downloads: false,
238 record_video: false,
239 record_har: false,
240 ignore_https_errors: false,
241 }
242 }
243}
244
245impl ContextConfig {
246 #[must_use]
248 pub fn new(name: &str) -> Self {
249 Self {
250 name: name.to_string(),
251 ..Self::default()
252 }
253 }
254
255 #[must_use]
257 pub const fn with_viewport(mut self, width: u32, height: u32) -> Self {
258 self.viewport_width = width;
259 self.viewport_height = height;
260 self
261 }
262
263 #[must_use]
265 pub const fn with_device_scale(mut self, scale: f64) -> Self {
266 self.device_scale_factor = scale;
267 self
268 }
269
270 #[must_use]
272 pub const fn mobile(mut self) -> Self {
273 self.is_mobile = true;
274 self.has_touch = true;
275 self
276 }
277
278 #[must_use]
280 pub fn with_user_agent(mut self, user_agent: &str) -> Self {
281 self.user_agent = Some(user_agent.to_string());
282 self
283 }
284
285 #[must_use]
287 pub fn with_locale(mut self, locale: &str) -> Self {
288 self.locale = Some(locale.to_string());
289 self
290 }
291
292 #[must_use]
294 pub fn with_timezone(mut self, timezone: &str) -> Self {
295 self.timezone = Some(timezone.to_string());
296 self
297 }
298
299 #[must_use]
301 pub fn with_geolocation(mut self, lat: f64, lng: f64) -> Self {
302 self.geolocation = Some(Geolocation {
303 latitude: lat,
304 longitude: lng,
305 accuracy: 10.0,
306 });
307 self
308 }
309
310 #[must_use]
312 pub fn with_permission(mut self, permission: &str) -> Self {
313 self.permissions.push(permission.to_string());
314 self
315 }
316
317 #[must_use]
319 pub fn with_header(mut self, key: &str, value: &str) -> Self {
320 self.extra_headers
321 .insert(key.to_string(), value.to_string());
322 self
323 }
324
325 #[must_use]
327 pub const fn offline(mut self) -> Self {
328 self.offline = true;
329 self
330 }
331
332 #[must_use]
334 pub fn with_storage_state(mut self, state: StorageState) -> Self {
335 self.storage_state = Some(state);
336 self
337 }
338
339 #[must_use]
341 pub const fn with_video(mut self) -> Self {
342 self.record_video = true;
343 self
344 }
345
346 #[must_use]
348 pub const fn with_har(mut self) -> Self {
349 self.record_har = true;
350 self
351 }
352
353 #[must_use]
355 pub const fn ignore_https_errors(mut self) -> Self {
356 self.ignore_https_errors = true;
357 self
358 }
359}
360
361#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
363pub struct Geolocation {
364 pub latitude: f64,
366 pub longitude: f64,
368 pub accuracy: f64,
370}
371
372#[derive(Debug)]
374pub struct BrowserContext {
375 pub id: String,
377 pub config: ContextConfig,
379 pub state: ContextState,
381 pub created_at: Instant,
383 pages: Arc<Mutex<Vec<String>>>,
385 storage: Arc<Mutex<StorageState>>,
387 pub error_message: Option<String>,
389}
390
391impl BrowserContext {
392 #[must_use]
394 pub fn new(id: &str, config: ContextConfig) -> Self {
395 let storage = config.storage_state.clone().unwrap_or_default();
396 Self {
397 id: id.to_string(),
398 config,
399 state: ContextState::Creating,
400 created_at: Instant::now(),
401 pages: Arc::new(Mutex::new(Vec::new())),
402 storage: Arc::new(Mutex::new(storage)),
403 error_message: None,
404 }
405 }
406
407 pub fn ready(&mut self) {
409 self.state = ContextState::Ready;
410 }
411
412 pub fn acquire(&mut self) {
414 self.state = ContextState::InUse;
415 }
416
417 pub fn release(&mut self) {
419 self.state = ContextState::Ready;
420 }
421
422 pub fn close(&mut self) {
424 self.state = ContextState::Closed;
425 }
426
427 pub fn set_error(&mut self, message: &str) {
429 self.state = ContextState::Error;
430 self.error_message = Some(message.to_string());
431 }
432
433 #[must_use]
435 pub const fn is_available(&self) -> bool {
436 matches!(self.state, ContextState::Ready)
437 }
438
439 #[must_use]
441 pub const fn is_in_use(&self) -> bool {
442 matches!(self.state, ContextState::InUse)
443 }
444
445 #[must_use]
447 pub const fn is_closed(&self) -> bool {
448 matches!(self.state, ContextState::Closed)
449 }
450
451 #[must_use]
453 pub fn age_ms(&self) -> u64 {
454 self.created_at.elapsed().as_millis() as u64
455 }
456
457 pub fn new_page(&self) -> String {
459 let page_id = format!("{}_{}", self.id, uuid::Uuid::new_v4());
460 if let Ok(mut pages) = self.pages.lock() {
461 pages.push(page_id.clone());
462 }
463 page_id
464 }
465
466 pub fn close_page(&self, page_id: &str) {
468 if let Ok(mut pages) = self.pages.lock() {
469 pages.retain(|p| p != page_id);
470 }
471 }
472
473 #[must_use]
475 pub fn page_count(&self) -> usize {
476 self.pages.lock().map(|p| p.len()).unwrap_or(0)
477 }
478
479 #[must_use]
481 pub fn storage_state(&self) -> StorageState {
482 self.storage.lock().map(|s| s.clone()).unwrap_or_default()
483 }
484
485 pub fn clear_storage(&self) {
487 if let Ok(mut storage) = self.storage.lock() {
488 storage.clear();
489 }
490 }
491
492 pub fn add_cookie(&self, cookie: Cookie) {
494 if let Ok(mut storage) = self.storage.lock() {
495 storage.cookies.push(cookie);
496 }
497 }
498
499 pub fn clear_cookies(&self) {
501 if let Ok(mut storage) = self.storage.lock() {
502 storage.cookies.clear();
503 }
504 }
505}
506
507#[derive(Debug)]
509pub struct ContextPool {
510 contexts: Arc<Mutex<HashMap<String, BrowserContext>>>,
512 max_contexts: usize,
514 default_config: ContextConfig,
516 counter: Arc<Mutex<u64>>,
518}
519
520impl Default for ContextPool {
521 fn default() -> Self {
522 Self::new(10)
523 }
524}
525
526impl ContextPool {
527 #[must_use]
529 pub fn new(max_contexts: usize) -> Self {
530 Self {
531 contexts: Arc::new(Mutex::new(HashMap::new())),
532 max_contexts,
533 default_config: ContextConfig::default(),
534 counter: Arc::new(Mutex::new(0)),
535 }
536 }
537
538 #[must_use]
540 pub fn with_default_config(mut self, config: ContextConfig) -> Self {
541 self.default_config = config;
542 self
543 }
544
545 pub fn create(&self, config: Option<ContextConfig>) -> ProbarResult<String> {
547 let mut contexts = self
548 .contexts
549 .lock()
550 .map_err(|_| ProbarError::Io(std::io::Error::other("Failed to lock contexts")))?;
551
552 if contexts.len() >= self.max_contexts {
553 return Err(ProbarError::AssertionError {
554 message: format!("Maximum contexts ({}) reached", self.max_contexts),
555 });
556 }
557
558 let id = {
559 let mut counter = self
560 .counter
561 .lock()
562 .map_err(|_| ProbarError::Io(std::io::Error::other("Failed to lock counter")))?;
563 *counter += 1;
564 format!("ctx_{}", *counter)
565 };
566
567 let mut ctx_config = config.unwrap_or_else(|| self.default_config.clone());
568 ctx_config.name = id.clone();
569
570 let mut context = BrowserContext::new(&id, ctx_config);
571 context.ready();
572
573 contexts.insert(id.clone(), context);
574 Ok(id)
575 }
576
577 pub fn acquire(&self) -> ProbarResult<String> {
579 let mut contexts = self
580 .contexts
581 .lock()
582 .map_err(|_| ProbarError::Io(std::io::Error::other("Failed to lock contexts")))?;
583
584 for (id, context) in contexts.iter_mut() {
585 if context.is_available() {
586 context.acquire();
587 return Ok(id.clone());
588 }
589 }
590
591 drop(contexts);
593 let id = self.create(None)?;
594
595 let mut contexts = self
596 .contexts
597 .lock()
598 .map_err(|_| ProbarError::Io(std::io::Error::other("Failed to lock contexts")))?;
599
600 if let Some(context) = contexts.get_mut(&id) {
601 context.acquire();
602 }
603
604 Ok(id)
605 }
606
607 pub fn release(&self, context_id: &str) -> ProbarResult<()> {
609 let mut contexts = self
610 .contexts
611 .lock()
612 .map_err(|_| ProbarError::Io(std::io::Error::other("Failed to lock contexts")))?;
613
614 if let Some(context) = contexts.get_mut(context_id) {
615 context.release();
616 Ok(())
617 } else {
618 Err(ProbarError::AssertionError {
619 message: format!("Context {} not found", context_id),
620 })
621 }
622 }
623
624 pub fn close(&self, context_id: &str) -> ProbarResult<()> {
626 let mut contexts = self
627 .contexts
628 .lock()
629 .map_err(|_| ProbarError::Io(std::io::Error::other("Failed to lock contexts")))?;
630
631 if let Some(context) = contexts.get_mut(context_id) {
632 context.close();
633 Ok(())
634 } else {
635 Err(ProbarError::AssertionError {
636 message: format!("Context {} not found", context_id),
637 })
638 }
639 }
640
641 pub fn remove(&self, context_id: &str) -> ProbarResult<()> {
643 let mut contexts = self
644 .contexts
645 .lock()
646 .map_err(|_| ProbarError::Io(std::io::Error::other("Failed to lock contexts")))?;
647
648 contexts.remove(context_id);
649 Ok(())
650 }
651
652 #[must_use]
654 pub fn count(&self) -> usize {
655 self.contexts.lock().map(|c| c.len()).unwrap_or(0)
656 }
657
658 #[must_use]
660 pub fn available_count(&self) -> usize {
661 self.contexts
662 .lock()
663 .map(|c| c.values().filter(|ctx| ctx.is_available()).count())
664 .unwrap_or(0)
665 }
666
667 #[must_use]
669 pub fn in_use_count(&self) -> usize {
670 self.contexts
671 .lock()
672 .map(|c| c.values().filter(|ctx| ctx.is_in_use()).count())
673 .unwrap_or(0)
674 }
675
676 pub fn close_all(&self) {
678 if let Ok(mut contexts) = self.contexts.lock() {
679 for context in contexts.values_mut() {
680 context.close();
681 }
682 }
683 }
684
685 pub fn clear(&self) {
687 if let Ok(mut contexts) = self.contexts.lock() {
688 contexts.clear();
689 }
690 }
691
692 #[must_use]
694 pub fn context_ids(&self) -> Vec<String> {
695 self.contexts
696 .lock()
697 .map(|c| c.keys().cloned().collect())
698 .unwrap_or_default()
699 }
700}
701
702#[derive(Debug)]
704pub struct ContextManager {
705 pool: ContextPool,
707 active_contexts: Arc<Mutex<HashMap<String, String>>>,
709}
710
711impl Default for ContextManager {
712 fn default() -> Self {
713 Self::new()
714 }
715}
716
717impl ContextManager {
718 #[must_use]
720 pub fn new() -> Self {
721 Self {
722 pool: ContextPool::new(10),
723 active_contexts: Arc::new(Mutex::new(HashMap::new())),
724 }
725 }
726
727 #[must_use]
729 pub fn with_pool_size(pool_size: usize) -> Self {
730 Self {
731 pool: ContextPool::new(pool_size),
732 active_contexts: Arc::new(Mutex::new(HashMap::new())),
733 }
734 }
735
736 pub fn get_context(&self, test_id: &str) -> ProbarResult<String> {
738 let mut active = self.active_contexts.lock().map_err(|_| {
739 ProbarError::Io(std::io::Error::other("Failed to lock active contexts"))
740 })?;
741
742 if let Some(context_id) = active.get(test_id) {
744 return Ok(context_id.clone());
745 }
746
747 let context_id = self.pool.acquire()?;
749 let _ = active.insert(test_id.to_string(), context_id.clone());
750 Ok(context_id)
751 }
752
753 pub fn release_context(&self, test_id: &str) -> ProbarResult<()> {
755 let mut active = self.active_contexts.lock().map_err(|_| {
756 ProbarError::Io(std::io::Error::other("Failed to lock active contexts"))
757 })?;
758
759 if let Some(context_id) = active.remove(test_id) {
760 self.pool.release(&context_id)?;
761 }
762 Ok(())
763 }
764
765 pub fn create_isolated_context(
767 &self,
768 test_id: &str,
769 config: ContextConfig,
770 ) -> ProbarResult<String> {
771 let context_id = self.pool.create(Some(config))?;
772
773 let mut active = self.active_contexts.lock().map_err(|_| {
774 ProbarError::Io(std::io::Error::other("Failed to lock active contexts"))
775 })?;
776
777 if let Some(old_id) = active.get(test_id) {
779 let _ = self.pool.release(old_id);
780 }
781
782 let _ = active.insert(test_id.to_string(), context_id.clone());
783 Ok(context_id)
784 }
785
786 #[must_use]
788 pub fn stats(&self) -> ContextPoolStats {
789 ContextPoolStats {
790 total: self.pool.count(),
791 available: self.pool.available_count(),
792 in_use: self.pool.in_use_count(),
793 active_tests: self.active_contexts.lock().map(|a| a.len()).unwrap_or(0),
794 }
795 }
796
797 pub fn cleanup(&self) {
799 self.pool.close_all();
800 self.pool.clear();
801 if let Ok(mut active) = self.active_contexts.lock() {
802 active.clear();
803 }
804 }
805}
806
807#[derive(Debug, Clone, Serialize, Deserialize)]
809pub struct ContextPoolStats {
810 pub total: usize,
812 pub available: usize,
814 pub in_use: usize,
816 pub active_tests: usize,
818}
819
820#[cfg(test)]
821#[allow(clippy::unwrap_used, clippy::expect_used)]
822mod tests {
823 use super::*;
824
825 mod storage_state_tests {
826 use super::*;
827
828 #[test]
829 fn test_new() {
830 let state = StorageState::new();
831 assert!(state.is_empty());
832 }
833
834 #[test]
835 fn test_with_cookie() {
836 let state =
837 StorageState::new().with_cookie(Cookie::new("session", "abc123", "example.com"));
838 assert_eq!(state.cookies.len(), 1);
839 }
840
841 #[test]
842 fn test_with_local_storage() {
843 let state =
844 StorageState::new().with_local_storage("https://example.com", "key", "value");
845 assert!(!state.local_storage.is_empty());
846 }
847
848 #[test]
849 fn test_clear() {
850 let mut state =
851 StorageState::new().with_cookie(Cookie::new("session", "abc123", "example.com"));
852 state.clear();
853 assert!(state.is_empty());
854 }
855 }
856
857 mod cookie_tests {
858 use super::*;
859
860 #[test]
861 fn test_new() {
862 let cookie = Cookie::new("name", "value", "example.com");
863 assert_eq!(cookie.name, "name");
864 assert_eq!(cookie.value, "value");
865 assert_eq!(cookie.domain, "example.com");
866 assert_eq!(cookie.path, "/");
867 }
868
869 #[test]
870 fn test_with_path() {
871 let cookie = Cookie::new("name", "value", "example.com").with_path("/api");
872 assert_eq!(cookie.path, "/api");
873 }
874
875 #[test]
876 fn test_secure_http_only() {
877 let cookie = Cookie::new("name", "value", "example.com")
878 .secure()
879 .http_only();
880 assert!(cookie.secure);
881 assert!(cookie.http_only);
882 }
883
884 #[test]
885 fn test_same_site() {
886 let cookie =
887 Cookie::new("name", "value", "example.com").with_same_site(SameSite::Strict);
888 assert!(matches!(cookie.same_site, SameSite::Strict));
889 }
890 }
891
892 mod context_config_tests {
893 use super::*;
894
895 #[test]
896 fn test_new() {
897 let config = ContextConfig::new("test");
898 assert_eq!(config.name, "test");
899 assert_eq!(config.viewport_width, 1280);
900 assert_eq!(config.viewport_height, 720);
901 }
902
903 #[test]
904 fn test_with_viewport() {
905 let config = ContextConfig::new("test").with_viewport(1920, 1080);
906 assert_eq!(config.viewport_width, 1920);
907 assert_eq!(config.viewport_height, 1080);
908 }
909
910 #[test]
911 fn test_mobile() {
912 let config = ContextConfig::new("test").mobile();
913 assert!(config.is_mobile);
914 assert!(config.has_touch);
915 }
916
917 #[test]
918 fn test_with_geolocation() {
919 let config = ContextConfig::new("test").with_geolocation(37.7749, -122.4194);
920 assert!(config.geolocation.is_some());
921 let geo = config.geolocation.unwrap();
922 assert!((geo.latitude - 37.7749).abs() < f64::EPSILON);
923 }
924
925 #[test]
926 fn test_with_header() {
927 let config = ContextConfig::new("test").with_header("Authorization", "Bearer token");
928 assert_eq!(
929 config.extra_headers.get("Authorization"),
930 Some(&"Bearer token".to_string())
931 );
932 }
933
934 #[test]
935 fn test_offline() {
936 let config = ContextConfig::new("test").offline();
937 assert!(config.offline);
938 }
939 }
940
941 mod browser_context_tests {
942 use super::*;
943
944 #[test]
945 fn test_new() {
946 let config = ContextConfig::new("test");
947 let context = BrowserContext::new("ctx_1", config);
948 assert_eq!(context.id, "ctx_1");
949 assert!(matches!(context.state, ContextState::Creating));
950 }
951
952 #[test]
953 fn test_state_transitions() {
954 let config = ContextConfig::new("test");
955 let mut context = BrowserContext::new("ctx_1", config);
956
957 context.ready();
958 assert!(context.is_available());
959
960 context.acquire();
961 assert!(context.is_in_use());
962
963 context.release();
964 assert!(context.is_available());
965
966 context.close();
967 assert!(context.is_closed());
968 }
969
970 #[test]
971 fn test_error_state() {
972 let config = ContextConfig::new("test");
973 let mut context = BrowserContext::new("ctx_1", config);
974
975 context.set_error("Connection failed");
976 assert!(matches!(context.state, ContextState::Error));
977 assert_eq!(context.error_message, Some("Connection failed".to_string()));
978 }
979
980 #[test]
981 fn test_pages() {
982 let config = ContextConfig::new("test");
983 let context = BrowserContext::new("ctx_1", config);
984
985 let page1 = context.new_page();
986 let page2 = context.new_page();
987 assert_eq!(context.page_count(), 2);
988
989 context.close_page(&page1);
990 assert_eq!(context.page_count(), 1);
991
992 context.close_page(&page2);
993 assert_eq!(context.page_count(), 0);
994 }
995
996 #[test]
997 fn test_storage() {
998 let config = ContextConfig::new("test");
999 let context = BrowserContext::new("ctx_1", config);
1000
1001 context.add_cookie(Cookie::new("session", "abc", "example.com"));
1002 let storage = context.storage_state();
1003 assert_eq!(storage.cookies.len(), 1);
1004
1005 context.clear_cookies();
1006 let storage = context.storage_state();
1007 assert!(storage.cookies.is_empty());
1008 }
1009 }
1010
1011 mod context_pool_tests {
1012 use super::*;
1013
1014 #[test]
1015 fn test_new() {
1016 let pool = ContextPool::new(5);
1017 assert_eq!(pool.count(), 0);
1018 }
1019
1020 #[test]
1021 fn test_create() {
1022 let pool = ContextPool::new(5);
1023 let id = pool.create(None).unwrap();
1024 assert!(!id.is_empty());
1025 assert_eq!(pool.count(), 1);
1026 }
1027
1028 #[test]
1029 fn test_max_contexts() {
1030 let pool = ContextPool::new(2);
1031 pool.create(None).unwrap();
1032 pool.create(None).unwrap();
1033
1034 let result = pool.create(None);
1035 assert!(result.is_err());
1036 }
1037
1038 #[test]
1039 fn test_acquire_release() {
1040 let pool = ContextPool::new(5);
1041 let id = pool.acquire().unwrap();
1042
1043 assert_eq!(pool.in_use_count(), 1);
1044 assert_eq!(pool.available_count(), 0);
1045
1046 pool.release(&id).unwrap();
1047 assert_eq!(pool.in_use_count(), 0);
1048 assert_eq!(pool.available_count(), 1);
1049 }
1050
1051 #[test]
1052 fn test_close() {
1053 let pool = ContextPool::new(5);
1054 let id = pool.create(None).unwrap();
1055
1056 pool.close(&id).unwrap();
1057 assert_eq!(pool.available_count(), 0);
1058 }
1059
1060 #[test]
1061 fn test_close_all() {
1062 let pool = ContextPool::new(5);
1063 pool.create(None).unwrap();
1064 pool.create(None).unwrap();
1065
1066 pool.close_all();
1067 assert_eq!(pool.available_count(), 0);
1068 }
1069
1070 #[test]
1071 fn test_clear() {
1072 let pool = ContextPool::new(5);
1073 pool.create(None).unwrap();
1074 pool.clear();
1075 assert_eq!(pool.count(), 0);
1076 }
1077 }
1078
1079 mod context_manager_tests {
1080 use super::*;
1081
1082 #[test]
1083 fn test_new() {
1084 let manager = ContextManager::new();
1085 let stats = manager.stats();
1086 assert_eq!(stats.total, 0);
1087 }
1088
1089 #[test]
1090 fn test_get_context() {
1091 let manager = ContextManager::new();
1092 let ctx_id = manager.get_context("test_1").unwrap();
1093 assert!(!ctx_id.is_empty());
1094
1095 let ctx_id2 = manager.get_context("test_1").unwrap();
1097 assert_eq!(ctx_id, ctx_id2);
1098
1099 let ctx_id3 = manager.get_context("test_2").unwrap();
1101 assert_ne!(ctx_id, ctx_id3);
1102 }
1103
1104 #[test]
1105 fn test_release_context() {
1106 let manager = ContextManager::new();
1107 let _ctx_id = manager.get_context("test_1").unwrap();
1108
1109 manager.release_context("test_1").unwrap();
1110 let stats = manager.stats();
1111 assert_eq!(stats.available, 1);
1112 assert_eq!(stats.in_use, 0);
1113 }
1114
1115 #[test]
1116 fn test_create_isolated_context() {
1117 let manager = ContextManager::new();
1118 let config = ContextConfig::new("isolated")
1119 .mobile()
1120 .with_viewport(375, 812);
1121
1122 let ctx_id = manager.create_isolated_context("test_1", config).unwrap();
1123 assert!(!ctx_id.is_empty());
1124 }
1125
1126 #[test]
1127 fn test_cleanup() {
1128 let manager = ContextManager::new();
1129 manager.get_context("test_1").unwrap();
1130 manager.get_context("test_2").unwrap();
1131
1132 manager.cleanup();
1133 let stats = manager.stats();
1134 assert_eq!(stats.total, 0);
1135 assert_eq!(stats.active_tests, 0);
1136 }
1137
1138 #[test]
1139 fn test_stats() {
1140 let manager = ContextManager::new();
1141 manager.get_context("test_1").unwrap();
1142 manager.get_context("test_2").unwrap();
1143
1144 let stats = manager.stats();
1145 assert_eq!(stats.total, 2);
1146 assert_eq!(stats.in_use, 2);
1147 assert_eq!(stats.active_tests, 2);
1148 }
1149 }
1150
1151 mod context_config_additional_tests {
1152 use super::*;
1153
1154 #[test]
1155 fn test_with_device_scale() {
1156 let config = ContextConfig::new("test").with_device_scale(2.0);
1157 assert!((config.device_scale_factor - 2.0).abs() < f64::EPSILON);
1158 }
1159
1160 #[test]
1161 fn test_with_user_agent() {
1162 let config = ContextConfig::new("test").with_user_agent("Custom UA");
1163 assert_eq!(config.user_agent, Some("Custom UA".to_string()));
1164 }
1165
1166 #[test]
1167 fn test_with_locale() {
1168 let config = ContextConfig::new("test").with_locale("en-US");
1169 assert_eq!(config.locale, Some("en-US".to_string()));
1170 }
1171
1172 #[test]
1173 fn test_with_timezone() {
1174 let config = ContextConfig::new("test").with_timezone("America/New_York");
1175 assert_eq!(config.timezone, Some("America/New_York".to_string()));
1176 }
1177
1178 #[test]
1179 fn test_with_permission() {
1180 let config = ContextConfig::new("test")
1181 .with_permission("geolocation")
1182 .with_permission("notifications");
1183 assert_eq!(config.permissions.len(), 2);
1184 assert!(config.permissions.contains(&"geolocation".to_string()));
1185 }
1186
1187 #[test]
1188 fn test_with_storage_state() {
1189 let storage =
1190 StorageState::new().with_cookie(Cookie::new("session", "abc", "example.com"));
1191 let config = ContextConfig::new("test").with_storage_state(storage);
1192 assert!(config.storage_state.is_some());
1193 assert_eq!(config.storage_state.unwrap().cookies.len(), 1);
1194 }
1195
1196 #[test]
1197 fn test_with_video() {
1198 let config = ContextConfig::new("test").with_video();
1199 assert!(config.record_video);
1200 }
1201
1202 #[test]
1203 fn test_with_har() {
1204 let config = ContextConfig::new("test").with_har();
1205 assert!(config.record_har);
1206 }
1207
1208 #[test]
1209 fn test_ignore_https_errors() {
1210 let config = ContextConfig::new("test").ignore_https_errors();
1211 assert!(config.ignore_https_errors);
1212 }
1213
1214 #[test]
1215 fn test_config_default() {
1216 let config = ContextConfig::default();
1217 assert!(config.name.is_empty());
1218 assert_eq!(config.viewport_width, 1280);
1219 assert!(!config.is_mobile);
1220 assert!(!config.offline);
1221 }
1222 }
1223
1224 mod storage_state_additional_tests {
1225 use super::*;
1226
1227 #[test]
1228 fn test_with_session_storage() {
1229 let state =
1230 StorageState::new().with_session_storage("https://example.com", "key", "value");
1231 assert!(!state.session_storage.is_empty());
1232 assert!(state.session_storage.contains_key("https://example.com"));
1233 }
1234
1235 #[test]
1236 fn test_is_empty() {
1237 let state = StorageState::new();
1238 assert!(state.is_empty());
1239
1240 let state_with_cookie =
1241 StorageState::new().with_cookie(Cookie::new("name", "value", "example.com"));
1242 assert!(!state_with_cookie.is_empty());
1243 }
1244 }
1245
1246 mod cookie_additional_tests {
1247 use super::*;
1248
1249 #[test]
1250 fn test_with_expires() {
1251 let cookie = Cookie::new("name", "value", "example.com").with_expires(1234567890);
1252 assert_eq!(cookie.expires, Some(1234567890));
1253 }
1254
1255 #[test]
1256 fn test_cookie_debug() {
1257 let cookie = Cookie::new("name", "value", "example.com");
1258 let debug = format!("{:?}", cookie);
1259 assert!(debug.contains("Cookie"));
1260 assert!(debug.contains("name"));
1261 }
1262
1263 #[test]
1264 fn test_same_site_variants() {
1265 let strict = SameSite::Strict;
1266 let lax = SameSite::Lax;
1267 let none = SameSite::None;
1268
1269 assert!(matches!(strict, SameSite::Strict));
1270 assert!(matches!(lax, SameSite::Lax));
1271 assert!(matches!(none, SameSite::None));
1272 }
1273
1274 #[test]
1275 fn test_same_site_debug() {
1276 assert!(format!("{:?}", SameSite::Strict).contains("Strict"));
1277 assert!(format!("{:?}", SameSite::Lax).contains("Lax"));
1278 assert!(format!("{:?}", SameSite::None).contains("None"));
1279 }
1280 }
1281
1282 mod context_state_tests {
1283 use super::*;
1284
1285 #[test]
1286 fn test_all_states() {
1287 let states = [
1288 ContextState::Creating,
1289 ContextState::Ready,
1290 ContextState::InUse,
1291 ContextState::Cleaning,
1292 ContextState::Closed,
1293 ContextState::Error,
1294 ];
1295
1296 for state in &states {
1297 let debug = format!("{:?}", state);
1298 assert!(!debug.is_empty());
1299 }
1300 }
1301
1302 #[test]
1303 fn test_state_clone() {
1304 let state = ContextState::Ready;
1305 let cloned = state;
1306 assert_eq!(state, cloned);
1307 }
1308
1309 #[test]
1310 fn test_state_serialization() {
1311 let state = ContextState::Ready;
1312 let serialized = serde_json::to_string(&state).unwrap();
1313 let deserialized: ContextState = serde_json::from_str(&serialized).unwrap();
1314 assert_eq!(state, deserialized);
1315 }
1316 }
1317
1318 mod browser_context_additional_tests {
1319 use super::*;
1320
1321 #[test]
1322 fn test_with_storage_state_initialization() {
1323 let storage =
1324 StorageState::new().with_cookie(Cookie::new("init", "value", "example.com"));
1325 let config = ContextConfig::new("test").with_storage_state(storage);
1326 let context = BrowserContext::new("ctx_1", config);
1327
1328 let stored = context.storage_state();
1329 assert_eq!(stored.cookies.len(), 1);
1330 }
1331
1332 #[test]
1333 fn test_context_age() {
1334 let config = ContextConfig::new("test");
1335 let context = BrowserContext::new("ctx_1", config);
1336
1337 let age = context.age_ms();
1339 assert!(age < 1000); }
1341
1342 #[test]
1343 fn test_clear_storage() {
1344 let storage =
1345 StorageState::new().with_cookie(Cookie::new("test", "value", "example.com"));
1346 let config = ContextConfig::new("test").with_storage_state(storage);
1347 let context = BrowserContext::new("ctx_1", config);
1348
1349 assert!(!context.storage_state().is_empty());
1350 context.clear_storage();
1351 assert!(context.storage_state().is_empty());
1352 }
1353
1354 #[test]
1355 fn test_debug() {
1356 let config = ContextConfig::new("test");
1357 let context = BrowserContext::new("ctx_1", config);
1358 let debug = format!("{:?}", context);
1359 assert!(debug.contains("BrowserContext"));
1360 assert!(debug.contains("ctx_1"));
1361 }
1362 }
1363
1364 mod geolocation_tests {
1365 use super::*;
1366
1367 #[test]
1368 fn test_geolocation_debug() {
1369 let geo = Geolocation {
1370 latitude: 37.7749,
1371 longitude: -122.4194,
1372 accuracy: 10.0,
1373 };
1374 let debug = format!("{:?}", geo);
1375 assert!(debug.contains("Geolocation"));
1376 assert!(debug.contains("37.7749"));
1377 }
1378
1379 #[test]
1380 fn test_geolocation_clone() {
1381 let geo = Geolocation {
1382 latitude: 37.7749,
1383 longitude: -122.4194,
1384 accuracy: 10.0,
1385 };
1386 let cloned = geo;
1387 assert!((geo.latitude - cloned.latitude).abs() < f64::EPSILON);
1388 }
1389 }
1390
1391 mod pool_stats_tests {
1392 use super::*;
1393
1394 #[test]
1395 fn test_pool_stats_debug() {
1396 let manager = ContextManager::new();
1397 manager.get_context("test_1").unwrap();
1398 let stats = manager.stats();
1399 let debug = format!("{:?}", stats);
1400 assert!(debug.contains("total"));
1401 }
1402 }
1403
1404 mod context_pool_error_tests {
1405 use super::*;
1406
1407 #[test]
1408 fn test_release_unknown_context() {
1409 let pool = ContextPool::new(5);
1410 let result = pool.release("unknown_ctx");
1411 assert!(result.is_err());
1412 }
1413
1414 #[test]
1415 fn test_close_unknown_context() {
1416 let pool = ContextPool::new(5);
1417 let result = pool.close("unknown_ctx");
1418 assert!(result.is_err());
1419 }
1420
1421 #[test]
1422 fn test_remove_context() {
1423 let pool = ContextPool::new(5);
1424 let id = pool.create(None).unwrap();
1425 assert_eq!(pool.count(), 1);
1426
1427 pool.remove(&id).unwrap();
1428 assert_eq!(pool.count(), 0);
1429 }
1430
1431 #[test]
1432 fn test_with_default_config() {
1433 let config = ContextConfig::new("default").mobile();
1434 let pool = ContextPool::new(5).with_default_config(config);
1435 let _id = pool.create(None).unwrap();
1436 assert_eq!(pool.count(), 1);
1438 }
1439
1440 #[test]
1441 fn test_acquire_creates_new_context() {
1442 let pool = ContextPool::new(5);
1443 let id1 = pool.acquire().unwrap();
1445 assert_eq!(pool.count(), 1);
1446 assert_eq!(pool.in_use_count(), 1);
1447
1448 let id2 = pool.acquire().unwrap();
1450 assert_eq!(pool.count(), 2);
1451 assert_eq!(pool.in_use_count(), 2);
1452
1453 assert_ne!(id1, id2);
1454 }
1455
1456 #[test]
1457 fn test_acquire_reuses_released_context() {
1458 let pool = ContextPool::new(5);
1459 let id1 = pool.acquire().unwrap();
1460 pool.release(&id1).unwrap();
1461
1462 let id2 = pool.acquire().unwrap();
1464 assert_eq!(id1, id2);
1465 assert_eq!(pool.count(), 1);
1466 }
1467
1468 #[test]
1469 fn test_pool_default() {
1470 let pool = ContextPool::default();
1471 assert_eq!(pool.count(), 0);
1472 }
1473 }
1474
1475 mod context_manager_error_tests {
1476 use super::*;
1477
1478 #[test]
1479 fn test_release_unknown_test_ok() {
1480 let manager = ContextManager::new();
1481 let result = manager.release_context("unknown_test");
1483 assert!(result.is_ok());
1484 }
1485
1486 #[test]
1487 fn test_with_pool_size() {
1488 let manager = ContextManager::with_pool_size(3);
1489 let stats = manager.stats();
1490 assert_eq!(stats.total, 0);
1491 }
1492
1493 #[test]
1494 fn test_manager_default() {
1495 let manager = ContextManager::default();
1496 assert_eq!(manager.stats().total, 0);
1497 }
1498 }
1499
1500 mod h0_context_state_tests {
1505 use super::*;
1506
1507 #[test]
1508 fn h0_ctx_01_state_creating() {
1509 assert_eq!(ContextState::Creating, ContextState::Creating);
1510 }
1511
1512 #[test]
1513 fn h0_ctx_02_state_ready() {
1514 assert_eq!(ContextState::Ready, ContextState::Ready);
1515 }
1516
1517 #[test]
1518 fn h0_ctx_03_state_in_use() {
1519 assert_eq!(ContextState::InUse, ContextState::InUse);
1520 }
1521
1522 #[test]
1523 fn h0_ctx_04_state_cleaning() {
1524 assert_eq!(ContextState::Cleaning, ContextState::Cleaning);
1525 }
1526
1527 #[test]
1528 fn h0_ctx_05_state_closed() {
1529 assert_eq!(ContextState::Closed, ContextState::Closed);
1530 }
1531
1532 #[test]
1533 fn h0_ctx_06_state_error() {
1534 assert_eq!(ContextState::Error, ContextState::Error);
1535 }
1536
1537 #[test]
1538 fn h0_ctx_07_state_inequality() {
1539 assert_ne!(ContextState::Ready, ContextState::Closed);
1540 }
1541
1542 #[test]
1543 fn h0_ctx_08_state_debug() {
1544 let debug = format!("{:?}", ContextState::Ready);
1545 assert!(debug.contains("Ready"));
1546 }
1547
1548 #[test]
1549 fn h0_ctx_09_state_clone() {
1550 let state = ContextState::InUse;
1551 let cloned = state;
1552 assert_eq!(state, cloned);
1553 }
1554
1555 #[test]
1556 fn h0_ctx_10_state_serialize() {
1557 let state = ContextState::Ready;
1558 let json = serde_json::to_string(&state).unwrap();
1559 assert!(!json.is_empty());
1560 }
1561 }
1562
1563 mod h0_same_site_tests {
1564 use super::*;
1565
1566 #[test]
1567 fn h0_ctx_11_same_site_strict() {
1568 assert_eq!(SameSite::Strict, SameSite::Strict);
1569 }
1570
1571 #[test]
1572 fn h0_ctx_12_same_site_lax() {
1573 assert_eq!(SameSite::Lax, SameSite::Lax);
1574 }
1575
1576 #[test]
1577 fn h0_ctx_13_same_site_none() {
1578 assert_eq!(SameSite::None, SameSite::None);
1579 }
1580
1581 #[test]
1582 fn h0_ctx_14_same_site_inequality() {
1583 assert_ne!(SameSite::Strict, SameSite::Lax);
1584 }
1585
1586 #[test]
1587 fn h0_ctx_15_same_site_debug() {
1588 let debug = format!("{:?}", SameSite::Strict);
1589 assert!(debug.contains("Strict"));
1590 }
1591 }
1592
1593 mod h0_cookie_tests {
1594 use super::*;
1595
1596 #[test]
1597 fn h0_ctx_16_cookie_name() {
1598 let cookie = Cookie::new("session", "abc", "example.com");
1599 assert_eq!(cookie.name, "session");
1600 }
1601
1602 #[test]
1603 fn h0_ctx_17_cookie_value() {
1604 let cookie = Cookie::new("session", "abc", "example.com");
1605 assert_eq!(cookie.value, "abc");
1606 }
1607
1608 #[test]
1609 fn h0_ctx_18_cookie_domain() {
1610 let cookie = Cookie::new("session", "abc", "example.com");
1611 assert_eq!(cookie.domain, "example.com");
1612 }
1613
1614 #[test]
1615 fn h0_ctx_19_cookie_default_path() {
1616 let cookie = Cookie::new("session", "abc", "example.com");
1617 assert_eq!(cookie.path, "/");
1618 }
1619
1620 #[test]
1621 fn h0_ctx_20_cookie_with_path() {
1622 let cookie = Cookie::new("session", "abc", "example.com").with_path("/api");
1623 assert_eq!(cookie.path, "/api");
1624 }
1625
1626 #[test]
1627 fn h0_ctx_21_cookie_expires() {
1628 let cookie = Cookie::new("session", "abc", "example.com").with_expires(1234567890);
1629 assert_eq!(cookie.expires, Some(1234567890));
1630 }
1631
1632 #[test]
1633 fn h0_ctx_22_cookie_http_only() {
1634 let cookie = Cookie::new("session", "abc", "example.com").http_only();
1635 assert!(cookie.http_only);
1636 }
1637
1638 #[test]
1639 fn h0_ctx_23_cookie_secure() {
1640 let cookie = Cookie::new("session", "abc", "example.com").secure();
1641 assert!(cookie.secure);
1642 }
1643
1644 #[test]
1645 fn h0_ctx_24_cookie_same_site() {
1646 let cookie =
1647 Cookie::new("session", "abc", "example.com").with_same_site(SameSite::Strict);
1648 assert_eq!(cookie.same_site, SameSite::Strict);
1649 }
1650
1651 #[test]
1652 fn h0_ctx_25_cookie_default_same_site() {
1653 let cookie = Cookie::new("session", "abc", "example.com");
1654 assert_eq!(cookie.same_site, SameSite::Lax);
1655 }
1656 }
1657
1658 mod h0_storage_state_tests {
1659 use super::*;
1660
1661 #[test]
1662 fn h0_ctx_26_storage_new_empty() {
1663 let state = StorageState::new();
1664 assert!(state.is_empty());
1665 }
1666
1667 #[test]
1668 fn h0_ctx_27_storage_with_cookie() {
1669 let state = StorageState::new().with_cookie(Cookie::new("test", "val", "example.com"));
1670 assert_eq!(state.cookies.len(), 1);
1671 }
1672
1673 #[test]
1674 fn h0_ctx_28_storage_with_local() {
1675 let state = StorageState::new().with_local_storage("https://ex.com", "key", "value");
1676 assert!(!state.local_storage.is_empty());
1677 }
1678
1679 #[test]
1680 fn h0_ctx_29_storage_with_session() {
1681 let state = StorageState::new().with_session_storage("https://ex.com", "key", "value");
1682 assert!(!state.session_storage.is_empty());
1683 }
1684
1685 #[test]
1686 fn h0_ctx_30_storage_clear() {
1687 let mut state = StorageState::new().with_cookie(Cookie::new("test", "v", "ex.com"));
1688 state.clear();
1689 assert!(state.is_empty());
1690 }
1691 }
1692
1693 mod h0_context_config_tests {
1694 use super::*;
1695
1696 #[test]
1697 fn h0_ctx_31_config_new_name() {
1698 let config = ContextConfig::new("test");
1699 assert_eq!(config.name, "test");
1700 }
1701
1702 #[test]
1703 fn h0_ctx_32_config_default_viewport() {
1704 let config = ContextConfig::default();
1705 assert_eq!(config.viewport_width, 1280);
1706 assert_eq!(config.viewport_height, 720);
1707 }
1708
1709 #[test]
1710 fn h0_ctx_33_config_with_viewport() {
1711 let config = ContextConfig::new("test").with_viewport(1920, 1080);
1712 assert_eq!(config.viewport_width, 1920);
1713 }
1714
1715 #[test]
1716 fn h0_ctx_34_config_mobile() {
1717 let config = ContextConfig::new("test").mobile();
1718 assert!(config.is_mobile);
1719 assert!(config.has_touch);
1720 }
1721
1722 #[test]
1723 fn h0_ctx_35_config_offline() {
1724 let config = ContextConfig::new("test").offline();
1725 assert!(config.offline);
1726 }
1727
1728 #[test]
1729 fn h0_ctx_36_config_with_geolocation() {
1730 let config = ContextConfig::new("test").with_geolocation(40.7, -74.0);
1731 assert!(config.geolocation.is_some());
1732 }
1733
1734 #[test]
1735 fn h0_ctx_37_config_with_header() {
1736 let config = ContextConfig::new("test").with_header("X-Test", "value");
1737 assert!(config.extra_headers.contains_key("X-Test"));
1738 }
1739
1740 #[test]
1741 fn h0_ctx_38_config_with_video() {
1742 let config = ContextConfig::new("test").with_video();
1743 assert!(config.record_video);
1744 }
1745
1746 #[test]
1747 fn h0_ctx_39_config_with_har() {
1748 let config = ContextConfig::new("test").with_har();
1749 assert!(config.record_har);
1750 }
1751
1752 #[test]
1753 fn h0_ctx_40_config_ignore_https() {
1754 let config = ContextConfig::new("test").ignore_https_errors();
1755 assert!(config.ignore_https_errors);
1756 }
1757 }
1758
1759 mod h0_browser_context_tests {
1760 use super::*;
1761
1762 #[test]
1763 fn h0_ctx_41_context_new_id() {
1764 let ctx = BrowserContext::new("ctx_1", ContextConfig::default());
1765 assert_eq!(ctx.id, "ctx_1");
1766 }
1767
1768 #[test]
1769 fn h0_ctx_42_context_initial_state() {
1770 let ctx = BrowserContext::new("ctx_1", ContextConfig::default());
1771 assert_eq!(ctx.state, ContextState::Creating);
1772 }
1773
1774 #[test]
1775 fn h0_ctx_43_context_ready() {
1776 let mut ctx = BrowserContext::new("ctx_1", ContextConfig::default());
1777 ctx.ready();
1778 assert!(ctx.is_available());
1779 }
1780
1781 #[test]
1782 fn h0_ctx_44_context_acquire() {
1783 let mut ctx = BrowserContext::new("ctx_1", ContextConfig::default());
1784 ctx.ready();
1785 ctx.acquire();
1786 assert!(ctx.is_in_use());
1787 }
1788
1789 #[test]
1790 fn h0_ctx_45_context_release() {
1791 let mut ctx = BrowserContext::new("ctx_1", ContextConfig::default());
1792 ctx.ready();
1793 ctx.acquire();
1794 ctx.release();
1795 assert!(ctx.is_available());
1796 }
1797
1798 #[test]
1799 fn h0_ctx_46_context_close() {
1800 let mut ctx = BrowserContext::new("ctx_1", ContextConfig::default());
1801 ctx.close();
1802 assert!(ctx.is_closed());
1803 }
1804
1805 #[test]
1806 fn h0_ctx_47_context_error() {
1807 let mut ctx = BrowserContext::new("ctx_1", ContextConfig::default());
1808 ctx.set_error("Failed");
1809 assert_eq!(ctx.state, ContextState::Error);
1810 }
1811
1812 #[test]
1813 fn h0_ctx_48_context_page_count() {
1814 let ctx = BrowserContext::new("ctx_1", ContextConfig::default());
1815 assert_eq!(ctx.page_count(), 0);
1816 }
1817
1818 #[test]
1819 fn h0_ctx_49_context_new_page() {
1820 let ctx = BrowserContext::new("ctx_1", ContextConfig::default());
1821 let page_id = ctx.new_page();
1822 assert!(!page_id.is_empty());
1823 assert_eq!(ctx.page_count(), 1);
1824 }
1825
1826 #[test]
1827 fn h0_ctx_50_context_pool_new() {
1828 let pool = ContextPool::new(10);
1829 assert_eq!(pool.count(), 0);
1830 }
1831 }
1832}