Nokogirl::HTML.parse trying to find a <li> element by its class - ruby-on-rails

I'm trying to get the contents of elements
The last line fails for invalid syntax
browser=Watir::Browser.new(:chrome)
doc=Nokogiri::HTML.parse(browser.html)
list=doc.css("ul").sort { |x,y| y.css("li").count <=> x.css("li").count }.first
rows=list.css("class=brand-content")

Try rows=list.css(".brand-content").
'css' means it uses CSS selector syntax. li.brand-content would also work.

Related

Cannot get class name from node after locating it in playwright

I have an SVG object like that:
<svg class="class-a color-green marker" .../>
In Playwright I want to get an exact list of classes of this element. I use the following code to locate it:
page.locator(".status-marker").first
The node is located properly, but when I call evaluate("node => node.className") on it, I get an empty dict, like the locator stripped all information about classes at all.
In general, it doesn't matter how I get the handle of this element, I always get an empty dict on evaluate("node => node.className").
Calling page.locator(".status-marker").first.is_visible() returns True, so the object exists.
Also if I run page.locator(".status-marker").first.evaluate("node => node.outerHTML") I'll get the full HTML of that node, that does have the class name included. I could parse that, but it would be pretty clunky solution.
I found out that I could use expect(locator).to_have_class(), but If the node has more than one class I need to put all of them for it to pass, when I care only on one of them (the other classes are dynamically generated, so I can't even know about them during tests).
Edit:
Here's some additional sample:
assert page.locator(".marker").first.evaluate("node => node.className") == {}
expect(page.locator(".marker").first).to_have_class("text-green-1")
The first assert passes - the evaluate("node => node.className") returns an empty dict. The expect() fails with the following error:
AssertionError: Locator expected to have class 'text-green-1'
E Actual value: inline pr-2 text-green-1 marker svelte-fa s-z-WEjw8Gh1FG
I've found a way to reproduce it (it happens to me in font awesome plugin for svelte):
def test_svelte_fa(page):
page.goto("https://cweili.github.io/svelte-fa/")
item = page.locator(".svelte-fa").first
assert item.is_visible()
assert "svelte-fa" in item.evaluate("node => node.className")
In your example, the className of an SVG is an SVGAnimatedString object. Which is not serializable.
If you do JSON.stringify($('.svelte-fa').className) on the browser, you will see that the value is {}.
Values returned by the evaluate function needs to be serializable.

No space between elements in a Thymeleaf each loop

With Thymeleaf when I do :
<span th:each="item : ${X}">1</span>
I get :
<span>1</span>
<span>1</span>
<span>1</span>
...
How can I have this :
<span>1</span><span>1</span><span>1</span>...
?
It is processed with a new line due the next issue, to improve readability:
https://github.com/thymeleaf/thymeleaf/issues/113
At the moment, I don't think you could avoid the break line without patching it. So, if you want to have this behavior you could patch the class AbstractIterationAttrProcessor. How? Just create a class with the same name and package in your project, and copy the content of the original one:
Package: org.thymeleaf.processor.attr
Class Name = AbstractIterationAttrProcessor
Then change the conditional in line 133 to avoid the new line if it is an span element. It would be like next:
if (preserveWhitespace && index > 0 && !element.getOriginalName().equals("span")) {
parentNode.insertBefore(element, new Text(whitespace));
}
I've just added !element.getOriginalName().equals("span") to the condition. With this patch your span will be processed as:
<span>1</span><span>1</span>
I've opened an issue due your question:
https://github.com/thymeleaf/thymeleaf/issues/586

Is there a way to use {#select } tag inside array in dust?

Here is the code snippet I tried to achieve index based selection of elements from an array:
{#result}
{#select key={$idx}}
{#lte value=3}
<p>{notes}</p>
<p style="color:grey;">{createdBy}-{createdDate}</p>
{/lte}
{/select}
{/result}
But above code throws error "SyntaxError: Expected end tag for result but it was not found". Can anyone please suggest any fix for this error?
It looks like the error is the curly braces surrounding $idx. Dust references in parameters don't use curly braces (e.g. {#select key=$idx}) or they must have quotes around them (e.g. {#select key="{$idx}"}). So, your template would look something like:
{#result}
{#select key=$idx}
{#lte value=3}
<p>{name}</p>
<p style="color:grey;">{createdBy}-{createdDate}</p>
{/lte}
{/select}
{/result}

Ruby, accessing a nested value in a hash

I have the following hash. Using ruby, I want to get the value of "runs". I can't figure out how to do it. If I do my_hash['entries'], I can dig down that far. If I take that value and dig down lower, I get this error:
no implicit conversion of String into Integer:
{"id"=>2582, "entries"=>[{"id"=>"7", "runs"=>[{"id"=>2588, ...
Assuming that you want to lookup values by id, Array#detect comes to the rescue:
h = {"id"=>2582, "entries"=>[{"id"=>"7", "runs"=>[{"id"=>2588}]}]}
# ⇓⇓⇓⇓⇓⇓⇓ lookup element with id = 7
h['entries'].detect { |e| e['id'] == 7 }['runs']
.detect { |e| e['id'] == 2588 }
#⇒ { "id" => 2588 }
As you have an array inside the entries so you can access it using an index like this:
my_hash["entries"][0]["runs"]
You need to follow the same for accessing values inside the runs as it is also an array.
Hope this helps.
I'm not sure about your hash, as it's incomplete. So , guessing you have multiple run values like:
hash = {"id"=>2582, "entries"=>[{"id"=>"7", "runs"=>[{"id"=>2588}]},
{"id"=>"8", "runs"=>[{"id"=>2589}]},
{"id"=>"9", "runs"=>[{"id"=>2590}]}]}
Then, you can do
hash["entries"].map{|entry| entry["runs"]}
OUTPUT
[[{"id"=>2588}], [{"id"=>2589}], [{"id"=>2590}]]

Capybara: Test that JavaScript runs without errors

I would like to write a request spec that verifies the loading and execution of javascript on a given page without any errors.
I know I can add something to the DOM at the end of my JavaScript file and assert the presence of this content, but that feels like a hack and forces me to pollute my code for testing reasons. I'd prefer to do something along the lines of.
visit some_path
page.should succesfully_run_javascript
You can achieve this with the following piece of code in your tests.
page.driver.console_messages.each { |error| puts error }
expect(page.driver.console_messages.length).to eq(0)
The first line prints out an errors, handy for seeing what's going on. The second line causes the test to fail.
One way that's potentially not very polluting is to collect any errors by adding something like this to head (keep in mind this will override any onerror handler you may already have):
<script>
window.errors = [];
window.onerror = function(error, url, line) {
window.errors.push(error);
};
</script>
You could then do something like:
page_errors = page.evaluate_script('window.errors')
and assert on the array being empty. You could output the errors otherwise...
Note: It's important to add the scriptlet to head (potentially as the first scriptlet/script tag) as it needs to be one the first executed scripts.
A problem with using page.driver.console_messages is that it doesn't clear between tests. So if you want to only assert that there are no errors on a particular test the spec might fail due to a previous test.
You can scope these errors to a particular spec by saving the last console.log timestamp.
Helper method:
def assert_no_js_errors
last_timestamp = page.driver.browser.manage.logs.get(:browser)
.map(&:timestamp)
.last || 0
yield
errors = page.driver.browser.manage.logs.get(:browser)
.reject { |e| e.timestamp > last_timestamp }
.reject { |e| e.level == 'WARNING' }
assert errors.length.zero?, "Expected no js errors, but these errors where found: #{errors.join(', ')}"
end
And then use it like:
def test_somthing_without_js_errors
assert_no_js_errors do
# TODO: Write test
end
end

Resources