Skip to content

Commit 196d0d6

Browse files
mhanberg401matthallamclainJohnny OtsukaJohn Cotton
authored
add check for chromedriver chrome version match (#688)
* Fixes #685 When google chrome and chromedriver versions are out of sync wallaby can start in a bad state. We added a version comparison step to `Wallaby.Chrome.validate/0` that will error during application start if chrome and chrome versions do not match to a major, minor, and build version. Co-authored-by: Alex McLain <[email protected]> Co-authored-by: Johnny Otsuka <[email protected]> Co-authored-by: John Cotton <[email protected]> Co-authored-by: Liam Elder <[email protected]> * Use correct config option in DependencyError To configure the Chrome binary, you use the `:chromdriver` option and set it to `[binary: "/path/to/chrome"]` * Emit a warning instead of raising an error Sometimes having a mismatched chromedriver/chrome version still works, and having this error might inconvenience the developer. Emitting a warning I think is sufficient to alert the developer as to a potential problem. This also fixes some incorrect config value. The path to an alternative chrome binary is found is found with `Application.get_env(:wallaby, :chromedriver)[:binary]`, not `Application.get_env(:wallaby, :chrome)[:path]`. Co-authored-by: Matt Hall <[email protected]> Co-authored-by: Alex McLain <[email protected]> Co-authored-by: Johnny Otsuka <[email protected]> Co-authored-by: John Cotton <[email protected]> Co-authored-by: Liam Elder <[email protected]>
1 parent c34481b commit 196d0d6

File tree

6 files changed

+239
-53
lines changed

6 files changed

+239
-53
lines changed

integration_test/cases/browser/assert_refute_has_test.exs

+8-7
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,14 @@ defmodule Wallaby.Integration.Browser.AssertRefuteHasTest do
3535
end
3636

3737
test "mentions the count of found vs. expected index", %{session: session} do
38-
assert_raise ExpectationNotMetError,
39-
~r/Expected.*and return element at index 7 but only 6 visible elements were found/i,
40-
fn ->
41-
session
42-
|> visit("nesting.html")
43-
|> assert_has(@wrong_at_query)
44-
end
38+
expect =
39+
~r/Expected.*and return element at index 7, but only 6 visible elements were found/i
40+
41+
assert_raise ExpectationNotMetError, expect, fn ->
42+
session
43+
|> visit("nesting.html")
44+
|> assert_has(@wrong_at_query)
45+
end
4546
end
4647

4748
test "passes if `at` element exists", %{session: session} do
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
defmodule Wallaby.Integration.Browser.WindowPositionTest do
22
use Wallaby.Integration.SessionCase, async: true
33

4-
test "getting the window position", %{session: session} do
5-
window_position =
6-
session
7-
|> visit("/")
8-
|> move_window(100, 200)
9-
|> window_position()
4+
# this test dows not return the right values on mac
5+
# reason is unclear, I think it's a bug in chromedriver on mac
6+
if :os.type() != {:unix, :darwin} do
7+
test "getting the window position", %{session: session} do
8+
window_position =
9+
session
10+
|> visit("/")
11+
|> move_window(100, 200)
12+
|> window_position()
1013

11-
assert %{"x" => 100, "y" => 200} = window_position
14+
assert %{"x" => 100, "y" => 200} = window_position
15+
end
1216
end
1317
end

integration_test/chrome/starting_sessions_test.exs

+43-14
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
defmodule Wallaby.Integration.Chrome.StartingSessionsTest do
22
use ExUnit.Case, async: false
33

4+
import ExUnit.CaptureIO
5+
46
import Wallaby.SettingsTestHelpers
57
import Wallaby.TestSupport.ApplicationControl
68
import Wallaby.TestSupport.TestScriptUtils
@@ -23,7 +25,7 @@ defmodule Wallaby.Integration.Chrome.StartingSessionsTest do
2325

2426
test_script_path =
2527
chromedriver_path
26-
|> ChromeTestScript.build_wrapper_script()
28+
|> ChromeTestScript.build_chromedriver_wrapper_script()
2729
|> write_test_script!(workspace_path)
2830

2931
ensure_setting_is_reset(:wallaby, :chromedriver)
@@ -41,7 +43,7 @@ defmodule Wallaby.Integration.Chrome.StartingSessionsTest do
4143

4244
test_script_path =
4345
chromedriver_path
44-
|> ChromeTestScript.build_wrapper_script()
46+
|> ChromeTestScript.build_chromedriver_wrapper_script()
4547
|> write_test_script!(workspace_path)
4648

4749
ensure_setting_is_reset(:wallaby, :chromedriver)
@@ -96,7 +98,7 @@ defmodule Wallaby.Integration.Chrome.StartingSessionsTest do
9698
workspace_path: workspace_path
9799
} do
98100
test_script_path =
99-
ChromeTestScript.build_version_mock_script(version: "2.29")
101+
ChromeTestScript.build_chromedriver_version_mock_script(version: "2.29")
100102
|> write_test_script!(workspace_path)
101103

