elixir-wallaby / wallaby Goto Github PK
View Code? Open in Web Editor NEWConcurrent browser tests for your Elixir web apps.
Home Page: https://twitter.com/elixir_wallaby
License: MIT License
Concurrent browser tests for your Elixir web apps.
Home Page: https://twitter.com/elixir_wallaby
License: MIT License
We are working around it for now by setting PATH
explicitly, but it'd be nicer/easier if it were just configurable.
Blocking each webdriver call with a call to check for js errors and logs is an expensive and wasteful process. All of this same information is sent to the server process from phantom. We should be able to capture this information using the same logging capture behaviour and then print logs to the console or throw errors in our respective test. This should work correctly because each phantom is scoped to a single test at a time.
It seems to be quite common to have multiple OTP-Apps dealing with their own Repo
s in in umbrella application.
According to a short talk to @keathley, this is currently not supported by wallaby.
An example of such an umbrella application is wojtekmach's acme_bank from ExConf '16. It has a Repo at least in the sub-apps bank
and auth
.
We noticed some ambiguous match problems when using #find for an element - it will timeout instead of returning an error:
session |> find(".project")
# If there are two projects, this will hang
I'm having some tests fail intermittently because of a HTTPPoison timeout error. Would you be open to setting the HTTPoison timeout to :infinity
as ExUnit.Case already has a configurable timeout with a default?
EDIT: Adding some more information.
mix test test/features
This makes me think it's actually the first test running before everything is fully initialised (phantom?), so setting HTTPoison mike not be the best way to fix in your opinion.
I've split this into its own issue as from what I've read its a harder issue to solve.
It'd also be very nice to be able to test drag and drop file uploaders by allowing us to easily specify that we've dropped a file onto a specific element.
There is a lot of ways to interact with the forms, but I haven't found a way to interact with website by attach some files to the file fields. I think this should be implemented
@keathley WDYT?
I found injectJs heavily important because it allows to use polyfills in the code. I'm working on it now
I tried this while debugging
visit("/path")
|> take_screenshot
|> find(".my-css")
But it failed because take_screenshot
did not return a session. I think it would be pretty cool if it returned a session so debugging with screenshots would be a bit simpler
Is there any chance this can be modified to run on Windows. The shall script requires a bash environment, as I understand it. Could that not be replaced with a similar batch file?
If you pass a session to text it returns an Invalid command
from Webdriver:
session
|> visit("/")
|> text
Hi there! First time using Wallaby on my first Phoenix app. I've written my first acceptance test but have run in to an issue I can't resolve on my own
It's very simple.. filling out a form to register a user.
<%= form_for @changeset, organisation_path(@conn, :create), fn f -> %>
<%= if @changeset.action do %>
<p>Oh noes! Please check the errors below.</p>
<% end %>
<%= inputs_for f, :users, fn u -> %>
<%= error_tag u, :first_name %>
<%= text_input u, :first_name, placeholder: "First name" %>
<%= error_tag u, :first_name %>
<%= text_input u, :last_name, placeholder: "Last name" %>
<%= error_tag u, :email %>
<%= text_input u, :email, placeholder: "Email" %>
<% end %>
<%= error_tag f, :name %>
<%= text_input f, :name, placeholder: "Organisation name" %>
<div class="actions">
<input type="submit" value="Submit" />
<a href="#" class="forgot-password">Already signed up?</a>
</div>
<% end %>
defmodule Esteem.UserSignsUpTest do
use Esteem.AcceptanceCase, async: true
test "user signs up a new organisation", %{session: session} do
welcome_message =
session
|> visit("/")
|> find("form")
|> fill_in("First name", with: "Luke")
|> fill_in("Last name", with: "Rollans")
|> fill_in("Email", with: "[email protected]")
|> fill_in("Organisation name", with: "Org name")
|> click("Submit")
|> find("p")
|> text
assert welcome_message =~ "Whelp"
end
end
1) test user signs up a new organisation (Esteem.UserSignsUpTest)
test/acceptance/user_signs_up_test.exs:4
** (Wallaby.ElementNotFound) Could not find element
stacktrace:
(wallaby) lib/wallaby/node.ex:342: Wallaby.Node.retry/2
(wallaby) lib/wallaby/node.ex:192: Wallaby.Node.click/2
test/acceptance/user_signs_up_test.exs:15
Seems that Wallaby cannot find the "Submit" button (line 15). I've tried using an ID, scoping a find
to the actions
wrapper, and other methods of detection to no vail.
Am I doing something glaringly wrong? I've read through the docs a few times and looked at test source code.. from what I can tell, it should work.
I have a relatively simple sign up form (the body html):
<div id="root">
<div data-reactroot="" class="view-container registrations new">
<main>
<header>
<div class="logo"></div>
</header>
<form>
<div class="field"><input type="text" name="first_name" value="" placeholder="First name" required=""></div>
<div class="field"><input type="text" name="last_name" value="" placeholder="Last name" required=""></div>
<div class="field"><input type="email" name="email" value="" placeholder="Email" required=""></div>
<div class="field"><input type="text" name="password" value="" placeholder="Password" required=""></div>
<div class="field"><input type="password" name="password_confirmation" value="" placeholder="Confirm password" required=""></div>
<button type="submit">Sign up</button><a href="/signin">Sign in</a>
</form>
</main>
</div>
</div>
When I attempt to run this test:
test "redirect to sign in", %{session: session} do
user = %{
first_name: "Jane",
last_name: "Smith",
email: "[email protected]",
password: "password",
encrypted_password: Comeonin.Bcrypt.hashpwsalt("password"),
}
session
|> visit("/signup")
|> fill_in("first_name", with: user.first_name)
end
I reproducibly get this output:
PhantomJS has crashed. Please read the bug reporting guide at
<http://phantomjs.org/bug-reporting.html> and file a bug report.
1) test redirect to sign in (MyApp.UserRegistrationTest)
test/acceptance/user_registration_test.exs:4
** (RuntimeError) There was an error calling: http://localhost:50589/session/216548c0-6af0-11e6-873a-d74ddc1e1078/element/:wdc:1472149027298/value -> closed
stacktrace:
(wallaby) lib/wallaby/driver.ex:302: Wallaby.Driver.make_request/3
(wallaby) lib/wallaby/driver.ex:274: Wallaby.Driver.check_logs!/2
(wallaby) lib/wallaby/node.ex:58: Wallaby.Node.fill_in/2
(wallaby) lib/wallaby/dsl/actions.ex:63: Wallaby.DSL.Actions.fill_in/3
test/acceptance/user_registration_test.exs:18: (test)
Finished in 2.2 seconds
1 test, 1 failure
Randomized with seed 380223
I have another, similar test for which this doesn't happen at all - is there something I'm missing?
At least it didn't work for me, although my buttons had similar markup to the ones in test/support/pages/forms.html
, then again there seems to be no test coverage for it either.
I wrote an xpath that is able to find the possible range of buttons with inspiration from Capybara.
An implementation similar to click_link
(and as I use it) would be:
def press(session, button)
button_xpath = ".//*[self::input | self::button][(./@type = 'submit' or ./@type = 'reset' or ./@type = 'button' or ./@type = 'image')][(((./@id = '#{button}' or ./@name = '#{button}' or ./@value = '#{button}' or ./@alt = '#{button}' or ./@title = '#{button}' or contains(normalize-space(string(.)), '#{button}'))))]"
session
|> Node.find({:xpath, button_xpath}
|> Node.click
end
If desired, I can create a PR.
Hi,
I am trying to use Wallaby to test the user session flow, it's a Phoenix app. So for the sign out, the EEx looks like this:
<%= link "Sign Out", to: session_path(@conn, :delete), method: :delete %>
Unfortunately this doesn't appear to work. Use take_screenshot
I can see that the link is not getting clicked.
If I change the implementation to either:
<%= form_tag session_path(@conn, :delete), method: :delete %>
<%= submit "Sign Out", class: "btn btn-primary" %>
</form>
# use `click_on` in the test
Or:
<%= link "Sign Out", to: session_path(@conn, :delete) %>
# change route from :delete to :get
Then the test works.
Also, the Phoenix JS is included correctly, and JS is being executed in the test environment.
Is this a known issue, or have I somehow done this incorrectly?
Is there a way to clear local storage for each test run? Or to specify different local storage directories?
I see that it's possible to modify phantomjs to use a different local storage path, but I'm trying to avoid tests that modify local storage from stepping on one another.
Hey there!
I've been experiencing this for a while, but given it isn't important I haven't opened an issue. I consistently get timeout issues (and sometimes other, seemingly unrelated error messages) when I run my acceptance tests asynchronously using the async
option.
If I turn off the async option everything is green but my suite takes about an extra 3-4 seconds to run at its current size. Currently sitting at about 200 tests all up, so it's not huge, but will continue to grow pretty steadily so I'm hoping to fix this issue now rather than later.
Here's an example of two acceptance tests that reproduce the issue when run in isolation together.
defmodule Sightseer.UserLogsInTest do
use Sightseer.AcceptanceCase, async: true
test "user logs in", %{session: session} do
user = insert(:user, encrypted_password: Comeonin.Bcrypt.hashpwsalt("test"))
session
|> visit("/sessions/new")
|> fill_in("Email address", with: user.email)
|> fill_in("Password", with: "test")
|> click_button("Submit")
notice = session
|> find(".alert-info")
|> text
assert notice == "Signed in successfully"
end
end
defmodule Sightseer.UserSignsUpTest do
use Sightseer.AcceptanceCase, async: true
test "user signs up", %{session: session} do
session
|> visit("/signup")
|> fill_in("First name", with: "Luke")
|> fill_in("Email address", with: "[email protected]")
|> fill_in("Password", with: "1234567madpasswordbro")
|> click_button("Submit")
notice = session
|> find(".alert-info")
|> text
assert notice == "Successfully signed up!"
end
end
async
is onGenerated sightseer app
1) test user logs in up (Sightseer.UserLogsInTest)
test/acceptance/user_logs_in_test.exs:4
** (exit) exited in: :gen_server.call(Wallaby.ServerPool, {:checkout, #Reference<0.0.1.94027>, true}, 5000)
** (EXIT) time out
stacktrace:
(stdlib) gen_server.erl:212: :gen_server.call/3
(poolboy) src/poolboy.erl:55: :poolboy.checkout/3
(wallaby) lib/wallaby.ex:34: Wallaby.start_session/1
(sightseer) test/support/acceptance_case.ex:21: Sightseer.AcceptanceCase.__ex_unit_setup_0/1
(sightseer) test/support/acceptance_case.ex:1: Sightseer.AcceptanceCase.__ex_unit__/2
test/acceptance/user_logs_in_test.exs:1: Sightseer.UserLogsInTest.__ex_unit__/2
...................................................................................................................................................................................
Finished in 5.9 seconds
180 tests, 1 failure
Any insight in to how I might be able improve reliability here would be very much appreciated :)
Hi. is that possible to write test like :
test "something" do
end
without setup, and also skip
setup tag do
xxxx
{:ok, conn: conn}
end
in ConnCase.
thanks.
Pretty sure I've followed everything in the README and got up to actually writing some tests; but running into issues. I'm seeing this error:
Erlang/OTP 18 [erts-7.2.1] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
test/acceptance/create_projects_test.exs:6: warning: variable session is unused
Forum.CreateProjectTest
* alternate formatRequest to pry #PID<0.304.0> at test/acceptance/create_projects_test.exs:20
visit session, "/"
IEx.pry
click_link session, "New Project"
Allow? [Yn] n
23:51:33.726 [error] Postgrex.Protocol (#PID<0.258.0>) disconnected: ** (DBConnection.Error) ownership timeout
23:51:34.456 [error] Postgrex.Protocol (#PID<0.257.0>) disconnected: ** (DBConnection.Error) ownership timeout
And then after 60 second the test times out and fails. The first visit
is fine, but inspecting text(session)
in the IEx.pry returns an empty string. Something is missing either in my configuration, or in the setup steps, and I'm not sure what it is :)
I've pushed my test app to GitHub here: https://github.com/sevenseacat/test_wallaby
When running a test case that doesn't rely on wallaby, is it possible to not start PhantomJS in this case?
It would be nice to be able to test drag and drop interactions with wallaby easily by specifying a selector to drag and the selector it should be dropped upon.
At first, I missed the instructions for setting the base_url. So when I did visit("/my_path")
it did not visit my app.
I think that if you use relative paths without a base_url an error should be raised
You called visit with #{relative_path}, but did not set a base_url
Set this somewhere in your app, maybe in test/test_helper.exs:
Application.put_env(:wallaby, :base_url, "http://localhost:4001")
If using Phoenix, you could grab the url from your endpoint:
Application.put_env(:wallaby, :base_url, YourApplication.Endpoint.url)
The #find method for a given selector which returns more than one result; it may not return these elements in the strictly-same order (expected to always be ordered by the DOM). This was our work-around:
projects =
session
|> sign_in
|> find(".project", count: 2)
first_project = List.first(projects)
project_text =
projects
|> Enum.map(&text(&1))
|> Enum.sort
assert project_text == ["Initech", "The Petty Marsh"]
I've added the config
# config/test.exs
config :wallaby, screenshot_on_failure: true
to my app but when I do assert "foo" == "bar"
no screenshot is taken. Screenshots in general do work though as adding take_screenshot(session)
to the same test does result in a screenshot.
Let me know if there is additional debug information needed. I've just started with wallaby so it's not impossible that I'm doing something wrong.
Hello. I'm wondering if it makes sense to use Wallaby for REST API testing where browser testing is also needed. Doing QA (end-to-end acceptance) testing in a standing environment with a single tool has many advantages, particularly if that tool features parallelism. Thanks!
it would be nice to have some way to have a collection of wallaby nodes be able to return a blob (or array!) of text:
project_text =
session
|> find(".project", count: 2)
|> text
# project_text would be "project 1 project 2" or ["project 1", "project 2"]
The winnings to date during the season, the amount of winnings paid, and the dues paid is listed for each team so the commissioner doesn't have to keep a separate spreadsheet and there is transparency with the owners.
In order to help write specs where nodes are removed from the DOM, we could use a method where it resolves when the pattern is not found. This would kinda be the opposite of find(pattern, count: x)
Wishful use case:
session
|> click("remove-button")
|> find(".new_object", count: 0)
#or
|> wait_until_gone(".new_object")
Where a failing spec would be like:
Expected to find 'new-object' with count 0 but found 1 node
I can't seem to get Wallaby playing nicely with authentication. I'm using Guardian to handle sessions and authentication, which is obviously done through a conn
.
Should I ...
conn
? If I build a conn, put the session in the state I want it to be, will the Wallaby session reflect that state? (I think this is a big no)I'm planning on blogging this once I've figured it all out.. Seems to be a bit of a hole in the Phoenix community atm :)
The README needs to be updated to include quick start information, info on the DSL, test examples, etc.
Was wondering if you guys are ok with adding an addition API to get the page source.
This would aid developers that would like to stick with their preferred DSL like floki.
I already have the function working in my fork of this project but would need some guidance in writing the tests. (where to place them etc..)
Example of the proposed API:
iex(4)> session |> Wallaby.Driver.visit("http://www.example.com") |> Wallaby.Driver.source()
"<!DOCTYPE html><html><head>\n <title>Example Domain</title>\n\n
the page source of the session currently
</html>"
I forgot to add server: true
to my endpoint so when I attempted to visit the page nothing was rendered. The visit
did not fail though, it continued on as if it loaded.
I think that if loading fails it should raise. It would be extra cool if the error message said something like this:
could not load #{the_full_url}. If you are using Phoenix you may need to enable the server
# config/test.exs
config :your_app, YourApplication.Endpoint,
server: true
Seems that using
fill_in(session, "session[email]", with: "x")
Takes 300-400ms to complete.
Not sure why this is.
Here is a exprof dump (note that this is in the header, so I use the full namespace:
profile do
Wallaby.DSL.Actions.fill_in(session, "session[email]", with: "xxx")
end
FUNCTION CALLS % TIME [uS / CALLS]
-------- ----- ------- ---- [----------]
hackney_response:stream_body/2 1 0.00 0 [ 0.00]
hackney_response:stream_body_recv/2 1 0.00 0 [ 0.00]
'Elixir.Keyword':merge/2 1 0.00 0 [ 0.00]
'Elixir.Keyword':'-merge/2-fun-0-'/2 2 0.00 0 [ 0.00]
'Elixir.Wallaby.Driver':clear/1 1 0.00 0 [ 0.00]
'Elixir.Wallaby.Driver':displayed/1 1 0.00 0 [ 0.00]
'Elixir.Wallaby.Driver':request/2 2 0.00 0 [ 0.00]
'Elixir.Wallaby.Driver':set_value/2 1 0.00 0 [ 0.00]
'Elixir.Wallaby.Driver':to_params/1 1 0.00 0 [ 0.00]
'Elixir.Wallaby.Driver':'-find_elements/1-fun-0-'/2 1 0.00 0 [ 0.00]
'Elixir.Wallaby.Node.Query':assert_count/1 1 0.00 0 [ 0.00]
'Elixir.Wallaby.Node.Query':build_locator/1 1 0.00 0 [ 0.00]
'Elixir.Wallaby.Node.Query':default_conditions/0 1 0.00 0 [ 0.00]
'Elixir.Wallaby.Node.Query':fillable_field/3 1 0.00 0 [ 0.00]
'Elixir.Wallaby.Node.Query':find_element/3 1 0.00 0 [ 0.00]
'Elixir.Wallaby.Node.Query':find_field/3 1 0.00 0 [ 0.00]
'Elixir.Wallaby.Node.Query':retry/1 1 0.00 0 [ 0.00]
'Elixir.Wallaby.Node.Query':retry/2 1 0.00 0 [ 0.00]
'Elixir.Wallaby.Node.Query':'-find_element/3-fun-0-'/1 1 0.00 0 [ 0.00]
'Elixir.Enum':'all?'/2 1 0.00 0 [ 0.00]
'Elixir.Enum':'do_all?'/2 2 0.00 0 [ 0.00]
'Elixir.Enum':map/2 1 0.00 0 [ 0.00]
'Elixir.Enum':'-map/2-lists^map/1-0-'/2 2 0.00 0 [ 0.00]
'Elixir.Wallaby.Node':clear/1 1 0.00 0 [ 0.00]
'Elixir.Wallaby.Node':'visible?'/1 1 0.00 0 [ 0.00]
'Elixir.Poison.Encoder.List':encode/2 1 0.00 0 [ 0.00]
'Elixir.Poison.Encoder.List':encode/3 1 0.00 0 [ 0.00]
'Elixir.Poison.Encoder.List':'-encode/3-lists^foldr/2-1-'/3 2 0.00 0 [ 0.00]
'Elixir.Poison.Encoder.List':'-encode/3-fun-0-'/3 1 0.00 0 [ 0.00]
'Elixir.Map':get/2 1 0.00 0 [ 0.00]
'Elixir.Map':get/3 1 0.00 0 [ 0.00]
erlang:send/2 1 0.00 0 [ 0.00]
erlang:monotonic_time/1 1 0.00 0 [ 0.00]
'Elixir.String.Casing':downcase/1 8 0.02 1 [ 0.13]
'Elixir.Keyword':'-merge/2-lists^filter/1-0-'/2 3 0.02 1 [ 0.33]
hackney_manager:get_state/2 8 0.02 1 [ 0.13]
hackney_http:ce_identity/1 8 0.02 1 [ 0.13]
hackney_url:parse_url/2 8 0.02 1 [ 0.13]
hackney_url:parse_addr/2 8 0.02 1 [ 0.13]
'Elixir.Wallaby.Driver':'check_logs!'/2 4 0.02 1 [ 0.25]
'Elixir.Wallaby.Driver':find_elements/1 1 0.02 1 [ 1.00]
'Elixir.Wallaby.Driver':'-set_value/2-fun-0-'/2 1 0.02 1 [ 1.00]
'Elixir.Wallaby.Driver':'-find_elements/1-fun-1-'/3 1 0.02 1 [ 1.00]
'Elixir.Wallaby.Driver':'-displayed/1-fun-0-'/1 1 0.02 1 [ 1.00]
'Elixir.Wallaby.Driver':'-clear/1-fun-0-'/1 1 0.02 1 [ 1.00]
metrics_dummy:increment_counter/1 8 0.02 1 [ 0.13]
'Elixir.Wallaby.Node.Query':assert_text/1 1 0.02 1 [ 1.00]
'Elixir.Wallaby.Node.Query':assert_visibility/1 1 0.02 1 [ 1.00]
'Elixir.Wallaby.Node.Query':build_conditions/1 1 0.02 1 [ 1.00]
inet_parse:hex/1 8 0.02 1 [ 0.13]
'Elixir.Agent':get_and_update/2 4 0.02 1 [ 0.25]
prim_inet:enc_opts/2 8 0.02 1 [ 0.13]
'Elixir.Process':whereis/1 4 0.02 1 [ 0.25]
'Elixir.HTTPoison':process_headers/1 8 0.02 1 [ 0.13]
'Elixir.HTTPoison':process_response_body/1 8 0.02 1 [ 0.13]
'Elixir.HTTPoison':process_status_code/1 8 0.02 1 [ 0.13]
'Elixir.Poison':'encode!'/2 6 0.02 1 [ 0.17]
'Elixir.Enum':each/2 4 0.02 1 [ 0.25]
'Elixir.Enum':'-each/2-lists^foreach/1-0-'/2 4 0.02 1 [ 0.25]
'Elixir.Wallaby.XPath':fillable_field/1 1 0.02 1 [ 1.00]
'Elixir.Wallaby.Node':fill_in/2 1 0.02 1 [ 1.00]
hackney_connect:reconnect/4 8 0.02 1 [ 0.13]
'Elixir.Poison.Parser':array_values/3 5 0.02 1 [ 0.20]
'Elixir.GenServer':call/3 4 0.02 1 [ 0.25]
maps:get/2 7 0.02 1 [ 0.14]
hackney_request:perform_all/6 8 0.04 2 [ 0.25]
hackney_headers:get_value/2 8 0.04 2 [ 0.25]
hackney_headers:insert/3 8 0.04 2 [ 0.25]
error_handler:undefined_function/3 3 0.04 2 [ 0.67]
hackney_http:content_decode/3 8 0.04 2 [ 0.25]
'Elixir.Poison.Encoder.Map':encode/2 6 0.04 2 [ 0.33]
'Elixir.Poison.Encoder.Map':'-encode/3-fun-1-'/4 7 0.04 2 [ 0.29]
hackney_url:urldecode/1 8 0.04 2 [ 0.25]
'Elixir.Poison.Decode':decode/2 8 0.04 2 [ 0.25]
dict:fold/3 8 0.04 2 [ 0.25]
dict:fold_dict/3 8 0.04 2 [ 0.25]
'Elixir.Wallaby.Driver':cast_as_node/2 1 0.04 2 [ 2.00]
'Elixir.Wallaby.Driver':request/3 6 0.04 2 [ 0.33]
metrics_dummy:update_histogram/2 8 0.04 2 [ 0.25]
metrics_dummy:update_meter/2 8 0.04 2 [ 0.25]
inet_parse:ipv4_addr/5 8 0.04 2 [ 0.25]
inet_parse:ipv4_field/2 8 0.04 2 [ 0.25]
inet_parse:ipv6strict_address/1 8 0.04 2 [ 0.25]
inet_parse:ipv6_addr/1 8 0.04 2 [ 0.25]
'Elixir.HTTPoison.Base':request/9 8 0.04 2 [ 0.25]
'Elixir.Agent':get_and_update/3 4 0.04 2 [ 0.50]
prim_inet:send/3 8 0.04 2 [ 0.25]
prim_inet:type_value_default/3 8 0.04 2 [ 0.25]
prim_inet:enc_value/2 8 0.04 2 [ 0.25]
prim_inet:enc_value_default/3 8 0.04 2 [ 0.25]
prim_inet:enc_value_1/3 8 0.04 2 [ 0.25]
prim_inet:enc_value_2/2 8 0.04 2 [ 0.25]
prim_inet:decode_opt_val/1 8 0.04 2 [ 0.25]
prim_inet:dec_opt_val/3 8 0.04 2 [ 0.25]
'Elixir.Wallaby.DSL.Actions':fill_in/3 1 0.04 2 [ 2.00]
'Elixir.HTTPoison':process_request_body/1 8 0.04 2 [ 0.25]
'Elixir.Poison':'decode!'/1 8 0.04 2 [ 0.25]
'Elixir.Poison':'encode!'/1 6 0.04 2 [ 0.33]
hackney_connect:socket_from_pool/4 8 0.04 2 [ 0.25]
hackney_tcp_transport:send/2 8 0.04 2 [ 0.25]
'Elixir.Poison.Parser':number_complete/2 8 0.04 2 [ 0.25]
'Elixir.Poison.Parser':number_frac/2 8 0.04 2 [ 0.25]
gen_server:do_cast/2 8 0.04 2 [ 0.25]
'Elixir.Wallaby.Phantom.Logger':log/1 4 0.04 2 [ 0.50]
application_controller:get_key/2 8 0.04 2 [ 0.25]
gen_tcp:send/2 8 0.04 2 [ 0.25]
gen_tcp:controlling_process/2 8 0.04 2 [ 0.25]
maps:keys/1 6 0.04 2 [ 0.33]
maps:find/2 6 0.04 2 [ 0.33]
erlang:binary_to_integer/1 8 0.04 2 [ 0.25]
lists:keymember/3 10 0.04 2 [ 0.20]
erlang:'++'/2 9 0.04 2 [ 0.22]
erlang:make_ref/0 8 0.04 2 [ 0.25]
hackney_response:wait_headers/2 8 0.06 3 [ 0.38]
hackney_response:end_stream_body/2 8 0.06 3 [ 0.38]
hackney_request:maybe_add_host/2 8 0.06 3 [ 0.38]
hackney_headers:'-new/1-fun-0-'/2 8 0.06 3 [ 0.38]
hackney_bstr:join/2 16 0.06 3 [ 0.19]
error_handler:ensure_loaded/1 3 0.06 3 [ 1.00]
hackney_http:transfer_decode/2 8 0.06 3 [ 0.38]
hackney_http:'-parse_body/1-fun-3-'/1 8 0.06 3 [ 0.38]
'Elixir.Poison.Encoder.Map':encode/3 6 0.06 3 [ 0.50]
hackney_url:parse_netloc/2 8 0.06 3 [ 0.38]
hackney_url:parse_path/1 8 0.06 3 [ 0.38]
hackney_url:parse_fragment/1 8 0.06 3 [ 0.38]
hackney_url:pathencode/1 8 0.06 3 [ 0.38]
hackney:reply/2 8 0.06 3 [ 0.38]
hackney:'-body/1-fun-0-'/1 8 0.06 3 [ 0.38]
code:call/1 11 0.06 3 [ 0.27]
dict:mk_seg/1 8 0.06 3 [ 0.38]
'Elixir.Wallaby.Driver':make_request/3 8 0.06 3 [ 0.38]
'Elixir.Wallaby.Driver':request/4 8 0.06 3 [ 0.38]
inet_tcp:send/2 8 0.06 3 [ 0.38]
hackney_pool:find_pool/2 8 0.06 3 [ 0.38]
'Elixir.Wallaby.Node.Query':build_query/3 1 0.06 3 [ 3.00]
unicode:characters_to_binary/1 8 0.06 3 [ 0.38]
inet_parse:ipv4_addr/1 8 0.06 3 [ 0.38]
inet_parse:ipv4_addr/2 8 0.06 3 [ 0.38]
inet_parse:hex/3 8 0.06 3 [ 0.38]
'Elixir.HTTPoison.Base':default_process_url/1 8 0.06 3 [ 0.38]
prim_inet:setopts/2 8 0.06 3 [ 0.38]
prim_inet:dec_opt/1 8 0.06 3 [ 0.38]
prim_inet:type_value/2 8 0.06 3 [ 0.38]
prim_inet:type_value_1/3 8 0.06 3 [ 0.38]
prim_inet:type_value_2/2 8 0.06 3 [ 0.38]
prim_inet:enc_value/3 8 0.06 3 [ 0.38]
prim_inet:enum_name/2 8 0.06 3 [ 0.38]
prim_inet:enc_opt_val/4 8 0.06 3 [ 0.38]
idna_ucs:from_utf8/1 8 0.06 3 [ 0.38]
idna_ucs:expand_utf8/1 8 0.06 3 [ 0.38]
hackney_app:get_app_env/2 8 0.06 3 [ 0.38]
'Elixir.HTTPoison':process_url/1 8 0.06 3 [ 0.38]
'Elixir.Poison':'decode!'/2 8 0.06 3 [ 0.38]
string:to_lower/1 8 0.06 3 [ 0.38]
inet:setopts/2 8 0.06 3 [ 0.38]
inet:tcp_controlling_process/2 8 0.06 3 [ 0.38]
hackney_connect:connect/5 8 0.06 3 [ 0.38]
'Elixir.Poison.Parser':number_exp/3 8 0.06 3 [ 0.38]
'Elixir.Poison.Parser':number_start/1 8 0.06 3 [ 0.38]
'Elixir.Poison.Parser':parse/2 8 0.06 3 [ 0.38]
gen_server:cast/2 8 0.06 3 [ 0.38]
gen_server:do_send/2 8 0.06 3 [ 0.38]
idna:utf8_to_ascii/1 8 0.06 3 [ 0.38]
gen:'-call/4-fun-0-'/4 28 0.08 4 [ 0.14]
application:get_key/2 8 0.08 4 [ 0.50]
hackney_util:mod_metrics/0 8 0.08 4 [ 0.50]
hackney_response:body/1 8 0.08 4 [ 0.50]
hackney_request:can_perform_all/3 8 0.08 4 [ 0.50]
hackney_request:maybe_add_cookies/2 8 0.08 4 [ 0.50]
hackney_request:default_ua/0 8 0.08 4 [ 0.50]
hackney_headers:to_list/1 8 0.08 4 [ 0.50]
hackney_manager:init_request/1 8 0.08 4 [ 0.50]
hackney_manager:close_request/1 8 0.08 4 [ 0.50]
hackney_manager:update_state/2 16 0.08 4 [ 0.25]
hackney_http:parser/1 8 0.08 4 [ 0.50]
hackney_http:parse_response_line/1 8 0.08 4 [ 0.50]
hackney_http:parse_reason/4 8 0.08 4 [ 0.50]
hackney_http:'-parse_body/1-fun-2-'/2 8 0.08 4 [ 0.50]
'Elixir.Poison.Encoder.Map':encode_name/1 7 0.08 4 [ 0.57]
'Elixir.Poison.Encoder.Map':'-encode/3-lists^foldl/2-0-'/3 13 0.08 4 [ 0.31]
hackney:host_header/2 8 0.08 4 [ 0.50]
hackney:maybe_update_req/1 8 0.08 4 [ 0.50]
dict:new/0 8 0.08 4 [ 0.50]
dict:fold_segs/4 16 0.08 4 [ 0.25]
dict:maybe_expand/2 38 0.08 4 [ 0.11]
inet_tcp:recv/3 16 0.08 4 [ 0.25]
'Elixir.Fops.AcceptanceCase':'-__ex_unit_setup_0/1-fun-0-'/2 1 0.08 4 [ 4.00]
'Elixir.HTTPoison.Base':build_hackney_options/2 8 0.08 4 [ 0.50]
'Elixir.HTTPoison.Base':response/6 8 0.08 4 [ 0.50]
prim_inet:recv/3 16 0.08 4 [ 0.25]
prim_inet:recv0/3 16 0.08 4 [ 0.25]
prim_inet:getopt/2 8 0.08 4 [ 0.50]
prim_inet:getopts/2 8 0.08 4 [ 0.50]
prim_inet:type_opt_1/1 24 0.08 4 [ 0.17]
prim_inet:type_value/3 8 0.08 4 [ 0.50]
prim_inet:encode_opt_val/1 8 0.08 4 [ 0.50]
prim_inet:encode_opts/1 8 0.08 4 [ 0.50]
metrics:increment_counter/2 8 0.08 4 [ 0.50]
'Elixir.HTTPoison':'-request/5-fun-1-'/1 8 0.08 4 [ 0.50]
'Elixir.HTTPoison':'-request/5-fun-0-'/1 8 0.08 4 [ 0.50]
string:tokens_single_1/3 24 0.08 4 [ 0.17]
inet:tcp_sync_input/3 8 0.08 4 [ 0.50]
hackney_connect:create_connection/5 8 0.08 4 [ 0.50]
hackney_tcp_transport:messages/1 8 0.08 4 [ 0.50]
hackney_tcp_transport:recv/3 16 0.08 4 [ 0.25]
'Elixir.Poison.Parser':'parse!'/2 8 0.08 4 [ 0.50]
'Elixir.Poison.Encoder':encode/2 14 0.08 4 [ 0.29]
'Elixir.Poison.Encoder':impl_for/1 14 0.08 4 [ 0.29]
gen_server:cast_msg/1 8 0.08 4 [ 0.50]
'Elixir.GenServer':whereis/1 4 0.08 4 [ 1.00]
idna:to_ascii/2 32 0.08 4 [ 0.13]
'Elixir.Poison.Encoder.BitString':encode/2 14 0.11 5 [ 0.36]
hackney_response:start_response/1 8 0.11 5 [ 0.63]
hackney_request:send/2 8 0.11 5 [ 0.63]
hackney_request:expectation/1 8 0.11 5 [ 0.63]
hackney_request:end_stream_body/1 8 0.11 5 [ 0.63]
'Elixir.Keyword':'has_key?'/2 10 0.11 5 [ 0.50]
hackney_headers:new/1 8 0.11 5 [ 0.63]
hackney_headers:to_binary/1 16 0.11 5 [ 0.31]
hackney_manager:update_state/1 16 0.11 5 [ 0.31]
hackney_url:parse_url/1 8 0.11 5 [ 0.63]
hackney:send_request/2 16 0.11 5 [ 0.31]
hackney:make_request/6 8 0.11 5 [ 0.63]
hackney:maybe_proxy/4 8 0.11 5 [ 0.63]
hackney:maybe_redirect/2 8 0.11 5 [ 0.63]
dict:store/3 38 0.11 5 [ 0.13]
'Elixir.String':slice/3 8 0.11 5 [ 0.63]
'Elixir.Wallaby.LogStore':append_logs/2 4 0.11 5 [ 1.25]
inet_parse:address/1 8 0.11 5 [ 0.63]
inet_parse:ipv4_address/1 8 0.11 5 [ 0.63]
prim_inet:enc_opt_val/2 16 0.11 5 [ 0.31]
prim_inet:enc_opts/1 16 0.11 5 [ 0.31]
prim_inet:dec_opt_val/1 16 0.11 5 [ 0.31]
metrics:update_meter/3 8 0.11 5 [ 0.63]
'Elixir.HTTPoison':process_request_headers/1 8 0.11 5 [ 0.63]
erlang:port_command/3 8 0.11 5 [ 0.63]
erlang:port_connect/2 8 0.11 5 [ 0.63]
hackney_connect:maybe_connect/1 8 0.11 5 [ 0.63]
hackney_connect:check_mod_metrics/1 16 0.11 5 [ 0.31]
hackney_tcp_transport:setopts/2 8 0.11 5 [ 0.63]
idna:label_to_ascii/1 16 0.11 5 [ 0.31]
gen_tcp:recv/3 16 0.11 5 [ 0.31]
erlang:error/1 8 0.11 5 [ 0.63]
erlang:function_exported/3 11 0.11 5 [ 0.45]
gen:do_for_proc/2 28 0.13 6 [ 0.21]
hackney_response:wait_status/1 16 0.13 6 [ 0.38]
hackney_response:stream_body1/2 17 0.13 6 [ 0.35]
hackney_request:perform/2 8 0.13 6 [ 0.75]
hackney_request:handle_body/4 7 0.13 6 [ 0.86]
hackney_request:req_type/2 8 0.13 6 [ 0.75]
hackney_headers:update/2 15 0.13 6 [ 0.40]
hackney_headers:make_header/2 31 0.13 6 [ 0.19]
hackney_headers:'-to_list/1-fun-0-'/3 31 0.13 6 [ 0.19]
'Elixir.Access':get/3 41 0.13 6 [ 0.15]
hackney_bstr:'-to_upper/1-lbc$^0/2-0-'/2 39 0.13 6 [ 0.15]
hackney_manager:new_request/1 8 0.13 6 [ 0.75]
hackney_http:parse_response_version/2 8 0.13 6 [ 0.75]
hackney_http:parse_headers/1 39 0.13 6 [ 0.15]
hackney_http:parse_header/2 24 0.13 6 [ 0.25]
hackney_http:parse_options/2 16 0.13 6 [ 0.38]
hackney_url:do_partial_pathencode/2 46 0.13 6 [ 0.13]
hackney:body/1 8 0.13 6 [ 0.75]
code:ensure_loaded/1 11 0.13 6 [ 0.55]
dict:get_bucket_s/2 39 0.13 6 [ 0.15]
dict:maybe_expand_aux/2 38 0.13 6 [ 0.16]
hackney_pool:checkout/4 8 0.13 6 [ 0.75]
hackney_pool:checkin/2 8 0.13 6 [ 0.75]
unicode:characters_to_list/1 16 0.13 6 [ 0.38]
prim_inet:enc_opt/1 16 0.13 6 [ 0.38]
metrics:init/1 8 0.13 6 [ 0.75]
idna_unicode:downcase/1 16 0.13 6 [ 0.38]
'Elixir.Poison.Parser':object_name/2 25 0.13 6 [ 0.24]
'Elixir.Poison.Parser':object_pairs/3 25 0.13 6 [ 0.24]
idna:to_ascii/1 16 0.13 6 [ 0.38]
maps:from_list/1 9 0.13 6 [ 0.67]
erlang:make_fun/3 17 0.13 6 [ 0.35]
os:timestamp/0 8 0.13 6 [ 0.75]
erlang:integer_to_list/1 23 0.13 6 [ 0.26]
erlang:erase/1 8 0.13 6 [ 0.75]
gen:do_call/4 28 0.15 7 [ 0.25]
hackney_response:maybe_close/1 8 0.15 7 [ 0.88]
hackney_request:stream_body/2 8 0.15 7 [ 0.88]
hackney_headers:'-to_binary/1-fun-0-'/2 31 0.15 7 [ 0.23]
hackney_headers:'-update/2-fun-0-'/2 30 0.15 7 [ 0.23]
hackney_bstr:to_upper/1 8 0.15 7 [ 0.88]
hackney:reply_response/2 8 0.15 7 [ 0.88]
'Elixir.Wallaby.Driver':log/1 4 0.15 7 [ 1.75]
prim_inet:dec_value/2 8 0.15 7 [ 0.88]
metrics:update_histogram/3 8 0.15 7 [ 0.88]
erlang:port_info/2 8 0.15 7 [ 0.88]
re:replace/4 40 0.15 7 [ 0.17]
inet:'-setopts/2-lc$^0/1-0-'/1 16 0.15 7 [ 0.44]
hackney_connect:is_pool/1 16 0.15 7 [ 0.44]
hackney_tcp_transport:controlling_process/2 8 0.15 7 [ 0.88]
erlang:atom_to_binary/2 15 0.15 7 [ 0.47]
lists:member/2 40 0.15 7 [ 0.17]
erlang:port_get_data/1 32 0.15 7 [ 0.22]
hackney_response:read_body/3 16 0.17 8 [ 0.50]
hackney_headers:get_value/3 31 0.17 8 [ 0.26]
hackney_bstr:char_to_upper/1 31 0.17 8 [ 0.26]
hackney_manager:get_state/1 16 0.17 8 [ 0.50]
hackney_http:te_identity/2 8 0.17 8 [ 1.00]
hackney_url:normalize/2 8 0.17 8 [ 1.00]
dict:find_val/2 47 0.17 8 [ 0.17]
dict:store_bkt_val/3 45 0.17 8 [ 0.18]
hackney_pool:sync_socket/2 8 0.17 8 [ 1.00]
prim_inet:enum_val/2 16 0.17 8 [ 0.50]
'Elixir.HTTPoison':request/5 8 0.17 8 [ 1.00]
re:check_for_crlf/2 40 0.17 8 [ 0.20]
re:do_grun/6 40 0.17 8 [ 0.20]
'Elixir.String.Unicode':next_extend_size/2 64 0.17 8 [ 0.13]
'Elixir.String.Unicode':split_at/2 16 0.17 8 [ 0.50]
application_controller:get_env/2 32 0.17 8 [ 0.25]
unicode:characters_to_list/2 16 0.17 8 [ 0.50]
erlang:list_to_binary/1 15 0.17 8 [ 0.53]
hackney_http:parse_status/4 32 0.19 9 [ 0.28]
dict:get_bucket/2 39 0.19 9 [ 0.23]
prim_inet:type_opt/2 24 0.19 9 [ 0.38]
idna_ucs:expand_utf8_1/3 80 0.19 9 [ 0.11]
'Elixir.HTTPoison':'-request/5-fun-2-'/1 8 0.19 9 [ 1.13]
string:tokens/2 24 0.19 9 [ 0.38]
re:postprocess/5 40 0.19 9 [ 0.23]
hackney_connect:use_default_pool/0 16 0.19 9 [ 0.56]
timer:now_diff/2 8 0.19 9 [ 1.13]
'Elixir.Poison.Encoder':'impl_for!'/1 14 0.19 9 [ 0.64]
unicode:characters_to_binary/2 8 0.19 9 [ 1.13]
erlang:unlink/1 8 0.19 9 [ 1.13]
application:get_env/2 32 0.21 10 [ 0.31]
hackney_response:wait_headers/4 39 0.21 10 [ 0.26]
hackney:request/5 16 0.21 10 [ 0.63]
'Elixir.String':downcase/1 8 0.21 10 [ 1.25]
inet_parse:ipv4_field/4 80 0.21 10 [ 0.13]
re:grun2/3 40 0.21 10 [ 0.25]
'Elixir.String.Unicode':do_split_at/4 80 0.21 10 [ 0.13]
gen_server:call/3 24 0.21 10 [ 0.42]
erlang:whereis/1 15 0.21 10 [ 0.67]
erlang:put/2 24 0.21 10 [ 0.42]
erlang:list_to_integer/1 32 0.21 10 [ 0.31]
gen:call/4 28 0.23 11 [ 0.39]
'Elixir.Keyword':get/3 82 0.23 11 [ 0.13]
hackney_bstr:join/3 85 0.23 11 [ 0.13]
hackney_http:parse_body/1 25 0.23 11 [ 0.44]
dict:on_bucket/3 38 0.23 11 [ 0.29]
prim_inet:enc_time/1 16 0.23 11 [ 0.69]
erlang:binary_to_list/1 32 0.23 11 [ 0.34]
'Elixir.Access':get/2 41 0.25 12 [ 0.29]
hackney_http:execute/1 48 0.25 12 [ 0.25]
dict:find/2 39 0.25 12 [ 0.31]
erlang:port_control/3 32 0.25 12 [ 0.38]
erts_internal:port_connect/2 8 0.25 12 [ 1.50]
erts_internal:port_info/2 8 0.25 12 [ 1.50]
erlang:phash/2 77 0.25 12 [ 0.16]
'Elixir.Poison.Encoder.BitString':escape/2 28 0.28 13 [ 0.46]
hackney_response:stream_body/1 16 0.28 13 [ 0.81]
re:grun/3 40 0.28 13 [ 0.33]
'Elixir.Access':fetch/2 41 0.30 14 [ 0.34]
dict:'-store/3-fun-0-'/3 38 0.30 14 [ 0.37]
prim_inet:ctl_cmd/3 32 0.30 14 [ 0.44]
idna_ucs:is_ascii/1 144 0.30 14 [ 0.10]
string:to_lower_char/1 72 0.30 14 [ 0.19]
idna_unicode:lookup/2 144 0.30 14 [ 0.10]
idna_unicode:'-lowercase/1-fun-0-'/2 144 0.30 14 [ 0.10]
'Elixir.Poison.Parser':value/2 34 0.30 14 [ 0.41]
lists:foldl/3 100 0.32 15 [ 0.15]
hackney_bstr:trim/1 40 0.32 15 [ 0.38]
hackney_http:parse_header/1 39 0.32 15 [ 0.38]
string:r_pad/3 144 0.32 15 [ 0.10]
string:'-to_lower/1-lc$^0/1-0-'/1 80 0.32 15 [ 0.19]
'Elixir.Poison.Parser':string_continue/2 68 0.32 15 [ 0.22]
'Elixir.String.Casing':downcase/2 72 0.34 16 [ 0.22]
code_server:call/1 11 0.34 16 [ 1.45]
hackney_http:parse_header_value/1 48 0.34 16 [ 0.33]
dict:fold_seg/4 136 0.34 16 [ 0.12]
erlang:list_to_integer/2 8 0.34 16 [ 2.00]
lists:keyfind/3 118 0.34 16 [ 0.14]
proplists:get_value/2 48 0.36 17 [ 0.35]
lists:reverse/2 120 0.36 17 [ 0.14]
'Elixir.Keyword':get/2 74 0.38 18 [ 0.24]
dict:get_slot/2 77 0.38 18 [ 0.23]
string:tokens_single_2/4 184 0.38 18 [ 0.10]
string:right/3 144 0.38 18 [ 0.13]
inet_db:lookup_socket/1 32 0.40 19 [ 0.59]
re:process_parameters/6 80 0.40 19 [ 0.24]
hackney_response:recv/1 16 0.42 20 [ 1.25]
lists:all/2 160 0.42 20 [ 0.13]
hackney_url:urldecode/3 80 0.45 21 [ 0.26]
re:do_replace/5 40 0.45 21 [ 0.53]
erlang:demonitor/2 28 0.45 21 [ 0.75]
re:check_for_unicode/2 80 0.47 22 [ 0.28]
ets:delete/2 8 0.49 23 [ 2.88]
erlang:monitor/2 28 0.49 23 [ 0.82]
hackney_trace:report_event/4 48 0.51 24 [ 0.50]
'Elixir.String.Unicode':next_grapheme_size/1 64 0.51 24 [ 0.38]
idna_unicode_data1:l/1 144 0.51 24 [ 0.17]
hackney_http:parse_first_line/3 16 0.53 25 [ 1.56]
hackney_http:match_eol/2 144 0.55 26 [ 0.18]
prim_inet:async_recv/3 16 0.55 26 [ 1.63]
idna:'-label_to_ascii/1-fun-0-'/1 144 0.57 27 [ 0.19]
dict:fold_bucket/3 159 0.62 29 [ 0.18]
idna_unicode:codepoint_downcase/1 144 0.62 29 [ 0.20]
idna_unicode:hex/1 144 0.62 29 [ 0.20]
idna_unicode:'-downcase/1-lc$^0/1-0-'/1 160 0.62 29 [ 0.18]
re:loopexec/7 40 0.62 29 [ 0.72]
re:process_repl_params/2 120 0.66 31 [ 0.26]
re:to_binary/2 120 0.66 31 [ 0.26]
erlang:integer_to_list/2 144 0.68 32 [ 0.22]
binary:split/3 32 0.68 32 [ 1.00]
idna_unicode:lookup/1 144 0.70 33 [ 0.23]
lists:reverse/1 121 0.74 35 [ 0.29]
hackney_bstr:to_binary/1 123 0.81 38 [ 0.31]
idna_unicode:lowercase/1 144 0.85 40 [ 0.28]
ets:lookup/2 48 0.89 42 [ 0.88]
string:chars/3 432 0.93 44 [ 0.10]
erlang:integer_to_list/3 288 0.98 46 [ 0.16]
'Elixir.Poison.Parser':skip_whitespace/1 122 1.17 55 [ 0.45]
hackney_http:execute/2 72 1.34 63 [ 0.88]
'Elixir.Poison.Parser':string_chunk_size/2 507 1.34 63 [ 0.12]
erlang:iolist_to_binary/1 192 1.34 63 [ 0.33]
erlang:send/3 36 1.40 66 [ 1.83]
hackney_bstr:to_lower/1 141 1.47 69 [ 0.49]
proplists:get_value/3 408 1.53 72 [ 0.18]
erts_internal:port_control/3 32 1.68 79 [ 2.47]
hackney_url:partial_pathencode/2 496 2.04 96 [ 0.19]
'Elixir.Poison.Encoder.BitString':chunk_size/3 692 2.06 97 [ 0.14]
binary:split/2 87 2.31 109 [ 1.25]
erts_internal:port_command/3 8 3.06 144 [ 18.00]
hackney_bstr:'-to_lower/1-lbc$^0/2-0-'/2 1567 3.44 162 [ 0.10]
re:run/3 80 3.67 173 [ 2.16]
erlang:setelement/3 489 5.35 252 [ 0.52]
hackney_bstr:char_to_lower/1 1426 5.84 275 [ 0.19]
------------------------------------------------------------ ----- ------- ---- [----------]
Total: 16645 100.00% 4709 [ 0.28]
The advice for async code is to find
something on the page that will be visible once the async action has completed (find
will block for up to 3 secs by default). This works great but there's a problem in that find
is chainable and narrows the scope of any finds further on in the chain.
session
|> visit("http://localhost:28080/?authAdapter=test&apiBasePath=%2F%2Flocalhost%3A4001%2F")
|> fill_in("Add new feed", with: "Lobby")
|> send_keys([:enter])
|> find(".side-nav__list-item a", text: "Lobby") # wait for async action to complete
|> fill_in("What's got you thinking?", with: "hello")
The last line fails because in this scenario the textarea that it's attempting to fill in is not inside the scope specified by the previous find, ".side-nav__list-item a", text: "Lobby".
There's a few options here:
1) Introduce a command that waits for something to appear
|> waitFor(".side-nav__list-item a", text: "Lobby") # wait for async action to complete
2) Allow the find scope to be reset
session
|> visit("http://localhost:28080/?authAdapter=test&apiBasePath=%2F%2Flocalhost%3A4001%2F")
|> fill_in("Add new feed", with: "Lobby")
|> send_keys([:enter])
|> find(".side-nav__list-item a", text: "Lobby") # wait for async action to complete
|> resetScope
|> fill_in("What's got you thinking?", with: "hello")
3) Don't make find
chainable
...seeing as you can always express chained finds with a single find
4) Introduce something like tap
session
|> visit("/page.html")
|> tap(fn session ->
session
|> users
|> find(".user", count: 3)
|> List.first
|> find(".user-name")
end)
|> continue with original scope
I have a frontend which depends on various options, like screen resolution or user agent.
I haven't found ability to mock user agent unfortunately.
I have configured wallaby to take screenshots when something went wrong.
This was fine when only a single test failed, but when there are multiple ones failing I do have problems to assign the correct screenshot to the test.
So it would be nice if either the testsname or some info from conn
("#{method}-#{path}"
) could be part of the name of the screenshot.
Using css selectors is a convenient and familiar construct for web developers. However, css has limitations that make it hard for wallaby to generate efficient queries. In fact most queries require multiple round trips through the webdriver before they can be resolved. Multiple queries means a larger opportunity for race conditions or stale dom nodes. These are edge cases that we have to handle explicitly because if we don't they creep into the users experience.
I propose this solution. We continue to allow people to use css selectors. But under the hood we convert those css selectors into a proper xpath query. Xpath would allow us to run all of our filters as a single query. Its well supported by webdriver; we already use it for all of the actions.
A side benefit of xpath would be that the query logic could be greatly simplified. Currently its a highly stateful and error prone process.
The largest downsides to generating xpath for users is that it could be surprising if they discover any bugs in our implementation. Its also impossible to use pseudo selectors like :hover
in xpath. We will need to call this out in our documentation and make sure users have a good escape hatch from xpath if need be.
In the future I want to encourage users to create page modules; re-usable functions that can encapsulate complex interactions on the page. Having a robust query generator is a good first step towards that goal.
Hello, I have a signup form:
<form accept-charset="UTF-8" action="/signup" method="post">
<input name="_csrf_token" type="hidden" value="...">
<input name="_utf8" type="hidden" value="โ">
<div class="form-group">
<label>Name</label>
<input class="form-control" id="user_name" name="user[name]" type="text">
</div>
<div class="form-group">
<label>Email</label>
<input class="form-control" id="user_email" name="user[email]" type="text">
</div>
<div class="form-group">
<label>Password</label>
<input class="form-control" id="user_password" name="user[password]" type="password">
</div>
<div class="form-group">
<input class="btn" type="submit" value="Submit">
</div>
</form>
Then, on the Wallaby test, I try to fill the inputs (Name, Email, Password). I've tried this:
fill_in(session, "Name", with: "Jaime")
and also this:
fill_in(session, "#user_name", with: "Jaime")
But both will fail with:
1) test signs up a user (Rocket.SignupTest)
test/acceptance/signup_test.exs:4
** (Wallaby.ElementNotFound) Could not find element
stacktrace:
(wallaby) lib/wallaby/node.ex:342: Wallaby.Node.retry/2
(wallaby) lib/wallaby/node.ex:81: Wallaby.Node.fill_in/3
test/acceptance/signup_test.exs:8
I could make it work with the name attribute:
fill_in(session, "user[name]", with: "Jaime")
I'm on Elixir 1.2.5, Phoenix 1.1.4, Ecto 1.1.7, Phantomjs 2.1.1.
I'm trying to access the <title>
element in a HTML document, so that I can assert its contents, but I'm not having any luck. Is this possible with Wallaby at the moment?
I've tried the following:
Given the following html snippet from a "home-page":
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Title</title>
</head>
<body>
This is the home-page
</body>
</html>
And the following tests:
defmodule MyApp.HomeFeatureTest do
use MyApp.FeatureCase
test "home-page body is present", %{session: session} do
text =
session
|> visit("/")
|> find("body")
|> text
assert text == "This is the home-page"
end
test "home-page head title is present", %{session: session} do
text =
session
|> visit("/")
|> find("title", visible: false)
|> text
assert text == "My Title"
end
test "home-page head title is present, part 2", %{session: session} do
text =
session
|> visit("/")
|> find("head title", visible: false)
|> text
assert text == "My Title"
end
end
2 out of 3 tests fail:
$ mix test test/features/homepage_feature_test.exs
1) test home-page head title is present (MyApp.HomeFeatureTest)
test/features/homepage_feature_test.exs:27
Assertion with == failed
code: text == "My Title"
lhs: ""
rhs: "My Title"
stacktrace:
test/features/homepage_feature_test.exs:34
2) test home-page head title is present, part 2 (MyApp.HomeFeatureTest)
test/features/homepage_feature_test.exs:37
Assertion with == failed
code: text == "My Title"
lhs: ""
rhs: "My Title"
stacktrace:
test/features/homepage_feature_test.exs:44
Finished in 1.1 seconds (0.1s on load, 0.9s on tests)
3 tests, 2 failures
Mentioned in README:
Wallaby.DSL.Navigation.visit(session, Phoenix.Ecto.SQL.Sandbox.path_for(YourApp.Repo, self()))
Should be either Wallaby.Driver.visit
or Wallaby.Session.visit
? (they're the only two visit
methods present)
So if you call click("Submit")
and it fails:
Could not click "Submit" because it was not found on the page
Would be extra cool if it found all the link text on the page and used String.jaro_distance
to find potential mispellings.
Could not click "Submit" because it was not found on the page
Did you mean "submit"?
It could potentiall work the same for CSS
Could not find ".my-css"
If you spelled it wrong
Could not find ".my-css"
Did you mean ".my_css"?
These problems come up a lot so these error messages would be insanely useful!
We have a page where clicking on a button will cause other buttons to appear further down the page.
Wallaby.Node.Find
would be unable to find the buttons that appeared further down the page. Originally I thought it was a problem with the nodes not rendering in time, but I could see >part< of buttons inside the failure screenshots.
Increasing the window size for the session fixed the problem
This behavior seems odd though - one would think that phantom would be able to click anything on the page regardless of if it is scrolled off.
When interacting with a form and filling in a form field which already contains pre-existing text, fill_in appends to the content.
Given a form field with an input containing a value "Foo"
session |> fill_in("Name", with: "Bar")
The input field will have "FooBar" as its value
Has file uploading been implemented in Wallaby? I looked but didn't see anything that would match a file input in xpath.ex nor did I see anything in the docs to indicate support.
subj.
@keathley I found that
find("identificator", visible: false) # => works well
click_on("identificator", visible: false) # => doesn't work, it writes me element was found but its not visible to a real user
The modules docs and stuff need to be updated / included.
This test will fail based on existing elements in page_1.html
test "not finding an invisible element", %{session: session, server: server} do
session
|> visit(server.base_url <> "page_1.html")
assert_raise Wallaby.ElementNotFound, fn ->
find(session, "#invisible", count: :any)
end
assert find(session, "#visible", count: :any) |> length == 1
end
Wallaby.js is a test runner for JavaScript that's been around for a while (relatively speaking), there's big potential for confusion here.
When using boilerplate instructions & trying to find an element on the page, the page was never fully rendering (in Allocations, we captured screenshots of the "Loading" page):
session
|> sign_in
|> visit("/")
# at this point, the page is stuck in the loading screen and never finds the project, even after:
:timer.sleep(5000)
session
|> find(".project", count: 1)
It seems like a setup issue, and the bug starts via this commit: f1bcc46
Slightly related to a previous issue, I'm trying to create a helper that steps through user sign in's.
Esteem.UserHelper.sign_in/3
is only ever invoked from another module which is using AcceptanceCase
defmodule Esteem.UserHelper do
use Wallaby.DSL
def sign_in(session, email, password) do
session
|> visit("/sessions/new")
|> fill_in("Your email", with: email)
|> fill_in("Password", with: password)
|> click_button("Submit")
end
end
When it runs, I get the following error
1) test user signs out and is redirected to login page (Esteem.UserSessionsTest)
test/acceptance/user_sessions_test.exs:16
** (FunctionClauseError) no function clause matching in Wallaby.DSL.Actions.fill_in/3
stacktrace:
(wallaby) lib/wallaby/dsl/actions.ex:59: Wallaby.DSL.Actions.fill_in(%Wallaby.Session{base_url: "http://localhost:65372/", id: "195cd7d0-3f74-11e6-b0d4-d76992d84dcf", screenshots: [], server: #PID<0.467.0>}, "Password", [with: 'test'])
(esteem) test/support/user_helper.ex:8: Esteem.UserHelper.sign_in/3
test/acceptance/user_sessions_test.exs:18
It seems that the second fill_in
(in this case, "Password") is getting a clause error.. and I honestly have no idea why.
Here is another test in the suite that works just fine, so I don't know why chaining fill_in
's would be an issue
defmodule Esteem.UserSignsUpTest do
use Esteem.AcceptanceCase, async: true
test "user signs up", %{session: session} do
session
|> visit("/")
|> fill_in("First name", with: "Luke")
|> fill_in("Last name", with: "Rollans")
|> fill_in("Email", with: "[email protected]")
|> fill_in("Password", with: "123456")
|> fill_in("Organisation name", with: "Marvellous Mutants")
|> click_button("Submit")
notice =
session
|> find(".alert-info")
|> text
assert notice == "Signed up successfully!"
end
end
I am really sorry to post again, but I just don't know how to proceed.. and hopefully this might help someone else in future :)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.