Capybara Fires Test Before JS Finished - javascript

I have a capybara test that checks to see if a class is found on the page. The class allows the user to view a sidebar. Here is the test below.
feature 'Lesson Sidebar', js: true do
before do
visit course_lesson_path
end
context 'persists the lesson sidebar display' do
before do
find('#lesson-sidebar__lesson-2').trigger('click')
end
scenario do
expect(page).to have_selector('.lesson-page__sidebar--expanded', visible: false)
end
end
end
The JS code simply tacks on the class to the sidebar element when #lesson-sidebar__lesson-2 is clicked. The code is within a document ready call.
$(document).ready(function() {
$('.lesson-subnav__heading').on("mousedown", "#lesson-sidebar__lesson-2", function (e) {
$('#sidebar').addClass('lesson-page__sidebar--expanded')
})
})
Here is the error response I received.
Capybara::ElementNotFound:
Unable to find css "#lesson-sidebar__lesson-2"
This is my problem. The test will randomly fail. Not only for this test but for other tests within this page. My assumption is that the test is running before the JS has a chance to finish which is why the test fails sometimes. How do I fix this so the test passes every time?

Update: The posting of the actual error shows that it's the find in the before block thats not finding the element, which means this has nothing do with the wait_for_ajax call or event handler binding. If doing something like find('#lesson-sidebar__lesson-2', wait: 20).click doesn't make the error go away, then you have an issue with your page loading (or you've mistyped the selector) although I would not expect that to create an intermittent failure. Check your test logs for what requests were actually made and/or add
sleep 10
page.save_screenshot
before the find/click and look at the page to see if it's what you expect. Another thing to check would be that you're showing your JS errors in your driver config (I assume your using poltergeist since most people who default to .trigger generally are)

Related

CypressError: cy.find() failed because this element is detached from the DOM

