testcontainers_modules/selenium/
mod.rs1use testcontainers::{
2 core::{ContainerPort, WaitFor},
3 Image,
4};
5
6const FIREFOX_IMAGE: &str = "selenium/standalone-firefox";
7const FIREFOX_TAG: &str = "144.0-geckodriver-0.36-grid-4.38.0-20251101";
8
9const CHROME_IMAGE: &str = "selenium/standalone-chrome";
10const CHROME_TAG: &str = "136.0-chromedriver-136.0-grid-4.38.0-20251101";
11
12pub const DRIVER_PORT: ContainerPort = ContainerPort::Tcp(4444);
14pub const WEB_UI_PORT: ContainerPort = ContainerPort::Tcp(7900);
16
17#[derive(Debug, Clone)]
48pub struct Selenium {
49 image: String,
50 tag: String,
51}
52
53impl Selenium {
54 pub fn new_firefox() -> Self {
58 Self {
59 image: FIREFOX_IMAGE.to_owned(),
60 tag: FIREFOX_TAG.to_owned(),
61 }
62 }
63
64 pub fn new_chrome() -> Self {
68 Self {
69 image: CHROME_IMAGE.to_owned(),
70 tag: CHROME_TAG.to_owned(),
71 }
72 }
73}
74
75impl Default for Selenium {
76 fn default() -> Self {
77 Self::new_firefox()
78 }
79}
80
81impl Image for Selenium {
82 fn name(&self) -> &str {
83 &self.image
84 }
85
86 fn tag(&self) -> &str {
87 &self.tag
88 }
89
90 fn ready_conditions(&self) -> Vec<WaitFor> {
91 vec![WaitFor::message_on_stdout("Started Selenium Standalone")]
92 }
93
94 fn expose_ports(&self) -> &[ContainerPort] {
95 &[DRIVER_PORT, WEB_UI_PORT]
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use testcontainers::runners::AsyncRunner;
102
103 use super::*;
104
105 #[tokio::test]
106 async fn selenium_firefox_check_status() -> Result<(), Box<dyn std::error::Error + 'static>> {
107 let selenium = Selenium::new_firefox();
108 check_status_impl(selenium).await?;
109 Ok(())
110 }
111
112 #[tokio::test]
113 async fn selenium_chrome_check_status() -> Result<(), Box<dyn std::error::Error + 'static>> {
114 let selenium = Selenium::new_chrome();
115 check_status_impl(selenium).await?;
116 Ok(())
117 }
118
119 async fn check_status_impl(
120 selenium: Selenium,
121 ) -> Result<(), Box<dyn std::error::Error + 'static>> {
122 let node = selenium.start().await?;
123
124 let driver_port = node.get_host_port_ipv4(DRIVER_PORT).await?;
125 let web_ui_port = node.get_host_port_ipv4(WEB_UI_PORT).await?;
126
127 let status_url = format!("http://127.0.0.1:{driver_port}/status");
129 let response = reqwest::get(&status_url).await?;
130 assert!(response.status().is_success());
131 let body = response.text().await?;
132 assert!(body.contains("\"ready\": true"));
133
134 let web_ui_url = format!("http://127.0.0.1:{web_ui_port}");
136 let response = reqwest::get(&web_ui_url).await?;
137 assert!(response.status().is_success());
138
139 Ok(())
140 }
141
142 #[tokio::test]
143 async fn selenium_firefox_run_js() -> Result<(), Box<dyn std::error::Error + 'static>> {
144 let node = Selenium::new_firefox().start().await?;
145 let driver_port = node.get_host_port_ipv4(DRIVER_PORT).await?;
146 let driver_url = format!("http://127.0.0.1:{driver_port}");
147
148 let client = fantoccini::ClientBuilder::native()
149 .connect(&driver_url)
150 .await?;
151
152 let result = client.execute("return 1 + 1", vec![]).await?;
153 let value = result.as_i64().unwrap();
154 assert_eq!(value, 2);
155
156 client.close().await?;
157
158 Ok(())
159 }
160}