I can't count the number of DOM elements on a site written in React.
/// <reference types="cypress" />
context('Checking all components', () => {
beforeEach(() => {
cy.visit('https://news-israel.com');
});
it('Checking posts', () => {
cy.get('.posts-wrapper').find('a').should('exist');
cy.get('.posts-wrapper').find('a').its('length').should('be.gte', 100);
});
});
In this case, it doesn't find the "a" tags because React rendering them asynchronously and dynamically.
The "post-wrapper" class finds, followed by an exception:
The following error originated from your application code, not from Cypress.
Cannot read property 'substr' of undefined
When Cypress detects uncaught errors originating from your application it will automatically fail the current test.
How to correctly count the number of elements in this case, so that you can "wait for the elements"?
The site I'm testing is in production - https://news-israel.com
The error is coming from the app itself, and ultimately should be fixed in the app source.
But see this note in the log
This behavior is configurable, and you can choose to turn this off by listening to the uncaught:exception event.
This links to an event handler you can use to debug. Add this to the top of the test to suppress the test failing when the error occurs.
Cypress.on('uncaught:exception', (err, runnable) => {
// returning false here prevents Cypress from
// failing the test
return false
})
Now the test works, provide you use the correct class posts-wrapper not post-wrapper.
If you are able to fix the source, the error comes from the react-typed library, which is used in BreakingNews.js at line 75
<Typed
strings={posts}
typeSpeed={15}
backSpeed={10}
backDelay={5000}
loop
/>
the posts variable is initially undefined, so you need a fallback value, e.g strings={posts || []}
To globally Handle Uncaught exceptions, Go to cypress/support/index.js and write:
Cypress.on('uncaught:exception', (err, runnable) => {
return false
})
Now to count the number of elements you do it via each() or by using Cypress.$
Using each():
cy.get('div.post-title').each((ele, list) => {}).then((list) => {
cy.log(list.length)
})
Using Cypress.$
cy.get('div').find('.post-title').then(ele => {
cy.log(Cypress.$(ele).length)
})
OR, As suggested by #Hiram
cy.get('div.post-title').then(ele => {
cy.log(Cypress.$(ele).length)
})
Related
So i am using cypress to log into google and create a google form. I have gotten through the login, but now that I want to select create new form, I am seeing cross origin errors which seem to be blocking such actions. Can anyone help me out here? Heres my code. Its the click on #1f that is not working
'''
describe('Test', function () {
it('logs into google', function () {
Cypress.on('uncaught:exception', (err, runnable) => {
return false
})
cy.visit('http://docs.google.com/forms/u/0')
cy.xpath("//input[#type='email']").type("userAlktest#gmail.com");
cy.xpath("//span[contains(text(),'Siguiente')]").click();
cy.wait(5000);
cy.xpath("//input[#type='password']").type("1Testagain");
cy.xpath("//span[contains(text(),'Siguiente')]").click();
cy.wait(5000);
///cy.xpath("//span[contains(text(),'Probar otra manera')]").click();
/// cy.wait(5000)
/// cy.xpath("//span[contains(text(),'Confirmar')]").click();
/// cy.wait(5000)
//cy.xpath("//span[contains(text(),'Untitled form')]").click();
//cy.wait(5000)
cy.get('#:1f').click();
cy.wait(5000);
'''
This is a cypress tradeoff and you can read about it from the cypress docs. In a nutshell, if you want to access two different URLs in one test you cannot. For this scenario, you have to write two tests where each URL is accessed. For e.g.
NOT OK ❌
it('navigates', () => {
cy.visit('https://apple.com')
cy.visit('https://google.com') // this will error
})
OK ✅
it('navigates', () => {
cy.visit('https://apple.com')
})
// split visiting different origin in another test
it('navigates to new origin', () => {
cy.visit('https://google.com') // yup all good
})
Maybe it would help it would help more if more clarity was provided on what the reason for creating a Google form.
Back to the question, the original domain your test is bounded is https://google.com. I imagine removing the describe() block and having two it() with no describe() could help you out.
I am trying to build my Gatsby site to deploy to production, but I have run into an issue which I believe is related to one line of code within my application where I use window to redirect to an external url that is retrieve from a GET request made on a button click. I tried to read through the documentation on this issue and attempted to use their window check conditional, but my redirect either didn't work or I received an error related to Can't resolve 'module'. Is there an alternative route to either replacing the use of window all together or fixing why this runs during build and causing the error?
Error:
failed Building static HTML for pages - 2.611s
ERROR #95312
"window" is not available during server side rendering.
Error for code checking for window:
WebpackError: ReferenceError: window is not defined
Code:
authClick(e) {
e.preventDefault();
this.setState({ authorizing: true }, ()=> {
axios.get(auth_url)
.then((res) => {
this.setState({
authorizing: false
})
window.location.replace(res.data) // Window call
// Attempt to fix the issue based on documentation
// if (typeof window !== `undefined`){
// const module = require("module")
// window.location.replace(res.data)
// }
}).catch((err) => {
console.log(err)
})
});
}
// Component
<button className="inline-flex items-center" onClick={this.authClick} disabled={this.state.authorizing}>
Auth
</button>
const isBrowser = () => typeof window !== "undefined"
isBrowser() && window.location.replace(res.data)
If you want to use window.location then you have to check first then use it isBrowser() && window.location.replace(res.data) instade of window.location.replace(res.data)
If you don't need to require any module based on the window availability, don't do it. In your case, you only need to make a redirection, not to importing any module.
Just:
authClick(e) {
e.preventDefault();
this.setState({ authorizing: true }, ()=> {
axios.get(auth_url)
.then((res) => {
this.setState({
authorizing: false
})
if (typeof window !== `undefined`){
window.location = res.data
}
}).catch((err) => {
console.log(err)
})
});
}
Note the change of making a redirection with window.location but you can also use your window.location.replace if you want.
The link to the documentation is correct the window is not available during the build process so you must define a check.
The term module in the documentation would be replaced by the actual name of the module you are referencing. Then you would wrap the import/require with the window check as mentioned in the documentation.
For your purposes the code would look like this:
...
if (typeof window !== `undefined`){
window.location.replace(res.data) // Window call
}
...
You would not need the require module since you are not importing a module.
Hope that clarifies.
Tip: put typeof window !== 'undefined' && window.whaterver-you-need to solve this.
I am UI testing an application containing OpenLayers. Since OpenLayers is for the most part drawing an SVG, directly testing effects via DOM is not always possible.
I would like to get the map object to check if the variables within it are set correctly after clicking an element outside of the map.
For this I put the map object in window.mapInstance.
During a test, I try this:
someTest: client => {
// regular Nightwatch testing omitted ...
// and finally:
client.execute(
function () { return window },
[],
function (r) {
console.log(r.value.mapInstance.getView().getZoom())
client.end()
}
)
}
That throws me an error:
Error while running .executeScript() protocol action: javascript error: circular reference
r.value is then set to the error object, and the whole thing fails.
What am I doing wrong?
As the test suite grows I need to be able to run something in BeforeSuite() which will connect to external suite and skip the suite if an external resource is unavailable.
Feature('External Server');
BeforeSuite((I) => {
// Check if server is available and skip all scenarios if it is not
});
Scenario('Login to the server', (I) => {
// Do not run this if the server is not available
})
I understand I could probably set a variable, but I think it would be nice if there was a way to tell the runner that a suite has been skipped.
The goal is to have a suite marked as skipped in the output eg:
Registration --
✓ Registration - pre-checks in 4479ms
✓ Registration - email validation in 15070ms
✓ Registration - password validation in 8194ms
External Server -- [SKIPPED]
- Login to the server [SKIPPED]
maybe prepend x before every scenario in your feature? example xScenario. I don't think codecept supports something similar to only for features. it currently works with scenarios only as far as I know.
you can use
Scenario.skip
in your step definition to skip a scenario.
Note: if any steps have been executed before skipping then it will still show it in the report
https://codecept.io/basics/#todo-test
My answer is compiled from a number of comments on the CodeceptJS github and stackoverflow. However, I can't recall the exact links or comments which helped me derive this solution, it's been at least a year, maybe two, since I started and have slowly modified this.
Edit: Found the github thread - https://github.com/codeceptjs/CodeceptJS/issues/661
Edit2: I wrote a post about "selective execution" (which avoids tagging unwanted tests with skip status) https://github.com/codeceptjs/CodeceptJS/issues/3544
I'll add a snippet at the bottom.
I'm on CodeceptJS 3.3.6
Define a hook file (eg: skip.js) and link it to your codeceptjs.conf.js file.
exports.config = {
...
plugins: {
skipHook: {
require: "../path/to/skip.js",
enabled: true,
}
}
...
}
The basic code in skip.js is
module.exports = function () {
event.dispatcher.on(event.test.before, function (test) {
const skipThisTest = decideSkip(test.tags);
if (skipThisTest) {
test.run = function skip() {
this.skip();
};
return;
}
});
};
I've defined a decision function decideSkip like so:
function decideSkip(testTags) {
if (!Array.isArray(testTags)) {
output.log(`Tags not an array, don't skip.`);
return false;
}
if (testTags.includes("#skip")) {
output.log(`Tags contain [#skip], should skip.`);
return true;
}
if (
process.env.APP_ENVIRONMENT !== "development" &&
testTags.includes("#stageSkip")
) {
output.log(`Tags contain [#stageSkip], should skip on staging.`);
return true;
}
}
(Mine is a bit more complicated, including evaluating whether a series of test case ids are in a provided list but this shows the essence. Obviously feel free to tweak as desired, the point is a boolean value returned to the defined event listener for event.test.before.)
Then, using BDD:
#skip #otherTags
Scenario: Some scenario
Given I do something first
When I do another thing
Then I see a result
Or standard test code:
const { I } = inject();
Feature("Some Feature");
Scenario("A scenario description #tag1 #skip #tag2", async () => {
console.log("Execute some code here");
});
I don't know if that will necessarily give you the exact terminal output you want External Server -- [SKIPPED]; however, it does notate the test as skipped in the terminal and report it accordingly in allure or xunit results; at least insofar as CodeceptJS skip() reports it.
For "selective execution" (which is related but not the same as "skip"), I've implemented a custom mocha.grep() utilization in my bootstrap. A key snippet is as follows. To be added either to a bootstrap anonymous function on codecept.conf.js or some similar related location.
const selective = ["tag1", "tag2"];
const re = selective.join("|");
const regex = new RegExp(re);
mocha.grep(regex);
I am moving into functional reactive code starting with rxjs and have some questions about how to best import it as I am getting mixed results.
Currently, I have a simple implementation where I check a web service for if a username already exists.
Here is the code that works perfectly:
$(window).load(function() {
// setup an observable
submission = Rx.Observable.fromEvent($("#submitbutton"), "click").map(
event => {
return "justsomeusername";
}
);
submission
.concatMap(username => {
return Rx.Observable.fromPromise(
$.get(
"{{ site.serveraddress }}/checkuser?&username=" + username
)
).catch(() => Rx.Observable.empty());
})
.subscribe(rval => {
console.log(rval);
});
});
This all works great BUT when I try to add a debounce like submission.debounce(500).concatMap((username) =>
Rx.js:8512 Uncaught TypeError: this.durationSelector.call is not a function
at DebounceSubscriber._next (Rx.js:8512)
at DebounceSubscriber.Subscriber.next (Rx.js:424)
at MapSubscriber._next (Rx.js:5715)
at MapSubscriber.Subscriber.next (Rx.js:424)
at HTMLButtonElement.handler (Rx.js:3913)
at HTMLButtonElement.dispatch (jquery.min.js:3)
at HTMLButtonElement.r.handle (jquery.min.js:3)
That is what appears in the console.
I think that the way I am importing or referencing the library is the cause, this is where there is some confusion.
I am importing like this at the top of an HTML page:
<script src="https://unpkg.com/#reactivex/rxjs#5.5.6/dist/global/Rx.js"></script>
If I try to import any other reference the Rx object seems not to exist? Does this have to do with the filename maybe becoming the object namespace?
For example, I downloaded all the latest rxjs release and in the dist see rx.all.js but if I import locally one of the latest dists like:
<script src="/myapp/javascript/rx.all.js"></script>
I get Rx undefined. What is the nuance occurring between these two different ways of reference.
What is the surefire way to import rxjs for client-side use? Is this the likely cause of the debounce not working (not having the full lib)?
Thanks!
You should use debounceTime for this, not debounce. debounce takes a function parameter which must determine dynamically the debounce time, whereas debounceTime can be used exactly as you state:
$(window).load(function() {
// setup an observable
submission = Rx.Observable.fromEvent($("#submitbutton"), "click").map(
event => {
return "justsomeusername";
}
);
submission
.debounceTime(500)
.concatMap(username => {
return Rx.Observable.fromPromise(
$.get(
"{{ site.serveraddress }}/checkuser?&username=" + username
)
).catch(() => Rx.Observable.empty());
})
.subscribe(rval => {
console.log(rval);
});
});
If you wanted to use debounce, you'd do something like this:
submission
.debounce((val) => {
// Add whatever logic for determining the right
// debounce time for the value entered by the
// user, in this case, simply returning a hard
// coded 500 which will do exactly the same as
// debounceTime(500)
return 500;
})
.concatMap(username => {
// ... snip ...
});
});
It doesn't look like there is any problem with how you are importing RxJS - although I would advise if you're just starting with RxJS to go to v6 as it changes how a lot of things are done with operators like debounce and concatMap - it's a pain to change it all later so better to make the change when you're just starting!