Change which URL the cypress test visits at run time - cypress

While im developping my website the URL changes quite a lot, if I need to restart or something. So at one time it might be 0.0.0.0:5500 and the next time it could 0.0.0.0:34935.
So if I set the: cy.visit('url) to be a constant URL then I would constantly have to change it to work.
So I am wondering if there is a way to input the URL at runtime and use that.

You can pass in the baseUrl as a command line option.
cypress run --config baseUrl=0.0.0.0:33935
cypress run --config baseUrl=0.0.0.0:5500
If you didn't want to use a command line variable, but wanted to execute strictly off of if you can reach an endpoint, you could overwrite the cy.visit() command to ping one of the endpoints before navigating to it.
Cypress.Commands.overwrite('visit', (originalFn, url, options) => {
cy.request({ url: 'http://0.0.0.0:5500', failOnStatusCode: false}).then((res) => {
if (res.status === 200) {
return '0.0.0.0:5500'
}
return '0.0.0.0:33935'
}).then((baseUrl) => {
return originalFn(`${baseUrl}${url}`, options);
});
})
Note that this solution will ping your endpoints before every cy.visit(). There's probably a more elegant way to do this, involving having a dummy url for your baseUrl in cypress.json, and only do this request if Cypress.env('baseUrl') is still that dummy url.

You are doing it wrong. Why your port for the application changes all the time. Rather than asking your developer to make your application accessible on port 8080 or some fixed port, you are exploring other options which is not required at all.

Related

How to wait all similar requests in cypress?

When start page of app are loading I have a lot of requests on locize api.
requests count
When I try to write a test for it, I need to wait when all of them finished and then start to get an DOM elements.
I do it like this:
cy.intercept({
method: "GET",
url: "https://api.locize.app/**",
}).as("languages");
cy.visit("/");
cy.wait("#languages")
but this code wait only 1st request.
test log
I was try to do it with cy.get("#languages.all")
but it's haven’t any effect.
So the question is, how can I get waiting for all requests before it's go further?
P.S. I'm pretty new in cypress, so I'll be really appreciated for any help.
One of the solution that I found is library cypress-network-idle.
https://www.npmjs.com/package/cypress-network-idle
Basically it helped me solve the problem
If you know the calls for the language, then you can store them in an array and iterate over it to create unique intercepts and wait on them. However, this would be brittle to changes in your app calls.
const enGBItems = ['Objects', 'Locations', ... ]
Cypress._.forEach(enGBItems, function(item){
cy.intercept({
method: "GET",
url: `https://api.locize.app/**/en-GB/${item}`,
}).as(item)
})
cy.visit("/")
Cypress._.forEach(enGBItems, function(item){
cy.wait(`#${item}`)
})

Ensure that fixtures exists when running a test. Control order of tests running

A lot of this is wrapped in commands, but I've left that part out to make the problem more feasible.
Consider these two tests:
# Test1: Test login for user
- Step1: Logs in manually (go to login-URL, fill out credentials and click 'Log in').
- Step2: Save auth-cookies as fixtures.
# Test2: Test something is dashboard for user.
- Step1: Set auth-cookies (generated in Test1)
- Step2: Visits https:://example.org/dashboard and ensures the user can see the dashboard.
If they run as written as listed above, then everything is fine.
But if Test2 runs before Test1, then Test2 will fail, since Test1 hasn't to generated the cookies yet.
So Test1 is kind of a prerequisite for Test2.
But Test1 doesn't need to run every time Test2 runs - only if the auth-cookies aren't generated.
I wish I could define my Test2 to be like this instead:
Test2: Test something is dashboard for user.
- Step1: Run ensureAuthCookiesExists-command
- Step2: If the AuthCookies.json-fixture doesn't exist, then run Test1
- Step3: Sets auth-cookies (generated in Test1)
- Step4: Visits https:://example.org/dashboard and ensures the user can see the dashboard.
Solution attempt 1: Control by order
For a long time I've done this using this answer: How to control order of tests. And then having my tests defines like this:
{
"baseUrl": "http://localhost:5000",
"testFiles": [
"preparations/*.js",
"feature-1/check-header.spec.js",
"feature-2/check-buttons.spec.js",
"feature-3/check-images.spec.js",
"feature-4/check-404-page.spec.js",
//...
]
}
But that is annoying, since it means that I keep having to add to add new features to that list, which get's annoying.
And this only solves the problem if I want to run all the tests. If I want to run preparations.spec.js and thereafter: feature-2/check-buttons.spec.js. Then I can't do that easily.
Solution attempt 2: Naming tests smartly
I also tried simply naming them appropriately, like explain here: naming your tests in Cypress.
But that pollutes the naming of the tests, making it more cluttered. And it faces the same issues as solution attempt 1 (that I can't easily run two specific tests after one another).
Solution attempt 3: Making a command for it
I considered making a command that tests for it. Here is some pseudo-code:
beforeEach(() => {
if( preparationsHasntCompleted() ){
runPreparations();
}
}
This seems smart, but it would add extra runtime to all my tests.
This may not suit your testing aims, but the new cy.session() can assure cookie is set regardless of test processing order.
Use it in support in beforeEach() to run before every test.
The first test that runs (either test1 or test2) will perform the request, subsequent tests will use cached values (not repeating the requests).
// cypress/support/e2e.js -- v10 support file
beforeEach(() => {
cy.session('init', () => {
// request and set cookies
})
})
// cypress/e2e/test1.spec.cy.js
it('first test', () => {
// the beforeEach() for 1st test will fire the request
...
})
// cypress/e2e/test2.spec.cy.js
it('second test', () => {
// the beforeEach() for 2nd test will set same values as before from cache
// and not resend the request
})
Upside:
performing login once per run (ref runtime concerns)
performing tests in any order
using the same token for all tests in session (if that's important)
Downside:
if obtaining auth cookies manually (vi UI), effectively moving the login test to a beforeEach()
Example logging in via request
Rather than obtaining the auth cookie via UI, it may be possible to get it via cy.request().
Example from the docs,
cy.session([username, password], () => {
cy.request({
method: 'POST',
url: '/login',
body: { username, password },
}).then(({ body }) => {
cy.setCookie('authToken', body.token)
})
})
It is generally not recommended to write tests that depend on each other as stated in the best practices. You can never be sure that they run correctly. In your case if the first test fails all the other ones will fail, even if the component/functionality is functioning propperly. Especially if you test more than just the pure login procedure, e.g. design.
As #Fody said before you can ensure being logged in in the beforeEach() hook.
I would do it like this:
Use one describe to test the login via UI.
Use another describe where you put the login (via REST) in the before() and the following command Cypress.Cookies.preserveOnce('<nameOfTheCookie>'); in the beforeEach() to not delete the test for the following it()s

Cypress: Switching from cy.route() to cy.intercept()

It seems that most people I read about experence zero trouble with this. I, on the other hand, have a test suite which someone else wrote, in which I'm trying to replace route() with intercept(). The API intercepts are done to handle button clicks etc., and about 99.9% percent of them fails if I just replace it. So, there's obviously some syntax in/use of intercept() I've not found a description for.
Example:
This works:
cy.route('POST', getApiPrefix() + '/prosjektfinansiering/'+ pfId +'/eiendom', result);
This does not work. The button click is not executed:
cy.intercept('POST', getApiPrefix() + '/prosjektfinansiering/'+ pfId +'/eiendom', result);
I've tried adding '**' in front of "/prosjekt...", and I've tried removing 'POST', with no luck.
Any ideas? I'll gladly post more info if necessary.
UPDATE:
Futher attempts:
Getting some hints here and there, it seems that this is a more correct way of using intercept():
return cy.intercept('POST', getApiPrefix() + '/prosjektfinansiering/'+ pfId +'/eiendom', {
body: result
});
This doesn't work, either.
The variables result in these examples is an object describing what is sent back to the frontend of the POST-request in the route matches the api path.
For troubleshooting, I can see that when using intercept(), there is ONE route that is not working when using intercept (the bottom one in the picture). However, I cannot for the life of me see why, and how the route match can be written differently?
Most likely, you're mixing the old use of cy.route() and cy.server(). In my experience, those two won't work well together. It's easier when you're starting fresh with just cy.intercept().
Your update is correct too; You have to encapsulate the return value you want mocked in {body: value}.
from what I am seeing in your circled screenshot, the API is not called after you try to intercept it. (the count under # column is -)
You need to track when the API is to be called and ensure you intercept before the call is made. Cypres can help you with this. You can go through the run steps in the cypress window.
You could also share this if you don't mind.
If you are 100% certain the button makes the call. Steps should be:
cy.intercept()
cy.get('button').click()
In the cypress window, right after the click, you should see the API being called.

Cypress. Why is my route alias not matching?

My POST request to ocr/receipt is never matched. I've...
created a route, matching **/ocr/**, specifying POST, and given it an alias.
called wait() with a long timeout.
I can watch the request complete in the network pane while the wait spinner turns happily in the test pane. Why is Cypress not matching this route?
beforeEach(function () {
cy.route('POST','**/ocr/**').as('ocr');
});
it('Création frais depuis le bouton « appareil photo »', function () {
cy.get('.in-progress').first().click()
cy.wait('#ocr', {'timeout':15000});
cy.get('#grpChoices > :nth-child(1)').click();
});
Well who would have guessed. Method is case sensitive, and it only works in lower case. So...
route('post','**/ocr/**').as('ocr')
fixed it.
The doc won't help you.
The other recurring reason for routes not triggering is if your app uses the fetch api. Fetch is not compatible with cypress.
In my case I defined the same alias in cy.route().as('acme') and cy.request().as('acme'). Even though the Cypress docs points out that cy.wait does not support requests it does not point out that aliases should be unique. Rename or removing cy.request.as('foo') solves this problem.
Make sure for all requests, you have a response.

Suppress REST Logging Calls from Supertest

I have started using MEAN stack and currently writing REST unit tests using Super Test
I want to have a little bit more clarity in my log file so that I can easily see my successful and failed tests.
I wish to suppress the console output for the actual rest API call which I think are coming out of SuperTest.
This image shows the logs I want to suppress.
I think it's actually coming from expressjs/morgan. I've gotten around it by setting the env to test and disabling morgan for the test env.
In my test files:
process.env.NODE_ENV = 'test';
In app.js:
if(app.get('env') !== 'test') app.use(logger('dev'));
You can setup morgan to accept a skip function.
Then, you can, say, toggle an env variable on/off - or define skipping logic of your own to temporarily mute the logging.
app.use(
logger('dev', {
skip: function(req, res) {
return process.env.MUTE_LOGGER === 'on';
},
}),
);

Resources