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.contexts.lock().map_err(|_| {
548 ProbarError::Io(std::io::Error::new(
549 std::io::ErrorKind::Other,
550 "Failed to lock contexts",
551 ))
552 })?;
553
554 if contexts.len() >= self.max_contexts {
555 return Err(ProbarError::AssertionError {
556 message: format!("Maximum contexts ({}) reached", self.max_contexts),
557 });
558 }
559
560 let id = {
561 let mut counter = self.counter.lock().map_err(|_| {
562 ProbarError::Io(std::io::Error::new(
563 std::io::ErrorKind::Other,
564 "Failed to lock counter",
565 ))
566 })?;
567 *counter += 1;
568 format!("ctx_{}", *counter)
569 };
570
571 let mut ctx_config = config.unwrap_or_else(|| self.default_config.clone());
572 ctx_config.name = id.clone();
573
574 let mut context = BrowserContext::new(&id, ctx_config);
575 context.ready();
576
577 contexts.insert(id.clone(), context);
578 Ok(id)
579 }
580
581 pub fn acquire(&self) -> ProbarResult<String> {
583 let mut contexts = self.contexts.lock().map_err(|_| {
584 ProbarError::Io(std::io::Error::new(
585 std::io::ErrorKind::Other,
586 "Failed to lock contexts",
587 ))
588 })?;
589
590 for (id, context) in contexts.iter_mut() {
591 if context.is_available() {
592 context.acquire();
593 return Ok(id.clone());
594 }
595 }
596
597 drop(contexts);
599 let id = self.create(None)?;
600
601 let mut contexts = self.contexts.lock().map_err(|_| {
602 ProbarError::Io(std::io::Error::new(
603 std::io::ErrorKind::Other,
604 "Failed to lock contexts",
605 ))
606 })?;
607
608 if let Some(context) = contexts.get_mut(&id) {
609 context.acquire();
610 }
611
612 Ok(id)
613 }
614
615 pub fn release(&self, context_id: &str) -> ProbarResult<()> {
617 let mut contexts = self.contexts.lock().map_err(|_| {
618 ProbarError::Io(std::io::Error::new(
619 std::io::ErrorKind::Other,
620 "Failed to lock contexts",
621 ))
622 })?;
623
624 if let Some(context) = contexts.get_mut(context_id) {
625 context.release();
626 Ok(())
627 } else {
628 Err(ProbarError::AssertionError {
629 message: format!("Context {} not found", context_id),
630 })
631 }
632 }
633
634 pub fn close(&self, context_id: &str) -> ProbarResult<()> {
636 let mut contexts = self.contexts.lock().map_err(|_| {
637 ProbarError::Io(std::io::Error::new(
638 std::io::ErrorKind::Other,
639 "Failed to lock contexts",
640 ))
641 })?;
642
643 if let Some(context) = contexts.get_mut(context_id) {
644 context.close();
645 Ok(())
646 } else {
647 Err(ProbarError::AssertionError {
648 message: format!("Context {} not found", context_id),
649 })
650 }
651 }
652
653 pub fn remove(&self, context_id: &str) -> ProbarResult<()> {
655 let mut contexts = self.contexts.lock().map_err(|_| {
656 ProbarError::Io(std::io::Error::new(
657 std::io::ErrorKind::Other,
658 "Failed to lock contexts",
659 ))
660 })?;
661
662 contexts.remove(context_id);
663 Ok(())
664 }
665
666 #[must_use]
668 pub fn count(&self) -> usize {
669 self.contexts.lock().map(|c| c.len()).unwrap_or(0)
670 }
671
672 #[must_use]
674 pub fn available_count(&self) -> usize {
675 self.contexts
676 .lock()
677 .map(|c| c.values().filter(|ctx| ctx.is_available()).count())
678 .unwrap_or(0)
679 }
680
681 #[must_use]
683 pub fn in_use_count(&self) -> usize {
684 self.contexts
685 .lock()
686 .map(|c| c.values().filter(|ctx| ctx.is_in_use()).count())
687 .unwrap_or(0)
688 }
689
690 pub fn close_all(&self) {
692 if let Ok(mut contexts) = self.contexts.lock() {
693 for context in contexts.values_mut() {
694 context.close();
695 }
696 }
697 }
698
699 pub fn clear(&self) {
701 if let Ok(mut contexts) = self.contexts.lock() {
702 contexts.clear();
703 }
704 }
705
706 #[must_use]
708 pub fn context_ids(&self) -> Vec<String> {
709 self.contexts
710 .lock()
711 .map(|c| c.keys().cloned().collect())
712 .unwrap_or_default()
713 }
714}
715
716#[derive(Debug)]
718pub struct ContextManager {
719 pool: ContextPool,
721 active_contexts: Arc<Mutex<HashMap<String, String>>>,
723}
724
725impl Default for ContextManager {
726 fn default() -> Self {
727 Self::new()
728 }
729}
730
731impl ContextManager {
732 #[must_use]
734 pub fn new() -> Self {
735 Self {
736 pool: ContextPool::new(10),
737 active_contexts: Arc::new(Mutex::new(HashMap::new())),
738 }
739 }
740
741 #[must_use]
743 pub fn with_pool_size(pool_size: usize) -> Self {
744 Self {
745 pool: ContextPool::new(pool_size),
746 active_contexts: Arc::new(Mutex::new(HashMap::new())),
747 }
748 }
749
750 pub fn get_context(&self, test_id: &str) -> ProbarResult<String> {
752 let mut active = self.active_contexts.lock().map_err(|_| {
753 ProbarError::Io(std::io::Error::new(
754 std::io::ErrorKind::Other,
755 "Failed to lock active contexts",
756 ))
757 })?;
758
759 if let Some(context_id) = active.get(test_id) {
761 return Ok(context_id.clone());
762 }
763
764 let context_id = self.pool.acquire()?;
766 let _ = active.insert(test_id.to_string(), context_id.clone());
767 Ok(context_id)
768 }
769
770 pub fn release_context(&self, test_id: &str) -> ProbarResult<()> {
772 let mut active = self.active_contexts.lock().map_err(|_| {
773 ProbarError::Io(std::io::Error::new(
774 std::io::ErrorKind::Other,
775 "Failed to lock active contexts",
776 ))
777 })?;
778
779 if let Some(context_id) = active.remove(test_id) {
780 self.pool.release(&context_id)?;
781 }
782 Ok(())
783 }
784
785 pub fn create_isolated_context(
787 &self,
788 test_id: &str,
789 config: ContextConfig,
790 ) -> ProbarResult<String> {
791 let context_id = self.pool.create(Some(config))?;
792
793 let mut active = self.active_contexts.lock().map_err(|_| {
794 ProbarError::Io(std::io::Error::new(
795 std::io::ErrorKind::Other,
796 "Failed to lock active contexts",
797 ))
798 })?;
799
800 if let Some(old_id) = active.get(test_id) {
802 let _ = self.pool.release(old_id);
803 }
804
805 let _ = active.insert(test_id.to_string(), context_id.clone());
806 Ok(context_id)
807 }
808
809 #[must_use]
811 pub fn stats(&self) -> ContextPoolStats {
812 ContextPoolStats {
813 total: self.pool.count(),
814 available: self.pool.available_count(),
815 in_use: self.pool.in_use_count(),
816 active_tests: self.active_contexts.lock().map(|a| a.len()).unwrap_or(0),
817 }
818 }
819
820 pub fn cleanup(&self) {
822 self.pool.close_all();
823 self.pool.clear();
824 if let Ok(mut active) = self.active_contexts.lock() {
825 active.clear();
826 }
827 }
828}
829
830#[derive(Debug, Clone, Serialize, Deserialize)]
832pub struct ContextPoolStats {
833 pub total: usize,
835 pub available: usize,
837 pub in_use: usize,
839 pub active_tests: usize,
841}
842
843#[cfg(test)]
844#[allow(clippy::unwrap_used, clippy::expect_used)]
845mod tests {
846 use super::*;
847
848 mod storage_state_tests {
849 use super::*;
850
851 #[test]
852 fn test_new() {
853 let state = StorageState::new();
854 assert!(state.is_empty());
855 }
856
857 #[test]
858 fn test_with_cookie() {
859 let state =
860 StorageState::new().with_cookie(Cookie::new("session", "abc123", "example.com"));
861 assert_eq!(state.cookies.len(), 1);
862 }
863
864 #[test]
865 fn test_with_local_storage() {
866 let state =
867 StorageState::new().with_local_storage("https://example.com", "key", "value");
868 assert!(!state.local_storage.is_empty());
869 }
870
871 #[test]
872 fn test_clear() {
873 let mut state =
874 StorageState::new().with_cookie(Cookie::new("session", "abc123", "example.com"));
875 state.clear();
876 assert!(state.is_empty());
877 }
878 }
879
880 mod cookie_tests {
881 use super::*;
882
883 #[test]
884 fn test_new() {
885 let cookie = Cookie::new("name", "value", "example.com");
886 assert_eq!(cookie.name, "name");
887 assert_eq!(cookie.value, "value");
888 assert_eq!(cookie.domain, "example.com");
889 assert_eq!(cookie.path, "/");
890 }
891
892 #[test]
893 fn test_with_path() {
894 let cookie = Cookie::new("name", "value", "example.com").with_path("/api");
895 assert_eq!(cookie.path, "/api");
896 }
897
898 #[test]
899 fn test_secure_http_only() {
900 let cookie = Cookie::new("name", "value", "example.com")
901 .secure()
902 .http_only();
903 assert!(cookie.secure);
904 assert!(cookie.http_only);
905 }
906
907 #[test]
908 fn test_same_site() {
909 let cookie =
910 Cookie::new("name", "value", "example.com").with_same_site(SameSite::Strict);
911 assert!(matches!(cookie.same_site, SameSite::Strict));
912 }
913 }
914
915 mod context_config_tests {
916 use super::*;
917
918 #[test]
919 fn test_new() {
920 let config = ContextConfig::new("test");
921 assert_eq!(config.name, "test");
922 assert_eq!(config.viewport_width, 1280);
923 assert_eq!(config.viewport_height, 720);
924 }
925
926 #[test]
927 fn test_with_viewport() {
928 let config = ContextConfig::new("test").with_viewport(1920, 1080);
929 assert_eq!(config.viewport_width, 1920);
930 assert_eq!(config.viewport_height, 1080);
931 }
932
933 #[test]
934 fn test_mobile() {
935 let config = ContextConfig::new("test").mobile();
936 assert!(config.is_mobile);
937 assert!(config.has_touch);
938 }
939
940 #[test]
941 fn test_with_geolocation() {
942 let config = ContextConfig::new("test").with_geolocation(37.7749, -122.4194);
943 assert!(config.geolocation.is_some());
944 let geo = config.geolocation.unwrap();
945 assert!((geo.latitude - 37.7749).abs() < f64::EPSILON);
946 }
947
948 #[test]
949 fn test_with_header() {
950 let config = ContextConfig::new("test").with_header("Authorization", "Bearer token");
951 assert_eq!(
952 config.extra_headers.get("Authorization"),
953 Some(&"Bearer token".to_string())
954 );
955 }
956
957 #[test]
958 fn test_offline() {
959 let config = ContextConfig::new("test").offline();
960 assert!(config.offline);
961 }
962 }
963
964 mod browser_context_tests {
965 use super::*;
966
967 #[test]
968 fn test_new() {
969 let config = ContextConfig::new("test");
970 let context = BrowserContext::new("ctx_1", config);
971 assert_eq!(context.id, "ctx_1");
972 assert!(matches!(context.state, ContextState::Creating));
973 }
974
975 #[test]
976 fn test_state_transitions() {
977 let config = ContextConfig::new("test");
978 let mut context = BrowserContext::new("ctx_1", config);
979
980 context.ready();
981 assert!(context.is_available());
982
983 context.acquire();
984 assert!(context.is_in_use());
985
986 context.release();
987 assert!(context.is_available());
988
989 context.close();
990 assert!(context.is_closed());
991 }
992
993 #[test]
994 fn test_error_state() {
995 let config = ContextConfig::new("test");
996 let mut context = BrowserContext::new("ctx_1", config);
997
998 context.set_error("Connection failed");
999 assert!(matches!(context.state, ContextState::Error));
1000 assert_eq!(context.error_message, Some("Connection failed".to_string()));
1001 }
1002
1003 #[test]
1004 fn test_pages() {
1005 let config = ContextConfig::new("test");
1006 let context = BrowserContext::new("ctx_1", config);
1007
1008 let page1 = context.new_page();
1009 let page2 = context.new_page();
1010 assert_eq!(context.page_count(), 2);
1011
1012 context.close_page(&page1);
1013 assert_eq!(context.page_count(), 1);
1014
1015 context.close_page(&page2);
1016 assert_eq!(context.page_count(), 0);
1017 }
1018
1019 #[test]
1020 fn test_storage() {
1021 let config = ContextConfig::new("test");
1022 let context = BrowserContext::new("ctx_1", config);
1023
1024 context.add_cookie(Cookie::new("session", "abc", "example.com"));
1025 let storage = context.storage_state();
1026 assert_eq!(storage.cookies.len(), 1);
1027
1028 context.clear_cookies();
1029 let storage = context.storage_state();
1030 assert!(storage.cookies.is_empty());
1031 }
1032 }
1033
1034 mod context_pool_tests {
1035 use super::*;
1036
1037 #[test]
1038 fn test_new() {
1039 let pool = ContextPool::new(5);
1040 assert_eq!(pool.count(), 0);
1041 }
1042
1043 #[test]
1044 fn test_create() {
1045 let pool = ContextPool::new(5);
1046 let id = pool.create(None).unwrap();
1047 assert!(!id.is_empty());
1048 assert_eq!(pool.count(), 1);
1049 }
1050
1051 #[test]
1052 fn test_max_contexts() {
1053 let pool = ContextPool::new(2);
1054 pool.create(None).unwrap();
1055 pool.create(None).unwrap();
1056
1057 let result = pool.create(None);
1058 assert!(result.is_err());
1059 }
1060
1061 #[test]
1062 fn test_acquire_release() {
1063 let pool = ContextPool::new(5);
1064 let id = pool.acquire().unwrap();
1065
1066 assert_eq!(pool.in_use_count(), 1);
1067 assert_eq!(pool.available_count(), 0);
1068
1069 pool.release(&id).unwrap();
1070 assert_eq!(pool.in_use_count(), 0);
1071 assert_eq!(pool.available_count(), 1);
1072 }
1073
1074 #[test]
1075 fn test_close() {
1076 let pool = ContextPool::new(5);
1077 let id = pool.create(None).unwrap();
1078
1079 pool.close(&id).unwrap();
1080 assert_eq!(pool.available_count(), 0);
1081 }
1082
1083 #[test]
1084 fn test_close_all() {
1085 let pool = ContextPool::new(5);
1086 pool.create(None).unwrap();
1087 pool.create(None).unwrap();
1088
1089 pool.close_all();
1090 assert_eq!(pool.available_count(), 0);
1091 }
1092
1093 #[test]
1094 fn test_clear() {
1095 let pool = ContextPool::new(5);
1096 pool.create(None).unwrap();
1097 pool.clear();
1098 assert_eq!(pool.count(), 0);
1099 }
1100 }
1101
1102 mod context_manager_tests {
1103 use super::*;
1104
1105 #[test]
1106 fn test_new() {
1107 let manager = ContextManager::new();
1108 let stats = manager.stats();
1109 assert_eq!(stats.total, 0);
1110 }
1111
1112 #[test]
1113 fn test_get_context() {
1114 let manager = ContextManager::new();
1115 let ctx_id = manager.get_context("test_1").unwrap();
1116 assert!(!ctx_id.is_empty());
1117
1118 let ctx_id2 = manager.get_context("test_1").unwrap();
1120 assert_eq!(ctx_id, ctx_id2);
1121
1122 let ctx_id3 = manager.get_context("test_2").unwrap();
1124 assert_ne!(ctx_id, ctx_id3);
1125 }
1126
1127 #[test]
1128 fn test_release_context() {
1129 let manager = ContextManager::new();
1130 let _ctx_id = manager.get_context("test_1").unwrap();
1131
1132 manager.release_context("test_1").unwrap();
1133 let stats = manager.stats();
1134 assert_eq!(stats.available, 1);
1135 assert_eq!(stats.in_use, 0);
1136 }
1137
1138 #[test]
1139 fn test_create_isolated_context() {
1140 let manager = ContextManager::new();
1141 let config = ContextConfig::new("isolated")
1142 .mobile()
1143 .with_viewport(375, 812);
1144
1145 let ctx_id = manager.create_isolated_context("test_1", config).unwrap();
1146 assert!(!ctx_id.is_empty());
1147 }
1148
1149 #[test]
1150 fn test_cleanup() {
1151 let manager = ContextManager::new();
1152 manager.get_context("test_1").unwrap();
1153 manager.get_context("test_2").unwrap();
1154
1155 manager.cleanup();
1156 let stats = manager.stats();
1157 assert_eq!(stats.total, 0);
1158 assert_eq!(stats.active_tests, 0);
1159 }
1160
1161 #[test]
1162 fn test_stats() {
1163 let manager = ContextManager::new();
1164 manager.get_context("test_1").unwrap();
1165 manager.get_context("test_2").unwrap();
1166
1167 let stats = manager.stats();
1168 assert_eq!(stats.total, 2);
1169 assert_eq!(stats.in_use, 2);
1170 assert_eq!(stats.active_tests, 2);
1171 }
1172 }
1173
1174 mod context_config_additional_tests {
1175 use super::*;
1176
1177 #[test]
1178 fn test_with_device_scale() {
1179 let config = ContextConfig::new("test").with_device_scale(2.0);
1180 assert!((config.device_scale_factor - 2.0).abs() < f64::EPSILON);
1181 }
1182
1183 #[test]
1184 fn test_with_user_agent() {
1185 let config = ContextConfig::new("test").with_user_agent("Custom UA");
1186 assert_eq!(config.user_agent, Some("Custom UA".to_string()));
1187 }
1188
1189 #[test]
1190 fn test_with_locale() {
1191 let config = ContextConfig::new("test").with_locale("en-US");
1192 assert_eq!(config.locale, Some("en-US".to_string()));
1193 }
1194
1195 #[test]
1196 fn test_with_timezone() {
1197 let config = ContextConfig::new("test").with_timezone("America/New_York");
1198 assert_eq!(config.timezone, Some("America/New_York".to_string()));
1199 }
1200
1201 #[test]
1202 fn test_with_permission() {
1203 let config = ContextConfig::new("test")
1204 .with_permission("geolocation")
1205 .with_permission("notifications");
1206 assert_eq!(config.permissions.len(), 2);
1207 assert!(config.permissions.contains(&"geolocation".to_string()));
1208 }
1209
1210 #[test]
1211 fn test_with_storage_state() {
1212 let storage =
1213 StorageState::new().with_cookie(Cookie::new("session", "abc", "example.com"));
1214 let config = ContextConfig::new("test").with_storage_state(storage);
1215 assert!(config.storage_state.is_some());
1216 assert_eq!(config.storage_state.unwrap().cookies.len(), 1);
1217 }
1218
1219 #[test]
1220 fn test_with_video() {
1221 let config = ContextConfig::new("test").with_video();
1222 assert!(config.record_video);
1223 }
1224
1225 #[test]
1226 fn test_with_har() {
1227 let config = ContextConfig::new("test").with_har();
1228 assert!(config.record_har);
1229 }
1230
1231 #[test]
1232 fn test_ignore_https_errors() {
1233 let config = ContextConfig::new("test").ignore_https_errors();
1234 assert!(config.ignore_https_errors);
1235 }
1236
1237 #[test]
1238 fn test_config_default() {
1239 let config = ContextConfig::default();
1240 assert!(config.name.is_empty());
1241 assert_eq!(config.viewport_width, 1280);
1242 assert!(!config.is_mobile);
1243 assert!(!config.offline);
1244 }
1245 }
1246
1247 mod storage_state_additional_tests {
1248 use super::*;
1249
1250 #[test]
1251 fn test_with_session_storage() {
1252 let state =
1253 StorageState::new().with_session_storage("https://example.com", "key", "value");
1254 assert!(!state.session_storage.is_empty());
1255 assert!(state.session_storage.contains_key("https://example.com"));
1256 }
1257
1258 #[test]
1259 fn test_is_empty() {
1260 let state = StorageState::new();
1261 assert!(state.is_empty());
1262
1263 let state_with_cookie =
1264 StorageState::new().with_cookie(Cookie::new("name", "value", "example.com"));
1265 assert!(!state_with_cookie.is_empty());
1266 }
1267 }
1268
1269 mod cookie_additional_tests {
1270 use super::*;
1271
1272 #[test]
1273 fn test_with_expires() {
1274 let cookie = Cookie::new("name", "value", "example.com").with_expires(1234567890);
1275 assert_eq!(cookie.expires, Some(1234567890));
1276 }
1277
1278 #[test]
1279 fn test_cookie_debug() {
1280 let cookie = Cookie::new("name", "value", "example.com");
1281 let debug = format!("{:?}", cookie);
1282 assert!(debug.contains("Cookie"));
1283 assert!(debug.contains("name"));
1284 }
1285
1286 #[test]
1287 fn test_same_site_variants() {
1288 let strict = SameSite::Strict;
1289 let lax = SameSite::Lax;
1290 let none = SameSite::None;
1291
1292 assert!(matches!(strict, SameSite::Strict));
1293 assert!(matches!(lax, SameSite::Lax));
1294 assert!(matches!(none, SameSite::None));
1295 }
1296
1297 #[test]
1298 fn test_same_site_debug() {
1299 assert!(format!("{:?}", SameSite::Strict).contains("Strict"));
1300 assert!(format!("{:?}", SameSite::Lax).contains("Lax"));
1301 assert!(format!("{:?}", SameSite::None).contains("None"));
1302 }
1303 }
1304
1305 mod context_state_tests {
1306 use super::*;
1307
1308 #[test]
1309 fn test_all_states() {
1310 let states = [
1311 ContextState::Creating,
1312 ContextState::Ready,
1313 ContextState::InUse,
1314 ContextState::Cleaning,
1315 ContextState::Closed,
1316 ContextState::Error,
1317 ];
1318
1319 for state in &states {
1320 let debug = format!("{:?}", state);
1321 assert!(!debug.is_empty());
1322 }
1323 }
1324
1325 #[test]
1326 fn test_state_clone() {
1327 let state = ContextState::Ready;
1328 let cloned = state;
1329 assert_eq!(state, cloned);
1330 }
1331
1332 #[test]
1333 fn test_state_serialization() {
1334 let state = ContextState::Ready;
1335 let serialized = serde_json::to_string(&state).unwrap();
1336 let deserialized: ContextState = serde_json::from_str(&serialized).unwrap();
1337 assert_eq!(state, deserialized);
1338 }
1339 }
1340
1341 mod browser_context_additional_tests {
1342 use super::*;
1343
1344 #[test]
1345 fn test_with_storage_state_initialization() {
1346 let storage =
1347 StorageState::new().with_cookie(Cookie::new("init", "value", "example.com"));
1348 let config = ContextConfig::new("test").with_storage_state(storage);
1349 let context = BrowserContext::new("ctx_1", config);
1350
1351 let stored = context.storage_state();
1352 assert_eq!(stored.cookies.len(), 1);
1353 }
1354
1355 #[test]
1356 fn test_context_age() {
1357 let config = ContextConfig::new("test");
1358 let context = BrowserContext::new("ctx_1", config);
1359
1360 let age = context.age_ms();
1362 assert!(age < 1000); }
1364
1365 #[test]
1366 fn test_clear_storage() {
1367 let storage =
1368 StorageState::new().with_cookie(Cookie::new("test", "value", "example.com"));
1369 let config = ContextConfig::new("test").with_storage_state(storage);
1370 let context = BrowserContext::new("ctx_1", config);
1371
1372 assert!(!context.storage_state().is_empty());
1373 context.clear_storage();
1374 assert!(context.storage_state().is_empty());
1375 }
1376
1377 #[test]
1378 fn test_debug() {
1379 let config = ContextConfig::new("test");
1380 let context = BrowserContext::new("ctx_1", config);
1381 let debug = format!("{:?}", context);
1382 assert!(debug.contains("BrowserContext"));
1383 assert!(debug.contains("ctx_1"));
1384 }
1385 }
1386
1387 mod geolocation_tests {
1388 use super::*;
1389
1390 #[test]
1391 fn test_geolocation_debug() {
1392 let geo = Geolocation {
1393 latitude: 37.7749,
1394 longitude: -122.4194,
1395 accuracy: 10.0,
1396 };
1397 let debug = format!("{:?}", geo);
1398 assert!(debug.contains("Geolocation"));
1399 assert!(debug.contains("37.7749"));
1400 }
1401
1402 #[test]
1403 fn test_geolocation_clone() {
1404 let geo = Geolocation {
1405 latitude: 37.7749,
1406 longitude: -122.4194,
1407 accuracy: 10.0,
1408 };
1409 let cloned = geo;
1410 assert!((geo.latitude - cloned.latitude).abs() < f64::EPSILON);
1411 }
1412 }
1413
1414 mod pool_stats_tests {
1415 use super::*;
1416
1417 #[test]
1418 fn test_pool_stats_debug() {
1419 let manager = ContextManager::new();
1420 manager.get_context("test_1").unwrap();
1421 let stats = manager.stats();
1422 let debug = format!("{:?}", stats);
1423 assert!(debug.contains("total"));
1424 }
1425 }
1426
1427 mod context_pool_error_tests {
1428 use super::*;
1429
1430 #[test]
1431 fn test_release_unknown_context() {
1432 let pool = ContextPool::new(5);
1433 let result = pool.release("unknown_ctx");
1434 assert!(result.is_err());
1435 }
1436
1437 #[test]
1438 fn test_close_unknown_context() {
1439 let pool = ContextPool::new(5);
1440 let result = pool.close("unknown_ctx");
1441 assert!(result.is_err());
1442 }
1443
1444 #[test]
1445 fn test_remove_context() {
1446 let pool = ContextPool::new(5);
1447 let id = pool.create(None).unwrap();
1448 assert_eq!(pool.count(), 1);
1449
1450 pool.remove(&id).unwrap();
1451 assert_eq!(pool.count(), 0);
1452 }
1453
1454 #[test]
1455 fn test_with_default_config() {
1456 let config = ContextConfig::new("default").mobile();
1457 let pool = ContextPool::new(5).with_default_config(config);
1458 let _id = pool.create(None).unwrap();
1459 assert_eq!(pool.count(), 1);
1461 }
1462
1463 #[test]
1464 fn test_acquire_creates_new_context() {
1465 let pool = ContextPool::new(5);
1466 let id1 = pool.acquire().unwrap();
1468 assert_eq!(pool.count(), 1);
1469 assert_eq!(pool.in_use_count(), 1);
1470
1471 let id2 = pool.acquire().unwrap();
1473 assert_eq!(pool.count(), 2);
1474 assert_eq!(pool.in_use_count(), 2);
1475
1476 assert_ne!(id1, id2);
1477 }
1478
1479 #[test]
1480 fn test_acquire_reuses_released_context() {
1481 let pool = ContextPool::new(5);
1482 let id1 = pool.acquire().unwrap();
1483 pool.release(&id1).unwrap();
1484
1485 let id2 = pool.acquire().unwrap();
1487 assert_eq!(id1, id2);
1488 assert_eq!(pool.count(), 1);
1489 }
1490
1491 #[test]
1492 fn test_pool_default() {
1493 let pool = ContextPool::default();
1494 assert_eq!(pool.count(), 0);
1495 }
1496 }
1497
1498 mod context_manager_error_tests {
1499 use super::*;
1500
1501 #[test]
1502 fn test_release_unknown_test_ok() {
1503 let manager = ContextManager::new();
1504 let result = manager.release_context("unknown_test");
1506 assert!(result.is_ok());
1507 }
1508
1509 #[test]
1510 fn test_with_pool_size() {
1511 let manager = ContextManager::with_pool_size(3);
1512 let stats = manager.stats();
1513 assert_eq!(stats.total, 0);
1514 }
1515
1516 #[test]
1517 fn test_manager_default() {
1518 let manager = ContextManager::default();
1519 assert_eq!(manager.stats().total, 0);
1520 }
1521 }
1522
1523 mod h0_context_state_tests {
1528 use super::*;
1529
1530 #[test]
1531 fn h0_ctx_01_state_creating() {
1532 assert_eq!(ContextState::Creating, ContextState::Creating);
1533 }
1534
1535 #[test]
1536 fn h0_ctx_02_state_ready() {
1537 assert_eq!(ContextState::Ready, ContextState::Ready);
1538 }
1539
1540 #[test]
1541 fn h0_ctx_03_state_in_use() {
1542 assert_eq!(ContextState::InUse, ContextState::InUse);
1543 }
1544
1545 #[test]
1546 fn h0_ctx_04_state_cleaning() {
1547 assert_eq!(ContextState::Cleaning, ContextState::Cleaning);
1548 }
1549
1550 #[test]
1551 fn h0_ctx_05_state_closed() {
1552 assert_eq!(ContextState::Closed, ContextState::Closed);
1553 }
1554
1555 #[test]
1556 fn h0_ctx_06_state_error() {
1557 assert_eq!(ContextState::Error, ContextState::Error);
1558 }
1559
1560 #[test]
1561 fn h0_ctx_07_state_inequality() {
1562 assert_ne!(ContextState::Ready, ContextState::Closed);
1563 }
1564
1565 #[test]
1566 fn h0_ctx_08_state_debug() {
1567 let debug = format!("{:?}", ContextState::Ready);
1568 assert!(debug.contains("Ready"));
1569 }
1570
1571 #[test]
1572 fn h0_ctx_09_state_clone() {
1573 let state = ContextState::InUse;
1574 let cloned = state;
1575 assert_eq!(state, cloned);
1576 }
1577
1578 #[test]
1579 fn h0_ctx_10_state_serialize() {
1580 let state = ContextState::Ready;
1581 let json = serde_json::to_string(&state).unwrap();
1582 assert!(!json.is_empty());
1583 }
1584 }
1585
1586 mod h0_same_site_tests {
1587 use super::*;
1588
1589 #[test]
1590 fn h0_ctx_11_same_site_strict() {
1591 assert_eq!(SameSite::Strict, SameSite::Strict);
1592 }
1593
1594 #[test]
1595 fn h0_ctx_12_same_site_lax() {
1596 assert_eq!(SameSite::Lax, SameSite::Lax);
1597 }
1598
1599 #[test]
1600 fn h0_ctx_13_same_site_none() {
1601 assert_eq!(SameSite::None, SameSite::None);
1602 }
1603
1604 #[test]
1605 fn h0_ctx_14_same_site_inequality() {
1606 assert_ne!(SameSite::Strict, SameSite::Lax);
1607 }
1608
1609 #[test]
1610 fn h0_ctx_15_same_site_debug() {
1611 let debug = format!("{:?}", SameSite::Strict);
1612 assert!(debug.contains("Strict"));
1613 }
1614 }
1615
1616 mod h0_cookie_tests {
1617 use super::*;
1618
1619 #[test]
1620 fn h0_ctx_16_cookie_name() {
1621 let cookie = Cookie::new("session", "abc", "example.com");
1622 assert_eq!(cookie.name, "session");
1623 }
1624
1625 #[test]
1626 fn h0_ctx_17_cookie_value() {
1627 let cookie = Cookie::new("session", "abc", "example.com");
1628 assert_eq!(cookie.value, "abc");
1629 }
1630
1631 #[test]
1632 fn h0_ctx_18_cookie_domain() {
1633 let cookie = Cookie::new("session", "abc", "example.com");
1634 assert_eq!(cookie.domain, "example.com");
1635 }
1636
1637 #[test]
1638 fn h0_ctx_19_cookie_default_path() {
1639 let cookie = Cookie::new("session", "abc", "example.com");
1640 assert_eq!(cookie.path, "/");
1641 }
1642
1643 #[test]
1644 fn h0_ctx_20_cookie_with_path() {
1645 let cookie = Cookie::new("session", "abc", "example.com").with_path("/api");
1646 assert_eq!(cookie.path, "/api");
1647 }
1648
1649 #[test]
1650 fn h0_ctx_21_cookie_expires() {
1651 let cookie = Cookie::new("session", "abc", "example.com").with_expires(1234567890);
1652 assert_eq!(cookie.expires, Some(1234567890));
1653 }
1654
1655 #[test]
1656 fn h0_ctx_22_cookie_http_only() {
1657 let cookie = Cookie::new("session", "abc", "example.com").http_only();
1658 assert!(cookie.http_only);
1659 }
1660
1661 #[test]
1662 fn h0_ctx_23_cookie_secure() {
1663 let cookie = Cookie::new("session", "abc", "example.com").secure();
1664 assert!(cookie.secure);
1665 }
1666
1667 #[test]
1668 fn h0_ctx_24_cookie_same_site() {
1669 let cookie =
1670 Cookie::new("session", "abc", "example.com").with_same_site(SameSite::Strict);
1671 assert_eq!(cookie.same_site, SameSite::Strict);
1672 }
1673
1674 #[test]
1675 fn h0_ctx_25_cookie_default_same_site() {
1676 let cookie = Cookie::new("session", "abc", "example.com");
1677 assert_eq!(cookie.same_site, SameSite::Lax);
1678 }
1679 }
1680
1681 mod h0_storage_state_tests {
1682 use super::*;
1683
1684 #[test]
1685 fn h0_ctx_26_storage_new_empty() {
1686 let state = StorageState::new();
1687 assert!(state.is_empty());
1688 }
1689
1690 #[test]
1691 fn h0_ctx_27_storage_with_cookie() {
1692 let state = StorageState::new().with_cookie(Cookie::new("test", "val", "example.com"));
1693 assert_eq!(state.cookies.len(), 1);
1694 }
1695
1696 #[test]
1697 fn h0_ctx_28_storage_with_local() {
1698 let state = StorageState::new().with_local_storage("https://ex.com", "key", "value");
1699 assert!(!state.local_storage.is_empty());
1700 }
1701
1702 #[test]
1703 fn h0_ctx_29_storage_with_session() {
1704 let state = StorageState::new().with_session_storage("https://ex.com", "key", "value");
1705 assert!(!state.session_storage.is_empty());
1706 }
1707
1708 #[test]
1709 fn h0_ctx_30_storage_clear() {
1710 let mut state = StorageState::new().with_cookie(Cookie::new("test", "v", "ex.com"));
1711 state.clear();
1712 assert!(state.is_empty());
1713 }
1714 }
1715
1716 mod h0_context_config_tests {
1717 use super::*;
1718
1719 #[test]
1720 fn h0_ctx_31_config_new_name() {
1721 let config = ContextConfig::new("test");
1722 assert_eq!(config.name, "test");
1723 }
1724
1725 #[test]
1726 fn h0_ctx_32_config_default_viewport() {
1727 let config = ContextConfig::default();
1728 assert_eq!(config.viewport_width, 1280);
1729 assert_eq!(config.viewport_height, 720);
1730 }
1731
1732 #[test]
1733 fn h0_ctx_33_config_with_viewport() {
1734 let config = ContextConfig::new("test").with_viewport(1920, 1080);
1735 assert_eq!(config.viewport_width, 1920);
1736 }
1737
1738 #[test]
1739 fn h0_ctx_34_config_mobile() {
1740 let config = ContextConfig::new("test").mobile();
1741 assert!(config.is_mobile);
1742 assert!(config.has_touch);
1743 }
1744
1745 #[test]
1746 fn h0_ctx_35_config_offline() {
1747 let config = ContextConfig::new("test").offline();
1748 assert!(config.offline);
1749 }
1750
1751 #[test]
1752 fn h0_ctx_36_config_with_geolocation() {
1753 let config = ContextConfig::new("test").with_geolocation(40.7, -74.0);
1754 assert!(config.geolocation.is_some());
1755 }
1756
1757 #[test]
1758 fn h0_ctx_37_config_with_header() {
1759 let config = ContextConfig::new("test").with_header("X-Test", "value");
1760 assert!(config.extra_headers.contains_key("X-Test"));
1761 }
1762
1763 #[test]
1764 fn h0_ctx_38_config_with_video() {
1765 let config = ContextConfig::new("test").with_video();
1766 assert!(config.record_video);
1767 }
1768
1769 #[test]
1770 fn h0_ctx_39_config_with_har() {
1771 let config = ContextConfig::new("test").with_har();
1772 assert!(config.record_har);
1773 }
1774
1775 #[test]
1776 fn h0_ctx_40_config_ignore_https() {
1777 let config = ContextConfig::new("test").ignore_https_errors();
1778 assert!(config.ignore_https_errors);
1779 }
1780 }
1781
1782 mod h0_browser_context_tests {
1783 use super::*;
1784
1785 #[test]
1786 fn h0_ctx_41_context_new_id() {
1787 let ctx = BrowserContext::new("ctx_1", ContextConfig::default());
1788 assert_eq!(ctx.id, "ctx_1");
1789 }
1790
1791 #[test]
1792 fn h0_ctx_42_context_initial_state() {
1793 let ctx = BrowserContext::new("ctx_1", ContextConfig::default());
1794 assert_eq!(ctx.state, ContextState::Creating);
1795 }
1796
1797 #[test]
1798 fn h0_ctx_43_context_ready() {
1799 let mut ctx = BrowserContext::new("ctx_1", ContextConfig::default());
1800 ctx.ready();
1801 assert!(ctx.is_available());
1802 }
1803
1804 #[test]
1805 fn h0_ctx_44_context_acquire() {
1806 let mut ctx = BrowserContext::new("ctx_1", ContextConfig::default());
1807 ctx.ready();
1808 ctx.acquire();
1809 assert!(ctx.is_in_use());
1810 }
1811
1812 #[test]
1813 fn h0_ctx_45_context_release() {
1814 let mut ctx = BrowserContext::new("ctx_1", ContextConfig::default());
1815 ctx.ready();
1816 ctx.acquire();
1817 ctx.release();
1818 assert!(ctx.is_available());
1819 }
1820
1821 #[test]
1822 fn h0_ctx_46_context_close() {
1823 let mut ctx = BrowserContext::new("ctx_1", ContextConfig::default());
1824 ctx.close();
1825 assert!(ctx.is_closed());
1826 }
1827
1828 #[test]
1829 fn h0_ctx_47_context_error() {
1830 let mut ctx = BrowserContext::new("ctx_1", ContextConfig::default());
1831 ctx.set_error("Failed");
1832 assert_eq!(ctx.state, ContextState::Error);
1833 }
1834
1835 #[test]
1836 fn h0_ctx_48_context_page_count() {
1837 let ctx = BrowserContext::new("ctx_1", ContextConfig::default());
1838 assert_eq!(ctx.page_count(), 0);
1839 }
1840
1841 #[test]
1842 fn h0_ctx_49_context_new_page() {
1843 let ctx = BrowserContext::new("ctx_1", ContextConfig::default());
1844 let page_id = ctx.new_page();
1845 assert!(!page_id.is_empty());
1846 assert_eq!(ctx.page_count(), 1);
1847 }
1848
1849 #[test]
1850 fn h0_ctx_50_context_pool_new() {
1851 let pool = ContextPool::new(10);
1852 assert_eq!(pool.count(), 0);
1853 }
1854 }
1855}