1#![allow(missing_docs)]
21#![allow(clippy::disallowed_methods)]
23#![cfg_attr(test, allow(clippy::large_stack_arrays, clippy::large_stack_frames))]
26
27#[macro_use]
29#[allow(unused_macros, clippy::duplicated_attributes)]
30mod generated_contracts;
31
32#[allow(
36 clippy::missing_errors_doc,
37 clippy::must_use_candidate,
38 clippy::missing_const_for_fn,
39 clippy::doc_markdown
40)]
41pub mod brick;
42
43#[allow(
47 clippy::missing_errors_doc,
48 clippy::must_use_candidate,
49 clippy::missing_const_for_fn,
50 clippy::doc_markdown,
51 clippy::expect_used
52)]
53pub mod brick_house;
54
55#[allow(
56 clippy::suboptimal_flops,
57 clippy::cast_precision_loss,
58 clippy::struct_excessive_bools,
59 clippy::missing_errors_doc,
60 clippy::must_use_candidate,
61 clippy::missing_const_for_fn,
62 clippy::unnecessary_wraps,
63 clippy::doc_markdown
64)]
65mod accessibility;
66mod assertion;
67#[allow(
68 clippy::missing_errors_doc,
69 clippy::must_use_candidate,
70 clippy::missing_const_for_fn,
71 clippy::doc_markdown
72)]
73mod bridge;
74mod browser;
75#[cfg(feature = "browser")]
79mod chromium_driver;
80#[allow(
81 clippy::missing_errors_doc,
82 clippy::must_use_candidate,
83 clippy::missing_const_for_fn,
84 clippy::doc_markdown,
85 dead_code
86)]
87mod driver;
88mod event;
89mod fuzzer;
90mod harness;
91#[allow(
92 clippy::missing_errors_doc,
93 clippy::must_use_candidate,
94 clippy::missing_const_for_fn,
95 clippy::unnecessary_wraps,
96 clippy::doc_markdown
97)]
98mod locator;
99#[allow(
100 clippy::missing_errors_doc,
101 clippy::must_use_candidate,
102 clippy::missing_const_for_fn,
103 clippy::doc_markdown,
104 clippy::cast_precision_loss,
105 clippy::format_push_string,
106 clippy::needless_raw_string_hashes
107)]
108mod reporter;
109mod result;
110#[allow(
111 clippy::missing_errors_doc,
112 clippy::must_use_candidate,
113 clippy::missing_const_for_fn,
114 clippy::unnecessary_wraps,
115 clippy::doc_markdown,
116 clippy::if_not_else,
117 clippy::ptr_as_ptr,
118 clippy::expect_used,
119 unsafe_code
120)]
121mod runtime;
122mod simulation;
123mod snapshot;
124#[cfg(feature = "media")]
125mod visual_regression;
126
127#[allow(
131 clippy::missing_errors_doc,
132 clippy::must_use_candidate,
133 clippy::missing_const_for_fn,
134 clippy::doc_markdown
135)]
136pub mod lint;
137
138#[allow(
142 clippy::missing_errors_doc,
143 clippy::must_use_candidate,
144 clippy::missing_const_for_fn,
145 clippy::doc_markdown
146)]
147pub mod mock;
148
149#[allow(
153 clippy::missing_errors_doc,
154 clippy::must_use_candidate,
155 clippy::missing_const_for_fn,
156 clippy::doc_markdown
157)]
158pub mod comply;
159
160#[allow(
162 clippy::missing_errors_doc,
163 clippy::must_use_candidate,
164 clippy::missing_const_for_fn,
165 clippy::doc_markdown
166)]
167mod page_object;
168
169#[allow(
171 clippy::missing_errors_doc,
172 clippy::must_use_candidate,
173 clippy::missing_const_for_fn,
174 clippy::doc_markdown
175)]
176mod fixture;
177
178#[cfg(feature = "tui")]
180#[allow(
181 clippy::missing_errors_doc,
182 clippy::must_use_candidate,
183 clippy::missing_const_for_fn,
184 clippy::doc_markdown
185)]
186pub mod tui;
187
188#[allow(
193 clippy::missing_errors_doc,
194 clippy::must_use_candidate,
195 clippy::missing_const_for_fn,
196 clippy::doc_markdown
197)]
198pub mod tui_load;
199
200#[allow(
202 clippy::missing_errors_doc,
203 clippy::must_use_candidate,
204 clippy::missing_const_for_fn,
205 clippy::doc_markdown
206)]
207pub mod replay;
208
209#[allow(
211 clippy::missing_errors_doc,
212 clippy::must_use_candidate,
213 clippy::missing_const_for_fn,
214 clippy::doc_markdown
215)]
216pub mod ux_coverage;
217
218#[allow(
220 clippy::missing_errors_doc,
221 clippy::must_use_candidate,
222 clippy::missing_const_for_fn,
223 clippy::doc_markdown
224)]
225pub mod emulation;
226
227#[cfg(feature = "media")]
229#[allow(
230 clippy::missing_errors_doc,
231 clippy::must_use_candidate,
232 clippy::missing_const_for_fn,
233 clippy::doc_markdown,
234 clippy::cast_possible_truncation
235)]
236pub mod media;
237
238#[cfg(all(not(target_arch = "wasm32"), feature = "watch"))]
241#[allow(
242 clippy::missing_errors_doc,
243 clippy::must_use_candidate,
244 clippy::missing_const_for_fn,
245 clippy::doc_markdown
246)]
247pub mod watch;
248
249#[allow(
251 clippy::missing_errors_doc,
252 clippy::must_use_candidate,
253 clippy::missing_const_for_fn,
254 clippy::doc_markdown,
255 clippy::cast_possible_truncation
256)]
257pub mod tracing_support;
258
259#[allow(
261 clippy::missing_errors_doc,
262 clippy::must_use_candidate,
263 clippy::missing_const_for_fn,
264 clippy::doc_markdown
265)]
266pub mod network;
267
268#[allow(
270 clippy::missing_errors_doc,
271 clippy::must_use_candidate,
272 clippy::missing_const_for_fn,
273 clippy::doc_markdown
274)]
275pub mod wait;
276
277#[allow(
279 clippy::missing_errors_doc,
280 clippy::must_use_candidate,
281 clippy::missing_const_for_fn,
282 clippy::doc_markdown
283)]
284pub mod websocket;
285
286#[allow(
288 clippy::missing_errors_doc,
289 clippy::must_use_candidate,
290 clippy::missing_const_for_fn,
291 clippy::doc_markdown,
292 clippy::cast_possible_truncation
293)]
294pub mod performance;
295
296#[allow(
298 clippy::missing_errors_doc,
299 clippy::must_use_candidate,
300 clippy::missing_const_for_fn,
301 clippy::doc_markdown
302)]
303pub mod context;
304
305#[allow(
307 clippy::module_name_repetitions,
308 clippy::must_use_candidate,
309 clippy::missing_const_for_fn,
310 clippy::missing_errors_doc,
311 clippy::doc_markdown,
312 clippy::cast_possible_truncation,
313 clippy::cast_precision_loss,
314 clippy::use_self,
315 clippy::inline_always,
316 clippy::similar_names,
317 clippy::missing_panics_doc,
318 clippy::suboptimal_flops,
319 clippy::uninlined_format_args,
320 clippy::redundant_closure_for_method_calls
321)]
322pub mod coverage;
323
324#[allow(
326 clippy::missing_errors_doc,
327 clippy::must_use_candidate,
328 clippy::missing_const_for_fn,
329 clippy::doc_markdown
330)]
331pub mod web;
332
333#[cfg(feature = "media")]
337#[allow(
338 clippy::missing_errors_doc,
339 clippy::must_use_candidate,
340 clippy::missing_const_for_fn,
341 clippy::doc_markdown,
342 clippy::cast_precision_loss
343)]
344pub mod pixel_coverage;
345
346#[allow(
348 clippy::missing_errors_doc,
349 clippy::must_use_candidate,
350 clippy::missing_const_for_fn,
351 clippy::doc_markdown
352)]
353pub mod gpu_pixels;
354
355#[allow(
357 clippy::missing_errors_doc,
358 clippy::must_use_candidate,
359 clippy::missing_const_for_fn,
360 clippy::doc_markdown
361)]
362pub mod runner;
363
364#[allow(
366 clippy::missing_errors_doc,
367 clippy::must_use_candidate,
368 clippy::missing_const_for_fn,
369 clippy::doc_markdown,
370 clippy::cast_precision_loss
371)]
372pub mod perf;
373
374#[allow(
376 clippy::missing_errors_doc,
377 clippy::must_use_candidate,
378 clippy::missing_const_for_fn,
379 clippy::doc_markdown
380)]
381pub mod renacer_integration;
382
383#[allow(
385 clippy::missing_errors_doc,
386 clippy::must_use_candidate,
387 clippy::missing_const_for_fn,
388 clippy::doc_markdown
389)]
390pub mod cdp_coverage;
391
392#[allow(
394 clippy::missing_errors_doc,
395 clippy::must_use_candidate,
396 clippy::missing_const_for_fn,
397 clippy::doc_markdown
398)]
399pub mod shard;
400
401#[allow(
403 clippy::missing_errors_doc,
404 clippy::must_use_candidate,
405 clippy::missing_const_for_fn,
406 clippy::doc_markdown
407)]
408pub mod clock;
409
410#[allow(
412 clippy::missing_errors_doc,
413 clippy::must_use_candidate,
414 clippy::missing_const_for_fn,
415 clippy::doc_markdown
416)]
417pub mod capabilities;
418
419#[allow(
421 clippy::missing_errors_doc,
422 clippy::must_use_candidate,
423 clippy::missing_const_for_fn,
424 clippy::doc_markdown
425)]
426pub mod strict;
427
428#[allow(
430 clippy::missing_errors_doc,
431 clippy::must_use_candidate,
432 clippy::missing_const_for_fn,
433 clippy::doc_markdown
434)]
435pub mod validators;
436
437#[allow(
441 clippy::missing_errors_doc,
442 clippy::must_use_candidate,
443 clippy::missing_const_for_fn,
444 clippy::doc_markdown,
445 clippy::too_long_first_doc_paragraph
446)]
447pub mod zero_js;
448
449#[allow(
453 clippy::missing_errors_doc,
454 clippy::must_use_candidate,
455 clippy::missing_const_for_fn,
456 clippy::doc_markdown,
457 clippy::too_long_first_doc_paragraph
458)]
459pub mod worker_harness;
460
461#[cfg(feature = "docker")]
465#[allow(
466 clippy::missing_errors_doc,
467 clippy::must_use_candidate,
468 clippy::missing_const_for_fn,
469 clippy::doc_markdown,
470 clippy::too_long_first_doc_paragraph
471)]
472pub mod docker;
473
474#[allow(
476 clippy::missing_errors_doc,
477 clippy::must_use_candidate,
478 clippy::missing_const_for_fn,
479 clippy::doc_markdown
480)]
481pub mod dialog;
482
483#[allow(
485 clippy::missing_errors_doc,
486 clippy::must_use_candidate,
487 clippy::missing_const_for_fn,
488 clippy::doc_markdown
489)]
490pub mod file_ops;
491
492#[allow(
494 clippy::missing_docs_in_private_items,
495 clippy::missing_errors_doc,
496 clippy::must_use_candidate,
497 clippy::missing_const_for_fn,
498 clippy::doc_markdown
499)]
500pub mod har;
501
502#[allow(
505 clippy::missing_errors_doc,
506 clippy::must_use_candidate,
507 clippy::missing_const_for_fn,
508 clippy::doc_markdown,
509 clippy::expect_used,
510 clippy::many_single_char_names,
511 clippy::suspicious_operation_groupings,
512 missing_docs,
513 missing_debug_implementations
514)]
515pub mod playbook;
516
517#[allow(
519 clippy::missing_errors_doc,
520 clippy::must_use_candidate,
521 clippy::missing_const_for_fn,
522 clippy::doc_markdown
523)]
524pub mod av_sync;
525
526#[allow(
528 clippy::missing_errors_doc,
529 clippy::must_use_candidate,
530 clippy::missing_const_for_fn,
531 clippy::doc_markdown,
532 clippy::cast_precision_loss
533)]
534pub mod audio_quality;
535
536#[allow(
538 clippy::missing_errors_doc,
539 clippy::must_use_candidate,
540 clippy::missing_const_for_fn,
541 clippy::doc_markdown,
542 clippy::cast_precision_loss
543)]
544pub mod video_quality;
545
546#[allow(
548 clippy::missing_errors_doc,
549 clippy::must_use_candidate,
550 clippy::missing_const_for_fn,
551 clippy::doc_markdown
552)]
553pub mod animation;
554
555#[allow(
560 clippy::missing_errors_doc,
561 clippy::must_use_candidate,
562 clippy::missing_const_for_fn,
563 clippy::doc_markdown
564)]
565pub mod presentar;
566
567#[allow(
587 clippy::missing_errors_doc,
588 clippy::must_use_candidate,
589 clippy::missing_const_for_fn,
590 clippy::doc_markdown
591)]
592pub mod perf_gate;
593
594#[cfg(any(feature = "llm-types", feature = "llm"))]
599#[allow(
600 clippy::missing_errors_doc,
601 clippy::must_use_candidate,
602 clippy::missing_const_for_fn,
603 clippy::doc_markdown
604)]
605pub mod llm;
606
607pub use accessibility::{
608 AccessibilityAudit, AccessibilityConfig, AccessibilityIssue, AccessibilityValidator, Color,
609 ContrastAnalysis, ContrastPair, FlashDetector, FlashResult, FocusConfig, KeyboardIssue,
610 Severity, MIN_CONTRAST_LARGE, MIN_CONTRAST_NORMAL, MIN_CONTRAST_UI,
611};
612pub use animation::{
613 sample_easing, verify_easing, verify_events, verify_timeline, AnimationEvent,
614 AnimationEventType, AnimationReport, AnimationTimeline, AnimationVerdict, EasingFunction,
615 EasingVerification, EventResult, Keyframe, ObservedEvent,
616};
617pub use assertion::{
618 retry_contains, retry_eq, retry_none, retry_some, retry_true, Assertion, AssertionCheckResult,
619 AssertionFailure, AssertionMode, AssertionResult, AssertionSummary, EnergyVerifier,
620 EquationContext, EquationResult, EquationVerifier, InvariantVerifier, KinematicVerifier,
621 MomentumVerifier, RetryAssertion, RetryConfig, RetryError, RetryResult, SoftAssertionError,
622 SoftAssertions, Variable,
623};
624pub use audio_quality::{
625 analyze_audio, analyze_samples, detect_clipping, detect_silence, AudioLevels,
626 AudioQualityConfig, AudioQualityReport, AudioVerdict, ClippingReport, SilenceRegion,
627 SilenceReport,
628};
629pub use av_sync::{
630 compare_edl_to_onsets, default_edl_path, detect_onsets, extract_audio, AudioOnset,
631 AudioTickPlacement, AvSyncReport, DetectionConfig, EditDecision, EditDecisionList,
632 SegmentSyncResult, SyncVerdict, TickDelta, DEFAULT_SAMPLE_RATE,
633};
634pub use bridge::{
635 BridgeConnection, DiffRegion, EntitySnapshot, GameStateData, GameStateSnapshot, SnapshotCache,
636 StateBridge, VisualDiff,
637};
638pub use browser::{Browser, BrowserConfig, BrowserConsoleLevel, BrowserConsoleMessage, Page};
639pub use capabilities::{
640 CapabilityError, CapabilityStatus, RequiredHeaders, WasmThreadCapabilities, WorkerEmulator,
641 WorkerMessage, WorkerState,
642};
643pub use cdp_coverage::{
644 CoverageConfig, CoverageRange, CoverageReport, CoveredFunction, FunctionCoverage, JsCoverage,
645 LineCoverage, ScriptCoverage, SourceMapEntry, WasmCoverage, WasmSourceMap,
646};
647#[cfg(feature = "browser")]
648pub use chromium_driver::ChromiumDriver;
649pub use clock::{
650 create_clock, Clock, ClockController, ClockError, ClockOptions, ClockState, FakeClock,
651};
652pub use context::{
653 BrowserContext, ContextConfig, ContextManager, ContextPool, ContextPoolStats, ContextState,
654 Cookie, Geolocation, SameSite, StorageState,
655};
656pub use dialog::{
657 AutoDialogBehavior, Dialog, DialogAction, DialogExpectation, DialogHandler,
658 DialogHandlerBuilder, DialogType,
659};
660#[cfg(feature = "browser")]
661pub use driver::{BrowserController, ProbarDriver};
662pub use driver::{
663 DeviceDescriptor, DriverConfig, ElementHandle, MockDriver, NetworkInterceptor, NetworkResponse,
664 PageMetrics, Screenshot,
665};
666pub use event::{InputEvent, Touch, TouchAction};
667pub use file_ops::{
668 guess_mime_type, Download, DownloadManager, DownloadState, FileChooser, FileInput,
669};
670pub use fixture::{
671 Fixture, FixtureBuilder, FixtureManager, FixtureScope, FixtureState, SimpleFixture,
672};
673pub use fuzzer::{
674 FuzzerConfig, InputFuzzer, InvariantCheck, InvariantChecker, InvariantViolation, Seed,
675};
676pub use har::{
677 Har, HarBrowser, HarCache, HarContent, HarCookie, HarCreator, HarEntry, HarError, HarHeader,
678 HarLog, HarOptions, HarPlayer, HarPostData, HarPostParam, HarQueryParam, HarRecorder,
679 HarRequest, HarResponse, HarTimings, NotFoundBehavior,
680};
681pub use harness::{TestCase, TestHarness, TestResult, TestSuite};
682pub use locator::{
683 expect, BoundingBox, DragBuilder, DragOperation, Expect, ExpectAssertion, Locator,
684 LocatorAction, LocatorOptions, LocatorQuery, Point, Selector, DEFAULT_POLL_INTERVAL_MS,
685 DEFAULT_TIMEOUT_MS,
686};
687pub use network::{
688 CapturedRequest, HttpMethod, MockResponse, NetworkInterception, NetworkInterceptionBuilder,
689 Route, UrlPattern,
690};
691pub use page_object::{
692 PageObject, PageObjectBuilder, PageObjectInfo, PageRegistry, SimplePageObject, UrlMatcher,
693};
694pub use performance::{
695 Measurement, MetricStats, MetricType, PerformanceMonitor, PerformanceProfile,
696 PerformanceProfiler, PerformanceProfilerBuilder, PerformanceSummary, PerformanceThreshold,
697};
698pub use playbook::{
699 calculate_mutation_score, check_complexity_violation, to_dot, Action as PlaybookAction,
700 ActionExecutor, Assertion as PlaybookAssertion, AssertionFailure as PlaybookAssertionFailure,
701 ComplexityAnalyzer, ComplexityClass, ComplexityResult, DeterminismInfo,
702 ExecutionResult as PlaybookExecutionResult, ExecutorError, Invariant, IssueSeverity,
703 MutantResult, MutationClass, MutationGenerator, MutationScore, PerformanceBudget, Playbook,
704 PlaybookError, PlaybookExecutor, ReachabilityInfo, State as PlaybookState, StateMachine,
705 StateMachineValidator, Transition as PlaybookTransition, ValidationIssue, ValidationResult,
706 WaitCondition as PlaybookWaitCondition,
707};
708pub use presentar::{
709 generate_falsification_playbook, parse_and_validate as parse_and_validate_presentar,
710 validate_config as validate_presentar_config, Cell as PresentarCell, Color as PresentarColor,
711 FalsificationCheck, FalsificationResult, KeybindingConfig, LayoutConfig, PanelConfig,
712 PanelConfigs, PanelType, PresentarConfig, PresentarError, TerminalAssertion, TerminalSnapshot,
713 ThemeConfig, ValidationResult as PresentarValidationResult, FALSIFICATION_COUNT,
714 SCHEMA_VERSION,
715};
716pub use renacer_integration::{
717 ChromeTrace, ChromeTraceEvent, TraceCollector, TraceContext, TraceSpan,
718 TracingConfig as RenacerTracingConfig,
719};
720pub use replay::{
721 Replay, ReplayHeader, ReplayPlayer, ReplayRecorder, StateCheckpoint, TimedInput,
722 VerificationResult, REPLAY_FORMAT_VERSION,
723};
724pub use reporter::{
725 AndonCordPulled, FailureMode, Reporter, TestResultEntry, TestStatus, TraceData,
726};
727pub use result::{ProbarError, ProbarResult};
728pub use runtime::{
729 ComponentId, EntityId, FrameResult, GameHostState, MemoryView, ProbarComponent, ProbarEntity,
730 RuntimeConfig, StateDelta, WasmRuntime,
731};
732pub use shard::{ShardConfig, ShardParseError, ShardReport, ShardedRunner};
733pub use simulation::{
734 run_replay, run_simulation, RandomWalkAgent, RecordedFrame, ReplayResult, SimulatedGameState,
735 SimulationConfig, SimulationRecording,
736};
737pub use snapshot::{Snapshot, SnapshotConfig, SnapshotDiff};
738pub use strict::{
739 ChecklistError, ConsoleCapture, ConsoleSeverity, ConsoleValidationError, E2ETestChecklist,
740 WasmStrictMode,
741};
742pub use tracing_support::{
743 ConsoleLevel, ConsoleMessage, EventCategory, EventLevel, ExecutionTracer, NetworkEvent,
744 SpanStatus, TraceArchive, TraceMetadata, TracedEvent, TracedSpan, TracingConfig,
745};
746#[cfg(feature = "tui")]
747pub use tui::{
748 expect_frame, FrameAssertion, FrameSequence, MultiValueTracker, SnapshotManager, TuiFrame,
749 TuiSnapshot, TuiTestBackend, ValueTracker,
750};
751pub use tui_load::{
752 ComponentTimings, DataGenerator, IntegrationLoadTest, SyntheticItem, TuiFrameMetrics,
753 TuiLoadAssertion, TuiLoadConfig, TuiLoadError, TuiLoadResult, TuiLoadTest,
754};
755pub use ux_coverage::{
756 calculator_coverage, game_coverage, ElementCoverage, ElementId, InteractionType, StateId,
757 TrackedInteraction, UxCoverageBuilder, UxCoverageReport, UxCoverageTracker,
758};
759pub use validators::{
760 CompressionAlgorithm, PartialResult, ScreenshotContent, StateTransition, StreamingMetric,
761 StreamingMetricRecord, StreamingState, StreamingUxValidator, StreamingValidationError,
762 StreamingValidationResult, TestExecutionStats, VuMeterConfig, VuMeterError, VuMeterSample,
763};
764pub use video_quality::{
765 build_ffprobe_args, parse_ffprobe_json, probe_video, validate_video, VideoCheck,
766 VideoExpectations, VideoProbe, VideoQualityReport, VideoVerdict,
767};
768#[cfg(feature = "media")]
769pub use visual_regression::{
770 perceptual_diff, ImageDiffResult, MaskRegion, ScreenshotComparison, VisualRegressionConfig,
771 VisualRegressionTester,
772};
773pub use wait::{
774 wait_timeout, wait_until, FnCondition, LoadState, NavigationOptions, PageEvent, WaitCondition,
775 WaitOptions, WaitResult, Waiter, DEFAULT_WAIT_TIMEOUT_MS, NETWORK_IDLE_THRESHOLD_MS,
776};
777#[cfg(all(not(target_arch = "wasm32"), feature = "watch"))]
778pub use watch::{
779 FileChange, FileChangeKind, FileWatcher, FnWatchHandler, WatchBuilder, WatchConfig,
780 WatchHandler, WatchStats,
781};
782pub use brick::{
784 Brick, BrickAssertion, BrickBudget, BrickError, BrickPhase, BrickResult, BrickVerification,
785 BudgetViolation,
786};
787pub use brick::{
789 AudioBrick, AudioParam, BrickWorkerMessage, BrickWorkerMessageDirection, EventBinding,
790 EventBrick, EventHandler, EventType, FieldType, RingBufferConfig, WorkerBrick,
791 WorkerTransition,
792};
793pub use brick_house::{BrickHouse, BrickHouseBuilder, BrickTiming, BudgetReport, JidokaAlert};
794pub use websocket::{
795 MessageDirection, MessageType, MockWebSocketResponse, WebSocketConnection, WebSocketMessage,
796 WebSocketMock, WebSocketMonitor, WebSocketMonitorBuilder, WebSocketState,
797};
798
799pub mod prelude {
801 pub use super::accessibility::*;
802 pub use super::animation::{
803 sample_easing, verify_easing, verify_events, verify_timeline, AnimationEvent,
804 AnimationEventType, AnimationReport, AnimationTimeline, AnimationVerdict, EasingFunction,
805 EasingVerification, EventResult, Keyframe, ObservedEvent,
806 };
807 pub use super::assertion::*;
808 pub use super::audio_quality::{
809 analyze_audio, analyze_samples, detect_clipping, detect_silence, AudioLevels,
810 AudioQualityConfig, AudioQualityReport, AudioVerdict, ClippingReport, SilenceRegion,
811 SilenceReport,
812 };
813 pub use super::av_sync::{
814 compare_edl_to_onsets, default_edl_path, detect_onsets, extract_audio, AudioOnset,
815 AudioTickPlacement, AvSyncReport, DetectionConfig, EditDecision, EditDecisionList,
816 SegmentSyncResult, SyncVerdict, TickDelta,
817 };
818 pub use super::video_quality::{
819 build_ffprobe_args, parse_ffprobe_json, probe_video, validate_video, VideoCheck,
820 VideoExpectations, VideoProbe, VideoQualityReport, VideoVerdict,
821 };
822 pub use super::brick::*;
824 pub use super::brick_house::*;
825 pub use super::bridge::*;
826 pub use super::browser::*;
827 pub use super::capabilities::*;
828 pub use super::clock::*;
829 pub use super::context::*;
830 pub use super::dialog::*;
831 pub use super::driver::*;
832 pub use super::event::*;
833 pub use super::file_ops::*;
834 pub use super::fixture::*;
835 pub use super::fuzzer::*;
836 pub use super::gpu_pixels::*;
837 pub use super::har::*;
838 pub use super::harness::*;
839 pub use super::locator::*;
840 pub use super::network::*;
841 pub use super::page_object::*;
842 pub use super::perf::*;
843 pub use super::performance::*;
844 #[cfg(feature = "media")]
845 pub use super::pixel_coverage::*;
846 pub use super::replay::*;
847 pub use super::reporter::*;
848 pub use super::result::*;
849 pub use super::runner::*;
850 pub use super::runtime::*;
851 pub use super::shard::*;
852 pub use super::simulation::*;
853 pub use super::snapshot::*;
854 pub use super::strict::{
857 ChecklistError, ConsoleCapture, ConsoleSeverity, ConsoleValidationError, E2ETestChecklist,
858 WasmStrictMode,
859 };
860 pub use super::tracing_support::*;
861 #[cfg(feature = "tui")]
862 pub use super::tui::*;
863 pub use super::tui_load::{
864 ComponentTimings, DataGenerator, IntegrationLoadTest, SyntheticItem, TuiFrameMetrics,
865 TuiLoadAssertion, TuiLoadConfig, TuiLoadError, TuiLoadResult, TuiLoadTest,
866 };
867 pub use super::ux_coverage::*;
868 pub use super::validators::*;
869 #[cfg(feature = "media")]
870 pub use super::visual_regression::*;
871 pub use super::worker_harness::*;
872 pub use super::zero_js::*;
873 pub use super::comply::*;
875 pub use super::lint::*;
876 pub use super::mock::*;
877 #[cfg(feature = "docker")]
879 pub use super::docker::{
880 check_shared_array_buffer_support, validate_coop_coep_headers, Browser as DockerBrowser,
881 ContainerConfig, ContainerState, CoopCoepConfig, DockerConfig, DockerError, DockerResult,
882 DockerTestRunner, DockerTestRunnerBuilder, ParallelRunner, ParallelRunnerBuilder,
883 TestResult as DockerTestResult, TestResults as DockerTestResults,
884 };
885 #[cfg(feature = "llm")]
886 pub use super::llm::*;
887 pub use super::wait::{
888 wait_timeout, wait_until, FnCondition, LoadState, NavigationOptions, PageEvent,
889 WaitCondition, WaitOptions, WaitResult, Waiter, DEFAULT_WAIT_TIMEOUT_MS,
890 NETWORK_IDLE_THRESHOLD_MS,
891 };
892 #[cfg(all(not(target_arch = "wasm32"), feature = "watch"))]
893 pub use super::watch::*;
894 pub use super::web::*;
895 pub use super::websocket::*;
896 pub use super::renacer_integration::{
899 ChromeTrace as RenacerChromeTrace, ChromeTraceEvent, TraceCollector, TraceContext,
900 TraceSpan, TracingConfig as RenacerTracingConfig,
901 };
902}
903
904pub mod standard_invariants {
906 pub use super::fuzzer::standard_invariants::*;
907}
908
909#[cfg(feature = "derive")]
911pub use jugar_probar_derive::{probar_test, ProbarComponent, ProbarEntity, ProbarSelector};
912
913#[cfg(test)]
914#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
915mod tests {
916 use super::*;
917
918 mod browser_tests {
923 use super::*;
924
925 #[test]
926 fn test_browser_config_defaults() {
927 let config = BrowserConfig::default();
928 assert!(config.headless);
929 assert_eq!(config.viewport_width, 800);
930 assert_eq!(config.viewport_height, 600);
931 }
932
933 #[test]
934 fn test_browser_config_builder() {
935 let config = BrowserConfig::default()
936 .with_viewport(1024, 768)
937 .with_headless(false);
938 assert!(!config.headless);
939 assert_eq!(config.viewport_width, 1024);
940 assert_eq!(config.viewport_height, 768);
941 }
942 }
943
944 mod touch_tests {
945 use super::*;
946
947 #[test]
948 fn test_touch_tap() {
949 let touch = Touch::tap(100.0, 200.0);
950 assert!((touch.x - 100.0).abs() < f32::EPSILON);
951 assert!((touch.y - 200.0).abs() < f32::EPSILON);
952 assert!(matches!(touch.action, TouchAction::Tap));
953 }
954
955 #[test]
956 fn test_touch_swipe() {
957 let touch = Touch::swipe(0.0, 0.0, 100.0, 0.0, 300);
958 assert!(matches!(touch.action, TouchAction::Swipe { .. }));
959 }
960
961 #[test]
962 fn test_touch_hold() {
963 let touch = Touch::hold(50.0, 50.0, 500);
964 assert!(matches!(touch.action, TouchAction::Hold { .. }));
965 }
966 }
967
968 mod assertion_tests {
969 use super::*;
970
971 #[test]
972 fn test_assertion_result_pass() {
973 let result = AssertionResult::pass();
974 assert!(result.passed);
975 assert!(result.message.is_empty());
976 }
977
978 #[test]
979 fn test_assertion_result_fail() {
980 let result = AssertionResult::fail("test error message");
981 assert!(!result.passed);
982 assert_eq!(result.message, "test error message");
983 }
984
985 #[test]
986 fn test_assertion_equals_pass() {
987 let result = Assertion::equals(&42, &42);
988 assert!(result.passed);
989 }
990
991 #[test]
992 fn test_assertion_equals_fail() {
993 let result = Assertion::equals(&42, &43);
994 assert!(!result.passed);
995 assert!(result.message.contains("expected"));
996 }
997
998 #[test]
999 fn test_assertion_contains_pass() {
1000 let result = Assertion::contains("hello world", "world");
1001 assert!(result.passed);
1002 }
1003
1004 #[test]
1005 fn test_assertion_contains_fail() {
1006 let result = Assertion::contains("hello world", "foo");
1007 assert!(!result.passed);
1008 assert!(result.message.contains("contain"));
1009 }
1010
1011 #[test]
1012 fn test_assertion_in_range_pass() {
1013 let result = Assertion::in_range(5.0, 0.0, 10.0);
1014 assert!(result.passed);
1015 }
1016
1017 #[test]
1018 fn test_assertion_in_range_fail() {
1019 let result = Assertion::in_range(15.0, 0.0, 10.0);
1020 assert!(!result.passed);
1021 assert!(result.message.contains("range"));
1022 }
1023
1024 #[test]
1025 fn test_assertion_in_range_at_boundaries() {
1026 let result = Assertion::in_range(0.0, 0.0, 10.0);
1028 assert!(result.passed);
1029 let result = Assertion::in_range(10.0, 0.0, 10.0);
1031 assert!(result.passed);
1032 }
1033
1034 #[test]
1035 fn test_assertion_is_true_pass() {
1036 let result = Assertion::is_true(true, "should be true");
1037 assert!(result.passed);
1038 }
1039
1040 #[test]
1041 fn test_assertion_is_true_fail() {
1042 let result = Assertion::is_true(false, "expected true");
1043 assert!(!result.passed);
1044 assert_eq!(result.message, "expected true");
1045 }
1046
1047 #[test]
1048 fn test_assertion_is_false_pass() {
1049 let result = Assertion::is_false(false, "should be false");
1050 assert!(result.passed);
1051 }
1052
1053 #[test]
1054 fn test_assertion_is_false_fail() {
1055 let result = Assertion::is_false(true, "expected false");
1056 assert!(!result.passed);
1057 assert_eq!(result.message, "expected false");
1058 }
1059
1060 #[test]
1061 fn test_assertion_is_some_pass() {
1062 let opt = Some(42);
1063 let result = Assertion::is_some(&opt);
1064 assert!(result.passed);
1065 }
1066
1067 #[test]
1068 fn test_assertion_is_some_fail() {
1069 let opt: Option<i32> = None;
1070 let result = Assertion::is_some(&opt);
1071 assert!(!result.passed);
1072 assert!(result.message.contains("None"));
1073 }
1074
1075 #[test]
1076 fn test_assertion_is_none_pass() {
1077 let opt: Option<i32> = None;
1078 let result = Assertion::is_none(&opt);
1079 assert!(result.passed);
1080 }
1081
1082 #[test]
1083 fn test_assertion_is_none_fail() {
1084 let opt = Some(42);
1085 let result = Assertion::is_none(&opt);
1086 assert!(!result.passed);
1087 assert!(result.message.contains("Some"));
1088 }
1089
1090 #[test]
1091 fn test_assertion_is_ok_pass() {
1092 let res: Result<i32, &str> = Ok(42);
1093 let result = Assertion::is_ok(&res);
1094 assert!(result.passed);
1095 }
1096
1097 #[test]
1098 fn test_assertion_is_ok_fail() {
1099 let res: Result<i32, &str> = Err("error");
1100 let result = Assertion::is_ok(&res);
1101 assert!(!result.passed);
1102 assert!(result.message.contains("Err"));
1103 }
1104
1105 #[test]
1106 fn test_assertion_is_err_pass() {
1107 let res: Result<i32, &str> = Err("error");
1108 let result = Assertion::is_err(&res);
1109 assert!(result.passed);
1110 }
1111
1112 #[test]
1113 fn test_assertion_is_err_fail() {
1114 let res: Result<i32, &str> = Ok(42);
1115 let result = Assertion::is_err(&res);
1116 assert!(!result.passed);
1117 assert!(result.message.contains("Ok"));
1118 }
1119
1120 #[test]
1121 fn test_assertion_approx_eq_pass() {
1122 let result = Assertion::approx_eq(1.0, 1.0001, 0.01);
1123 assert!(result.passed);
1124 }
1125
1126 #[test]
1127 fn test_assertion_approx_eq_fail() {
1128 let result = Assertion::approx_eq(1.0, 2.0, 0.01);
1129 assert!(!result.passed);
1130 assert!(result.message.contains("≈"));
1131 }
1132
1133 #[test]
1134 fn test_assertion_has_length_pass() {
1135 let data = vec![1, 2, 3, 4, 5];
1136 let result = Assertion::has_length(&data, 5);
1137 assert!(result.passed);
1138 }
1139
1140 #[test]
1141 fn test_assertion_has_length_fail() {
1142 let data = vec![1, 2, 3];
1143 let result = Assertion::has_length(&data, 5);
1144 assert!(!result.passed);
1145 assert!(result.message.contains("length"));
1146 }
1147
1148 #[test]
1149 fn test_assertion_has_length_empty() {
1150 let data: Vec<i32> = vec![];
1151 let result = Assertion::has_length(&data, 0);
1152 assert!(result.passed);
1153 }
1154 }
1155
1156 mod snapshot_tests {
1157 use super::*;
1158
1159 #[test]
1160 fn test_snapshot_creation() {
1161 let snapshot = Snapshot::new("test-snapshot", vec![0, 1, 2, 3]);
1162 assert_eq!(snapshot.name, "test-snapshot");
1163 assert_eq!(snapshot.data.len(), 4);
1164 assert_eq!(snapshot.width, 0);
1165 assert_eq!(snapshot.height, 0);
1166 }
1167
1168 #[test]
1169 fn test_snapshot_with_dimensions() {
1170 let snapshot = Snapshot::new("test", vec![1, 2, 3, 4]).with_dimensions(800, 600);
1171 assert_eq!(snapshot.width, 800);
1172 assert_eq!(snapshot.height, 600);
1173 }
1174
1175 #[test]
1176 fn test_snapshot_size() {
1177 let snapshot = Snapshot::new("test", vec![1, 2, 3, 4, 5]);
1178 assert_eq!(snapshot.size(), 5);
1179 }
1180
1181 #[test]
1182 fn test_snapshot_diff_identical() {
1183 let snap1 = Snapshot::new("test", vec![1, 2, 3]);
1184 let snap2 = Snapshot::new("test", vec![1, 2, 3]);
1185 let diff = snap1.diff(&snap2);
1186 assert!(diff.is_identical());
1187 assert_eq!(diff.difference_count, 0);
1188 assert!((diff.difference_percent - 0.0).abs() < f64::EPSILON);
1189 }
1190
1191 #[test]
1192 fn test_snapshot_diff_different() {
1193 let snap1 = Snapshot::new("test", vec![1, 2, 3]);
1194 let snap2 = Snapshot::new("test", vec![1, 2, 4]);
1195 let diff = snap1.diff(&snap2);
1196 assert!(!diff.is_identical());
1197 assert_eq!(diff.difference_count, 1);
1198 }
1199
1200 #[test]
1201 fn test_snapshot_diff_empty() {
1202 let snap1 = Snapshot::new("test", vec![]);
1203 let snap2 = Snapshot::new("test", vec![]);
1204 let diff = snap1.diff(&snap2);
1205 assert!(diff.is_identical());
1206 assert!((diff.difference_percent - 0.0).abs() < f64::EPSILON);
1207 }
1208
1209 #[test]
1210 fn test_snapshot_diff_different_lengths() {
1211 let snap1 = Snapshot::new("test", vec![1, 2, 3]);
1212 let snap2 = Snapshot::new("test", vec![1, 2, 3, 4, 5]);
1213 let diff = snap1.diff(&snap2);
1214 assert!(!diff.is_identical());
1215 assert_eq!(diff.difference_count, 2);
1217 }
1218
1219 #[test]
1220 fn test_snapshot_diff_within_threshold() {
1221 let snap1 = Snapshot::new("test", vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
1222 let snap2 = Snapshot::new("test", vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 11]);
1223 let diff = snap1.diff(&snap2);
1224 assert!(diff.within_threshold(0.1)); assert!(!diff.within_threshold(0.05)); }
1228
1229 #[test]
1230 fn test_snapshot_config_default() {
1231 let config = SnapshotConfig::default();
1232 assert!(!config.update_snapshots);
1233 assert!((config.threshold - 0.01).abs() < f64::EPSILON);
1234 assert_eq!(config.snapshot_dir, "__snapshots__");
1235 }
1236
1237 #[test]
1238 fn test_snapshot_config_with_update() {
1239 let config = SnapshotConfig::default().with_update(true);
1240 assert!(config.update_snapshots);
1241 }
1242
1243 #[test]
1244 fn test_snapshot_config_with_threshold() {
1245 let config = SnapshotConfig::default().with_threshold(0.05);
1246 assert!((config.threshold - 0.05).abs() < f64::EPSILON);
1247 }
1248
1249 #[test]
1250 fn test_snapshot_config_with_dir() {
1251 let config = SnapshotConfig::default().with_dir("custom_snapshots");
1252 assert_eq!(config.snapshot_dir, "custom_snapshots");
1253 }
1254
1255 #[test]
1256 fn test_snapshot_config_chained_builders() {
1257 let config = SnapshotConfig::default()
1258 .with_update(true)
1259 .with_threshold(0.02)
1260 .with_dir("my_snaps");
1261 assert!(config.update_snapshots);
1262 assert!((config.threshold - 0.02).abs() < f64::EPSILON);
1263 assert_eq!(config.snapshot_dir, "my_snaps");
1264 }
1265 }
1266
1267 mod harness_tests {
1268 use super::*;
1269 use harness::{SuiteResults, TestCase};
1270 use std::time::Duration;
1271
1272 #[test]
1273 fn test_test_suite_creation() {
1274 let suite = TestSuite::new("Game Tests");
1275 assert_eq!(suite.name, "Game Tests");
1276 assert!(suite.tests.is_empty());
1277 }
1278
1279 #[test]
1280 fn test_test_suite_add_test() {
1281 let mut suite = TestSuite::new("Suite");
1282 suite.add_test(TestCase::new("test1"));
1283 suite.add_test(TestCase::new("test2"));
1284 assert_eq!(suite.test_count(), 2);
1285 }
1286
1287 #[test]
1288 fn test_test_case_creation() {
1289 let case = TestCase::new("my_test");
1290 assert_eq!(case.name, "my_test");
1291 assert_eq!(case.timeout_ms, 30000); }
1293
1294 #[test]
1295 fn test_test_case_with_timeout() {
1296 let case = TestCase::new("my_test").with_timeout(5000);
1297 assert_eq!(case.timeout_ms, 5000);
1298 }
1299
1300 #[test]
1301 fn test_test_result_pass() {
1302 let result = TestResult::pass("test_example");
1303 assert!(result.passed);
1304 assert_eq!(result.name, "test_example");
1305 assert!(result.error.is_none());
1306 assert_eq!(result.duration, Duration::ZERO);
1307 }
1308
1309 #[test]
1310 fn test_test_result_fail() {
1311 let result = TestResult::fail("test_example", "assertion failed");
1312 assert!(!result.passed);
1313 assert!(result.error.is_some());
1314 assert_eq!(result.error.unwrap(), "assertion failed");
1315 }
1316
1317 #[test]
1318 fn test_test_result_with_duration() {
1319 let result = TestResult::pass("test").with_duration(Duration::from_millis(100));
1320 assert_eq!(result.duration, Duration::from_millis(100));
1321 }
1322
1323 #[test]
1324 fn test_suite_results_all_passed() {
1325 let results = SuiteResults {
1326 suite_name: "test".to_string(),
1327 results: vec![TestResult::pass("test1"), TestResult::pass("test2")],
1328 duration: Duration::ZERO,
1329 };
1330 assert!(results.all_passed());
1331 }
1332
1333 #[test]
1334 fn test_suite_results_not_all_passed() {
1335 let results = SuiteResults {
1336 suite_name: "test".to_string(),
1337 results: vec![
1338 TestResult::pass("test1"),
1339 TestResult::fail("test2", "error"),
1340 ],
1341 duration: Duration::ZERO,
1342 };
1343 assert!(!results.all_passed());
1344 }
1345
1346 #[test]
1347 fn test_suite_results_counts() {
1348 let results = SuiteResults {
1349 suite_name: "test".to_string(),
1350 results: vec![
1351 TestResult::pass("test1"),
1352 TestResult::fail("test2", "error"),
1353 TestResult::pass("test3"),
1354 ],
1355 duration: Duration::ZERO,
1356 };
1357 assert_eq!(results.passed_count(), 2);
1358 assert_eq!(results.failed_count(), 1);
1359 assert_eq!(results.total(), 3);
1360 }
1361
1362 #[test]
1363 fn test_suite_results_failures() {
1364 let results = SuiteResults {
1365 suite_name: "test".to_string(),
1366 results: vec![
1367 TestResult::pass("test1"),
1368 TestResult::fail("test2", "error2"),
1369 TestResult::fail("test3", "error3"),
1370 ],
1371 duration: Duration::ZERO,
1372 };
1373 let failures = results.failures();
1374 assert_eq!(failures.len(), 2);
1375 assert_eq!(failures[0].name, "test2");
1376 assert_eq!(failures[1].name, "test3");
1377 }
1378
1379 #[test]
1380 fn test_harness_run_empty_suite() {
1381 let harness = TestHarness::new();
1382 let suite = TestSuite::new("Empty");
1383 let results = harness.run(&suite);
1384 assert!(results.all_passed());
1385 assert_eq!(results.total(), 0);
1386 }
1387
1388 #[test]
1389 fn test_harness_with_fail_fast() {
1390 let harness = TestHarness::new().with_fail_fast();
1391 assert!(harness.fail_fast);
1392 }
1393
1394 #[test]
1395 fn test_harness_with_parallel() {
1396 let harness = TestHarness::new().with_parallel();
1397 assert!(harness.parallel);
1398 }
1399
1400 #[test]
1401 fn test_harness_default() {
1402 let harness = TestHarness::default();
1403 assert!(!harness.fail_fast);
1404 assert!(!harness.parallel);
1405 }
1406 }
1407
1408 mod input_event_tests {
1409 use super::*;
1410
1411 #[test]
1412 fn test_input_event_touch() {
1413 let event = InputEvent::touch(100.0, 200.0);
1414 assert!(
1415 matches!(event, InputEvent::Touch { x, y } if (x - 100.0).abs() < f32::EPSILON && (y - 200.0).abs() < f32::EPSILON)
1416 );
1417 }
1418
1419 #[test]
1420 fn test_input_event_key_press() {
1421 let event = InputEvent::key_press("ArrowUp");
1422 assert!(matches!(event, InputEvent::KeyPress { key } if key == "ArrowUp"));
1423 }
1424
1425 #[test]
1426 fn test_input_event_key_release() {
1427 let event = InputEvent::key_release("Space");
1428 assert!(matches!(event, InputEvent::KeyRelease { key } if key == "Space"));
1429 }
1430
1431 #[test]
1432 fn test_input_event_mouse_click() {
1433 let event = InputEvent::mouse_click(50.0, 75.0);
1434 assert!(
1435 matches!(event, InputEvent::MouseClick { x, y } if (x - 50.0).abs() < f32::EPSILON && (y - 75.0).abs() < f32::EPSILON)
1436 );
1437 }
1438
1439 #[test]
1440 fn test_input_event_mouse_move() {
1441 let event = InputEvent::mouse_move(150.0, 250.0);
1442 assert!(
1443 matches!(event, InputEvent::MouseMove { x, y } if (x - 150.0).abs() < f32::EPSILON && (y - 250.0).abs() < f32::EPSILON)
1444 );
1445 }
1446
1447 #[test]
1448 fn test_input_event_gamepad_button_pressed() {
1449 let event = InputEvent::gamepad_button(0, true);
1450 assert!(matches!(
1451 event,
1452 InputEvent::GamepadButton {
1453 button: 0,
1454 pressed: true
1455 }
1456 ));
1457 }
1458
1459 #[test]
1460 fn test_input_event_gamepad_button_released() {
1461 let event = InputEvent::gamepad_button(1, false);
1462 assert!(matches!(
1463 event,
1464 InputEvent::GamepadButton {
1465 button: 1,
1466 pressed: false
1467 }
1468 ));
1469 }
1470
1471 #[test]
1472 fn test_touch_tap_coordinates() {
1473 let touch = Touch::tap(100.0, 200.0);
1474 assert!((touch.x - 100.0).abs() < f32::EPSILON);
1475 assert!((touch.y - 200.0).abs() < f32::EPSILON);
1476 assert!(matches!(touch.action, TouchAction::Tap));
1477 }
1478
1479 #[test]
1480 fn test_touch_swipe_full_properties() {
1481 let touch = Touch::swipe(10.0, 20.0, 100.0, 200.0, 300);
1482 assert!((touch.x - 10.0).abs() < f32::EPSILON);
1483 assert!((touch.y - 20.0).abs() < f32::EPSILON);
1484 match touch.action {
1485 TouchAction::Swipe {
1486 end_x,
1487 end_y,
1488 duration_ms,
1489 } => {
1490 assert!((end_x - 100.0).abs() < f32::EPSILON);
1491 assert!((end_y - 200.0).abs() < f32::EPSILON);
1492 assert_eq!(duration_ms, 300);
1493 }
1494 _ => panic!("expected Swipe action"),
1495 }
1496 }
1497
1498 #[test]
1499 fn test_touch_hold_full_properties() {
1500 let touch = Touch::hold(50.0, 60.0, 500);
1501 assert!((touch.x - 50.0).abs() < f32::EPSILON);
1502 assert!((touch.y - 60.0).abs() < f32::EPSILON);
1503 match touch.action {
1504 TouchAction::Hold { duration_ms } => {
1505 assert_eq!(duration_ms, 500);
1506 }
1507 _ => panic!("expected Hold action"),
1508 }
1509 }
1510
1511 #[test]
1512 fn test_touch_action_equality() {
1513 assert_eq!(TouchAction::Tap, TouchAction::Tap);
1514 let swipe1 = TouchAction::Swipe {
1515 end_x: 1.0,
1516 end_y: 2.0,
1517 duration_ms: 100,
1518 };
1519 let swipe2 = TouchAction::Swipe {
1520 end_x: 1.0,
1521 end_y: 2.0,
1522 duration_ms: 100,
1523 };
1524 assert_eq!(swipe1, swipe2);
1525 let hold1 = TouchAction::Hold { duration_ms: 500 };
1526 let hold2 = TouchAction::Hold { duration_ms: 500 };
1527 assert_eq!(hold1, hold2);
1528 }
1529
1530 #[test]
1531 fn test_touch_equality() {
1532 let t1 = Touch::tap(100.0, 200.0);
1533 let t2 = Touch::tap(100.0, 200.0);
1534 assert_eq!(t1, t2);
1535 }
1536
1537 #[test]
1538 fn test_input_event_equality() {
1539 let e1 = InputEvent::touch(10.0, 20.0);
1540 let e2 = InputEvent::touch(10.0, 20.0);
1541 assert_eq!(e1, e2);
1542 }
1543 }
1544
1545 mod error_tests {
1546 use super::*;
1547
1548 #[test]
1549 fn test_probar_error_display() {
1550 let err = ProbarError::BrowserNotFound;
1551 let msg = err.to_string();
1552 assert!(msg.contains("browser") || msg.contains("Browser"));
1553 }
1554
1555 #[test]
1556 fn test_probar_error_timeout() {
1557 let err = ProbarError::Timeout { ms: 5000 };
1558 let msg = err.to_string();
1559 assert!(msg.contains("5000"));
1560 }
1561 }
1562}