102104
ensure_setting_is_reset(:wallaby, :chromedriver)
@@ -105,17 +107,48 @@ defmodule Wallaby.Integration.Chrome.StartingSessionsTest do
105107
assert {:error, _} = Application.start(:wallaby)
106108
end
107109

108-
test "application starts when chromedriver version >= 2.30", %{
110+
test "application starts when chromedriver version >= 2.30", %{workspace_path: workspace_path} do
111+
chromedriver_test_script_path =
112+
ChromeTestScript.build_chromedriver_version_mock_script(version: "2.30")
113+
|> write_test_script!(workspace_path)
114+
115+
chrome_test_script_path =
116+
ChromeTestScript.build_chrome_version_mock_script(version: "2.30")
117+
|> write_test_script!(workspace_path)
118+
119+
ensure_setting_is_reset(:wallaby, :chromedriver)
120+
Application.put_env(:wallaby, :chromedriver, path: chromedriver_test_script_path)
121+
Application.put_env(:wallaby, :chromedriver, binary: chrome_test_script_path)
122+
123+
log =
124+
capture_io(:stderr, fn ->
125+
assert :ok == Application.start(:wallaby)
126+
end)
127+
128+
assert log =~ "Looks like you're trying to run Wallaby with a mismatched version of Chrome"
129+
end
130+
131+
test "application does not start when chrome version != chromedriver version", %{
109132
workspace_path: workspace_path
110133
} do
111-
test_script_path =
112-
ChromeTestScript.build_version_mock_script(version: "2.30")
134+
chromedriver_test_script_path =
135+
ChromeTestScript.build_chromedriver_version_mock_script(version: "99.0.3945.36")
136+
|> write_test_script!(workspace_path)
137+
138+
chrome_test_script_path =
139+
ChromeTestScript.build_chrome_version_mock_script(version: "101.0.3945.36")
113140
|> write_test_script!(workspace_path)
114141

115142
ensure_setting_is_reset(:wallaby, :chromedriver)
116-
Application.put_env(:wallaby, :chromedriver, path: test_script_path)
143+
Application.put_env(:wallaby, :chromedriver, path: chromedriver_test_script_path)
144+
Application.put_env(:wallaby, :chromedriver, binary: chrome_test_script_path)
117145

118-
assert :ok = Application.start(:wallaby)
146+
log =
147+
capture_io(:stderr, fn ->
148+
assert :ok == Application.start(:wallaby)
149+
end)
150+
151+
assert log =~ "Looks like you're trying to run Wallaby with a mismatched version of Chrome"
119152
end
120153

121154
test "works with a path in the home directory" do
@@ -137,11 +170,7 @@ defmodule Wallaby.Integration.Chrome.StartingSessionsTest do
137170
test "fails to start when chromedriver path is configured incorrectly" do
138171
ensure_setting_is_reset(:wallaby, :chromedriver)
139172

140-
Application.put_env(
141-
:wallaby,
142-
:chromedriver,
143-
path: "this-really-should-not-exist"
144-
)
173+
Application.put_env(:wallaby, :chromedriver, path: "this-really-should-not-exist")
145174

146175
assert {:error, _} = Application.start(:wallaby)
147176
end
@@ -150,7 +179,7 @@ defmodule Wallaby.Integration.Chrome.StartingSessionsTest do
150179
{:ok, chromedriver_path} = Chrome.find_chromedriver_executable()
151180

152181
chromedriver_path
153-
|> ChromeTestScript.build_wrapper_script(opts)
182+
|> ChromeTestScript.build_chromedriver_wrapper_script(opts)
154183
|> write_test_script!(base_dir)
155184
end
156185
end

lib/wallaby/chrome.ex

+131-19
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ defmodule Wallaby.Chrome do
66
77
Start a Wallaby Session using this driver with the following command:
88
9-
```
9+
```elixir
1010
{:ok, session} = Wallaby.start_session()
1111
```
1212
@@ -20,7 +20,7 @@ defmodule Wallaby.Chrome do
2020
This will override the default capabilities and capabilities set with application configuration.
2121
This will _not_ override capabilities passed in directly to `Wallaby.start_session/1`.
2222
23-
```
23+
```elixir
2424
config :wallaby,
2525
chromedriver: [
2626
headless: false
@@ -31,7 +31,7 @@ defmodule Wallaby.Chrome do
3131
3232
These capabilities will override the default capabilities.
3333
34-
```
34+
```elixir
3535
config :wallaby,
3636
chromedriver: [
3737
capabilities: %{
@@ -44,7 +44,7 @@ defmodule Wallaby.Chrome do
4444
4545
If ChromeDriver is not available in your path, you can specify it's location.
4646
47-
```
47+
```elixir
4848
config :wallaby,
4949
chromedriver: [
5050
path: "path/to/chrome"
@@ -58,7 +58,7 @@ defmodule Wallaby.Chrome do
5858
This will override the default capabilities and capabilities set with application configuration.
5959
This will _not_ override capabilities passed in directly to `Wallaby.start_session/1`.
6060
61-
```
61+
```elixir
6262
config :wallaby,
6363
chromedriver: [
6464
binary: "path/to/chrome"
@@ -107,7 +107,6 @@ defmodule Wallaby.Chrome do
107107
@behaviour Wallaby.Driver
108108

109109
@default_readiness_timeout 5_000
110-
@chromedriver_version_regex ~r/^ChromeDriver (\d+)\.(\d+)/
111110

112111
alias Wallaby.Chrome.Chromedriver
113112
alias Wallaby.WebdriverClient
@@ -154,14 +153,91 @@ defmodule Wallaby.Chrome do
154153
@doc false
155154
@spec validate() :: :ok | {:error, DependencyError.t()}
156155
def validate do
157-
with {:ok, executable} <- find_chromedriver_executable() do
158-
{version, 0} = System.cmd(executable, ["--version"])
159-
160-
@chromedriver_version_regex
161-
|> Regex.run(version)
162-
|> Enum.drop(1)
163-
|> Enum.map(&String.to_integer/1)
164-
|> version_check()
156+
with {:ok, chromedriver_version} <- get_chromedriver_version(),
157+
{:ok, chrome_version} <- get_chrome_version(),
158+
:ok <- minimum_version_check(chromedriver_version) do
159+
version_compare(chrome_version, chromedriver_version)
160+
end
161+
end
162+
163+
@doc false
164+
@spec get_chrome_version() :: {:ok, list(String.t())} | {:error, term}
165+
def get_chrome_version do
166+
case :os.type() do
167+
{:win32, :nt} ->
168+
{stdout, 0} =
169+
System.cmd("reg", [
170+
"query",
171+
"HKLM\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Google Chrome"
172+
])
173+
174+
chrome_version = parse_version("Version", stdout)
175+
176+
{:ok, chrome_version}
177+
178+
_ ->
179+
case find_chrome_executable() do
180+
{:ok, chrome_executable} ->
181+
{stdout, 0} = System.cmd(chrome_executable, ["--version"])
182+
chrome_version = parse_version("Google Chrome", stdout)
183+
184+
{:ok, chrome_version}
185+
186+
error ->
187+
error
188+
end
189+
end
190+
end
191+
192+
@doc false
193+
@spec get_chromedriver_version() :: {:ok, list(String.t())} | {:error, term}
194+
def get_chromedriver_version do
195+
case find_chromedriver_executable() do
196+
{:ok, chromedriver_executable} ->
197+
{stdout, 0} = System.cmd(chromedriver_executable, ["--version"])
198+
chromedriver_version = parse_version("ChromeDriver", stdout)
199+
200+
{:ok, chromedriver_version}
201+
202+
error ->
203+
error
204+
end
205+
end
206+
207+
@doc false
208+
@spec find_chrome_executable :: {:ok, String.t()} | {:error, DependencyError.t()}
209+
def find_chrome_executable do
210+
default_chrome_path =
211+
case :os.type() do
212+
{:unix, :darwin} ->
213+
"/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome"
214+
215+
{:unix, :linux} ->
216+
"google-chrome"
217+
218+
{:win32, :nt} ->
219+
"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
220+
end
221+
222+
chrome_path =
223+
:wallaby
224+
|> Application.get_env(:chromedriver, [])
225+
|> Keyword.get(:binary, default_chrome_path)
226+
227+
[Path.expand(chrome_path), default_chrome_path]
228+
|> Enum.find(&System.find_executable/1)
229+
|> case do
230+
path when is_binary(path) ->
231+
{:ok, path}
232+
233+
nil ->
234+
exception =
235+
DependencyError.exception("""
236+
Wallaby can't find Chrome. Make sure you have chrome installed and included in your path.
237+
You can also provide a path using `config :wallaby, :chromedriver, binary: <path>`.
238+
""")
239+
240+
{:error, exception}
165241
end
166242
end
167243

@@ -182,25 +258,48 @@ defmodule Wallaby.Chrome do
182258
nil ->
183259
exception =
184260
DependencyError.exception("""
185-
Wallaby can't find chromedriver. Make sure you have chromedriver installed
186-
and included in your path.
261+
Wallaby can't find chromedriver. Make sure you have chromedriver installed and included in your path.
187262
You can also provide a path using `config :wallaby, :chromedriver, path: <path>`.
188263
""")
189264

190265
{:error, exception}
191266
end
192267
end
193268

194-
defp version_check([major_version, _minor_version]) when major_version > 2 do
269+
defp version_compare(chrome_version, chromedriver_version) do
270+
case chrome_version == chromedriver_version do
271+
true ->
272+
:ok
273+
274+
_ ->
275+
exception =
276+
DependencyError.exception("""
277+
Looks like you're trying to run Wallaby with a mismatched version of Chrome: #{Enum.join(chrome_version, ".")} and chromedriver: #{Enum.join(chromedriver_version, ".")}.
278+
Chrome and chromedriver must match to a major, minor, and build version.
279+
""")
280+
281+
IO.warn(exception.message)
282+
283+
:ok
284+
end
285+
end
286+
287+
defp minimum_version_check([major_version, _minor_version, _build_version])
288+
when major_version > 2 do
289+
:ok
290+
end
291+
292+
defp minimum_version_check([major_version, minor_version, _build_version])
293+
when major_version == 2 and minor_version >= 30 do
195294
:ok
196295
end
197296

198-
defp version_check([major_version, minor_version])
297+
defp minimum_version_check([major_version, minor_version])
199298
when major_version == 2 and minor_version >= 30 do
200299
:ok
201300
end
202301

203-
defp version_check(_version) do
302+
defp minimum_version_check(_version) do
204303
exception =
205304
DependencyError.exception("""
206305
Looks like you're trying to run an older version of chromedriver. Wallaby needs at least
@@ -210,6 +309,19 @@ defmodule Wallaby.Chrome do
210309
{:error, exception}
211310
end
212311

312+
defp parse_version(prefix, body) do
313+
result =
314+
case Regex.run(~r/\b#{prefix}\b.*?(\d+\.\d+(\.\d+)?)/, body) do
315+
[_, version, _] ->
316+
String.split(version, ".")
317+
318+
[_, version] ->
319+
String.split(version, ".")
320+
end
321+
322+
Enum.map(result, &String.to_integer/1)
323+
end
324+
213325
@doc false
214326
@spec start_session([start_session_opts]) :: Wallaby.Driver.on_start_session() | no_return
215327
def start_session(opts \\ []) do

0 commit comments

Comments
 (0)