There are several questions with this error but I seem to get this error while the element is certainly there.
This is the test code:
it.only('changes product attributes and sku', () => {
cy.visit('/product/hoganrebel-r261-sneaker-6708K62AZC-grey/M0E20000000DX1Y');
cy.get('[data-test=product-data]')
.then(($product) => {
cy.wrap($product)
.find('[data-test=attribute-select-Size]')
.select('6');
cy.url().should('include', 'M0E20000000DX20');
cy.wrap($product)
.pause()//here I can see the element but when I step into find it fails
.find('[data-test=attribute-select-Size]')
.select('7');
cy.url().should('include', 'M0E20000000DX22');
});
});
I install dependencies with npm ci and run the project with yarn start.
In another terminal tab I start the test with ./node_modules/cypress/bin/cypress open and then choose "run all specs" (only runs the it.only test). When it pauses I can clearly see the element but when opening the devtools and run document.querySelector('[data-test="attribute-select-Size"]') in the console it says null. Then I right click on the clearly visible element and choose inspect it shows me the element. Then go back to the console and run document.querySelector('[data-test="attribute-select-Size"]') again it gives me the element.
Adding a .wait(5000) before the pause does not solve this, could try to wait for xhr to finish but it has already finished after 5 seconds so that is not likely to be the problem.
This is obviously a bug but I'm not sure how to work around this issue.
Detached elements are usually due to the framework re-writing sections of DOM with the same data selector ([data-test=product-data]) that it had before, but creating a new instance of the element.
When you hold on to a reference with test code like .then(($product) => { cy.wrap($product)..., $product is no longer valid after the re-write (I guess caused by .select('6') but I didn't dig into your app code to verify).
The short answer is repeat the cy.get('[data-test=product-data]') selection, there is no reason to grab a specific reference to that element.
it('changes product attributes and sku', () => {
cy.visit('/product/hoganrebel-r261-sneaker-6708K62AZC-grey/M0E20000000DX1Y');
cy.get('[data-test=product-data]')
.find('[data-test=attribute-select-Size]')
.select('6'); // DOM changes here?
cy.url().should('include', 'M0E20000000DX20');
cy.get('[data-test=product-data]') // fresh DOM query
.find('[data-test=attribute-select-Size]')
.select('7');
cy.url().should('include', 'M0E20000000DX22');
});

find doesnt find button which is clearly in dom when testing ember view

Hey I have following problem with qunit acceptance test:
test('', assert => {
selectSearch('.address-lookup', '359 Paterson Road');
fillIn('input.mailing-address-city', 'Arlington');
fillIn('input.mailing-address-postal-code', '76002');
andThen(() => {
const discoverDriverBtn = find('.discover-driver-button')
click(discoverDriverBtn);
});
})
button .discover-driver-button is in template in if statement whe all inputs are filled
I see this button in qunit preview when running test and after stopping it I can find it in console
yet test is failing with message:
Error: Element [object Object] not found.
when I deleted if from view and displayed button all time the test passed
I think it might be connected somehow with run loop
You indicated button .discover-driver-button in template is visible only if all inputs are filled. However; the test code snippet you provided does not perform a fill with ember-power-select. Please have a look at the documentation. selectSearch helper only makes a search with the select field; whereas selectChoose does in fact makes the actual selection. If you use selectChoose in place of selectSearch (or after selectSearch) your test will most likely pass.

Selenium does not execute javascript

I am using capybara with Selenium as its driver. I am trying to click on an element, which when clicked it will reveal a div, but the click never invokes javascript to do just that.
Below is the code I have
scenario 'currently used transport mode cannot be re-selected' do
expect(page).to have_css("h2.summary")
expect(find('h2.summary').text).to eq("Single event")
expect(page).to have_content("Change journey")
page.click_link("Change journey")
expect(find('#travel-times-preview').visible?).to be_truthy # FAILS here because of previous step not working
end
error message
Capybara::ElementNotFound: Unable to find css "#travel-times-preview"
html
<a class="change-journey gray-text" href="#">Change journey</a>
javascript code to execute
$(".change-journey").on("click", function(e){
var target = $(this).data("preview-target");
$('[data-preview-toggle="'+ target +'"]').toggleClass("hidden");
if($(this).text().indexOf('Change journey') > -1){
$(this).text("Close Preview");
}else{
$(this).text("Change journey");
}
e.preventDefault();
});
database cleaner setup
config.before(:suite) do
if config.use_transactional_fixtures?
raise(<<-MSG)
Delete line `config.use_transactional_fixtures = true` from rails_helper.rb
(or set it to false) to prevent uncommitted transactions being used in
JavaScript-dependent specs.
During testing, the Ruby app server that the JavaScript browser driver
connects to uses a different database connection to the database connection
used by the spec.
This Ruby app server database connection would not be able to see data that
has been setup by the spec's database connection inside an uncommitted
transaction.
Disabling the use_transactional_fixtures setting helps avoid uncommitted
transactions in JavaScript-dependent specs, meaning that the Ruby app server
database connection can see any data set up by the specs.
MSG
end
end
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, type: :feature) do
# :rack_test driver's Rack app under test shares database connection
# with the specs, so we can use transaction strategy for speed.
driver_shares_db_connection_with_specs = Capybara.current_driver == :rack_test
if driver_shares_db_connection_with_specs
DatabaseCleaner.strategy = :transaction
else
# Non-:rack_test driver is probably a driver for a JavaScript browser
# with a Rack app under test that does *not* share a database
# connection with the specs, so we must use truncation strategy.
DatabaseCleaner.strategy = :truncation
end
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
While i can see the link being clicked, the underlying javascript is not executed.
Assuming you've looked into the dev console in the firefox window that opens and there are no JS errors, there are a few potential reasons for the behavior you're seeing, none of which have anything to do with e.preventDefault() (When you hear hoofs think horses, not zebras)
The link you show doesn't have a data-preview-target attribute so there is nothing for the click handler to read and append to data-preview-toggle= so nothing gets toggled. It's possible that data is added by other JS in your app so is set as a property and wasn't an attribute in the HTML (or you just chose to leave that detail out of the element you showed) which moves to #2
Your .on JS code is running before the element exists, and therefore not attaching. This can show up in test mode when it concatenates all the JS into one file because the JS is loaded faster than when multiple requests are made in dev mode. This would show up as the link text not changing since the click handler isn't run at all. If that is the case delay attaching the listener until the DOM is loaded.
This is the first JS supporting test you're writing, the elements being hidden/shown are generated from database records, and you haven't disabled transactional tests and/or configured database_cleaner correctly. This leads to objects you create in your tests not actually being visible in your app, so the element you're expecting to be on the page isn't actually there. You can verify that by pausing the test and looking in the HTML to see if it is or is not actually on the page.
Based on the error you provided this is not the cause of your issue, just adding for completeness of the answer: click_link clicks the link and returns immediately. That means the test continues running while the action triggered by the click continues. This can lead to a race condition in your code (if you have Capybara.ignore_hidden_elements = false set - bad idea) where your code finds the element and checks its visibility before it's changed. Because of that your final step should be written as below because it will wait/retry for the element to become visible
expect(page).to have_css('#travel-times-preview', visible: true)
As an aside, your test code can be improved and sped up by using the features Capybara provides
scenario 'currently used transport mode cannot be re-selected' do
expect(page).to have_css("h2.summary", text: "Single event") # do it in one query
page.click_link("Change journey") # click_link will find the content anyway so no need to check for it before
expect(page).to have_css('#travel-times-preview') # By default Capybara only finds visible elements so checking visibility is pointeless, if you've changed that default see #4
end

nsIDOMWindowUtils.isInModalState() not working

I get the utils of the window like this:
var utils = Services.wm.getMostRecentWindow('navigator:browser').
QueryInterface(Components.interfaces.nsIInterfaceRequestor).
getInterface(Components.interfaces.nsIDOMWindowUtils);
then running this code says its not a function:
Services.prompt.alert(null, 'is modal?', utils.isInModalState())
//Exception: utils.isInModalState is not a function
even though it says in the documentation it is.
I am not able to run this code either:
utils.enterModalState()
It runs without error but the window does not enter or leave modal state.
The .isInModalState property is actually tagged [noscript], so it is not available in Javascript, only C++.
.enterModalState() doesn't do what you think it does. It does not make a window UI-modal (like an alert dialog). It just essentially suspends scripts and event processing in the window.

cannot perform operations on a Metamorph that is not in the DOM

So I am using Ember 1.5.1 and Ember-data 1.0.0 beta7
I have the following routes
App.Router.map ->
#resource "items"
#resource 'item', path: '/items/:id
Item is fetched from server. These two routes work fine if I start on that route; weird thing happens when I start on a item page (/items/1), then I try to navigate to /items/ through a link with action handled by a function this.transitionToRoute("items") in ItemController. I got the following error:
Attempting transition to items ember.js?body=1:3524
Transition #2: items: calling beforeModel hook ember.js?body=1:3524
Transition #2: items: calling deserialize hook ember.js?body=1:3524
Error while loading route: Error: Cannot perform operations on a Metamorph that is not in the DOM.
at Metamorph.checkRemoved (http://localhost:3000/assets/ember.js?body=1:27009:15)
at Metamorph.html (http://localhost:3000/assets/ember.js?body=1:26979:12)
at Object.DOMManager.empty (http://localhost:3000/assets/ember.js?body=1:27974:16)
at Object.Ember.merge.empty (http://localhost:3000/assets/ember.js?body=1:25139:21)
at Ember.CollectionView.Ember.ContainerView.extend.arrayWillChange (http://localhost:3000/assets/ember.js?body=1:25962:25)
at Object.sendEvent (http://localhost:3000/assets/ember.js?body=1:2600:14)
at Ember.Array.Ember.Mixin.create.arrayContentWillChange (http://localhost:3000/assets/ember.js?body=1:15317:11)
at superFunction [as _super] (http://localhost:3000/assets/ember.js?body=1:7726:16)
at RecordArray.extend.arrayContentWillChange (http://localhost:3000/assets/ember-data.js?body=1:7624:28)
at superWrapper [as arrayContentWillChange] (http://localhost:3000/assets/ember.js?body=1:1295:16)
Transition #2: items: transition was aborted
However, I click on the link again, and the transition succeeds. Then everything works fine no matter how I navigate between the two routes.
My initial guess is that not all records have been loaded, when I try to transition, but even though I start on /items/ and go to /items/1 then try to go back /items/, I still get the same error.
Try this, add following code in your action
Ember.run.scheduleOnce('afterRender', this, function(){
this.transitionToRoute("items");
});
UPDATE:
Ok just checked your stacktrace. The problem is not because of transitionTORoute. As this is metamorph issue, its little hard to debug directly. This may actually caused by wrong handlebars expressions.
As pointed out in this link, a simple issue can be using HTML comment blocks.
{{! This comment will not be in the output }}
< !-- This comment will be in the output -- >
Manually manipulating the DOM. If you manually remove the script tags, then, well they won't be found.
Malformed HTML. Let's say you left an open div, then the metamorph-##-end tag will get nested at a different level than the start tag.
I have extracted lot of these points from other questions which i think may help you

Categories