Using Cypress to test a web app, I have tests failing because of an unexpected XHR response from our test backend.
The screenshots and screencasts doesn't help understanding why we get this error message from the webapp. It would be very useful to get the actual XHR requests logs as an artifact.
It seems to be possible to capture some routes using cy.route, but it seems to be more suitable to stub requests.
What is the correct way to capture and write the XHR logs alongside the screenshots and videos ? It would be even better if it would delete the file if everything passes.
To record the network traffic in the same manner like the network tab in devtools, you can utilize the HAR file format (http://www.softwareishard.com/blog/har-12-spec/). To generate a HAR file during the execution of your Cypress tests you can use the cypress-har-generator plugin.
describe('my tests', () => {
before(() => {
// start recording
cy.recordHar();
});
after(() => {
// save the HAR file
cy.saveHar({ waitForIdle: true });
});
it('does something', () => {
cy.visit('https://example.com');
// ...
});
});
It will save a HAR that can be used to inspect the network activity, identify the performance issues or troubleshooting bugs. The file can be skimmed using any kind of viewer (e.g. https://developer.chrome.com/blog/new-in-devtools-76/#HAR).
Related
I have a web application to perform the update operation with the help of a Update button in UI mode.
During that time list of APIs are loaded into the Network tab with XHR type as below. I have to verify one of the API put call is triggered and the status is passed.
url.toString() -- https://abcdef.execute-api.ue-east-2.amazonaws.com/stg2
It contains the RequestedURL value. I manually verified that in Network tab for that particular API put call. Then kept that in cypress.json and reading the url to the current class
Base URL for the UI operation: https://abc-stg2.awsdev.fgh.com/
Note: The both url are modified and dummy one, since it is confidential to share. The understanding is API calls are in AWS and the UI urls are in other environment
Try #1
// After the UI operation
cy.intercept('PUT',url.toString()).as('urupdate')
cy.wait('#urupdate',{requestTimeout:20000})
.its('status')
.should('be.eq',200)
Output:
Try #2
cy.intercept('PUT', url.toString(), (req) => {
if (req.body.status == 200) {
cy.log('pass')
}
})
Output:
The log is not getting printed and till the if statement everything is getting passed
How can we verify the particular API is triggered and the status is 200?
I have gone through the intercept as above and other stuffs in Cypress. But that does not get me the solution. Share your suggestions
I am using cypress to write tests and have a problem which doesn't appear in every test. In some cases it works and I don't know why. So...
The Problem:
I defined a route and alias for it in beforeEach:
beforeEach(function () {
cy.server()
cy.route('GET', '/favourites?funcName=columnPreset', []).as('columnPresetEmpty')
cy.visit('#/search')
})
Stub works fine if http request occured on page load.
But if I perform request responding to click event (modal dialog opens and executes http request) it just appear in commands not makred as stubbed and following cy.wait('#columnPresetEmpty') fails with request timeout.
it('does not work', function () {
cy.get('[data-test=button-gridSettings]').click()
cy.wait('#columnPresetEmpty')
})
At the same time in other tests I have almost similar functionality where request is performed just by clicking on a button, without opening new modal window. It's the only difference.
What am I doing wrong?
The issue might be cypress can not yet fully handle fetch calls. You can disable it the following way but make sure you have fetch polyfill. This will then issue XHR requests which cypress can observe.
cy.visit('#/search', {
onBeforeLoad: (win) => {
win.fetch = null
}
})
More to read here:
https://github.com/cypress-io/cypress/issues/95#issuecomment-281273126
I found the reason causing such behavior. Problem was not in a modal window itself, but code performing second request was called in promise's callback of another request. Something like:
fetch('/initData')
.then(loadView)
And loadView function executed second fetch.
So when I removed loadView from promise's callback both requests become visible for cypress.
For info, I tried it out on my search modal (in a Vue app) and it works ok.
What I did:
created a dummy file named test-get-in-modal.txt in the app's static folder
added an http.get('test-get-in-modal.txt') inside the modal code, so it only runs after the modal is open
in the spec, did a cy.server(), cy.route('GET', 'test-get-in-modal.txt', []).as('testGetInModal') in a before()
in the it() added cy.wait('#testGetInModal') which succeeded
changed to cy.route('GET', 'not-the-file-you-are-looking-for.txt'..., which failed as expected
The only difference I can see is that I cy.visit() the page prior to cy.server(), which is not the documented pattern but seems to be ok in this scenario.
I'm using Cypress for end-to-end testing. In my beforeEach, I'm using an SDK I've been provided to seed data on a server (the SDK sends API calls to the server but does not use cy.request inside it). The method on the SDK returns a promise, therefore I figured I could return the promise like so:
beforeEach(() => {
return sdk.createProperty(...);
});
My test then does something like this:
it('displays a property', () => {
cy.visit(`/companies/${appTestData.companyId}/properties`);
...the rest is commented out currently...
}
This actually works as intended, that is, it waits until the server response is returned before running the tests, but I see the following warning in the console when the test actually runs:
Cypress Warning: Cypress detected that you returned a promise in a test, but also invoked one or more cy commands inside of that promise...
I noticed if I change my beforeEach to use cy.then, the warning goes away:
beforeEach(() => {
cy.then(() => sdk.createProperty(...));
});
It seems a bit unnecessary and was kind of a stab in the dark, so I'd like to know if there's a prescribed way of doing what I need to do. I can't change the SDK I'm using to use cy.request, which I assume would also prevent the warning. Thanks.
Probably not what you want to hear, but can I confirm you that using cy.then(...) is the most standard way of waiting for a Promise in Cypress I know of.
After reading your question, I have tried to use Cypress Network Requests features to wait for a fetch('my/url') in a before(), but it doesn't seem to be detecting the request at all.
I've sw.js code like:
self.addEventListener('install', e => {
e.waitUntil(
caches.open('cache').then(cache => {
return cache.addAll([
'/',
'/index.html',
'/styles/main.css',
'/scripts/main.min.js'
])
.then(() => self.skipWaiting());
})
)
});
self.addEventListener('activate', event => {
event.waitUntil(self.clients.claim());
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(response => {
return response || fetch(event.request);
})
);
});
I've updated the index.html file recently, but the file isn't reflected the site. It's still showing the old content of index.html.
Now, How can I make sure app will be serving the latest code if file's changed on the server?
I've been seeing some related answers (like update and refresh), but please tell me what's going on each part of the answer.
You have two options:
Switch from your current cache-first strategy in your fetch handler to a strategy like stale-while-revalidate.
Use a build-time tool to generate a service worker that changes each time one of your local resources changes, which will trigger the install and activate handlers, giving them a chance to update your users' caches with the latest copies of your assets. These tools work by generating hashes of each local file and inlining those hashes into the generated service worker script.
Option 2. is more efficient, because your users will only have to make requests for your assets when something actually changes, as opposed to when using a stale-while-revalidate strategy, which requires firing off a "revalidate" request in the background each time your fetch handler is invoked. Option 2. does require a bit more work, though, because you need to incorporate a step into your build process to generate your service worker for you.
If you go the option 2. route, there are a few different build tools that I know about:
sw-precache (disclosure: I'm the author)
offline-plugin
I'm prototyping a MVC.NET 4.0 application and am defining our Javascript test configuration. I managed to get Jasmine working in VS2012 with the Chutzpah extensions, and I am able to run pure Javascript tests successfully.
However, I am unable to load test fixture (DOM) code and access it from my tests.
Here is the code I'm attempting to run:
test.js
/// various reference paths...
jasmine.getFixtures().fixturesPath = "./";
describe("jasmine tests:", function () {
it("Copies data correctly", function () {
loadFixtures('testfixture.html');
//setFixtures('<div id="wrapper"><div></div></div>');
var widget = $("#wrapper");
expect(widget).toExist();
});
});
The fixture is in the same folder as the test file. The setFixtures operation works, but when I attempt to load the HTML from a file, it doesn't. Initially, I tried to use the most recent version of jasmine-jquery from the repository, but then fell back to the over 1 year old download version 1.3.1 because it looked like there was a bug in the newer one. Here is the message I get with 1.3.1:
Test 'jasmine tests::Copies data correctly' failed
Error: Fixture could not be loaded: ./testfixture.html (status: error, message: undefined) in file:///C:/Users/db66162/SvnProjects/MvcPrototype/MvcPrototype.Tests/Scripts/jasmine/jasmine-jquery-1.3.1.js (line 103)
When I examine the source, it is doing an AJAX call, yet I'm not running in a browser. Instead, I'm using Chutzpah, which runs a headless browser (PhantomJS). When I run this in the browser with a test harness, it does work.
Is there someone out there who has a solution to this problem? I need to be able to run these tests automatically both in Visual Studio and TeamCity (which is why I am using Chutzpah). I am open to solutions that include using another test runner in place of Chutzpah. I am also going to evaluate the qUnit testing framework in this effort, so if you know that qUnit doesn't have this problem in my configuration, I will find that useful.
I fixed the issue by adding the following setting to chutzpah.json:
"TestHarnessLocationMode": "SettingsFileAdjacent",
where chutzpah.json is in my test app root
I eventually got my problem resolved. Thank you Ian for replying. I am able to use PhantomJS in TeamCity to run the tests through the test runner. I contacted the author of Chutzpah and he deployed an update to his product that solved my problem in Visual Studio. I can now run the Jasmine test using Chutzpah conventions to reference libraries and include fixtures while in VS, and use the PhantomJS runner in TeamCity to use the test runner (html).
My solution on TeamCity was to run a batch file that launches tests. So, the batch:
#echo off
REM -- Uses the PhantomJS headless browser packaged with Chutzpah to run
REM -- Jasmine tests. Does not use Chutzpah.
setlocal
set path=..\packages\Chutzpah.2.2.1\tools;%path%;
echo ##teamcity[message text='Starting Jasmine Tests']
phantomjs.exe phantom.run.js %1
echo ##teamcity[message text='Finished Jasmine Tests']
And the Javascript (phantom.run.js):
// This code lifted from https://gist.github.com/3497509.
// It takes the test harness HTML file URL as the parameter. It launches PhantomJS,
// and waits a specific amount of time before exit. Tests must complete before that
// timer ends.
(function () {
"use strict";
var system = require("system");
var url = system.args[1];
phantom.viewportSize = {width: 800, height: 600};
console.log("Opening " + url);
var page = new WebPage();
// This is required because PhantomJS sandboxes the website and it does not
// show up the console messages form that page by default
page.onConsoleMessage = function (msg) {
console.log(msg);
// Exit as soon as the last test finishes.
if (msg && msg.indexOf("Dixi.") !== -1) {
phantom.exit();
}
};
page.open(url, function (status) {
if (status !== 'success') {
console.log('Unable to load the address!');
phantom.exit(-1);
} else {
// Timeout - kill PhantomJS if still not done after 2 minutes.
window.setTimeout(function () {
phantom.exit();
}, 10 * 1000); // NB: use accurately, tune up referring to your needs
}
});
}());
I've got exactly the same problem. AFAIK it's to do with jasmine-jquery trying to load the fixtures via Ajax when the tests are run via the file:// URI scheme.
Apparently Chrome doesn't allow this (see https://stackoverflow.com/a/5469527/1904 and http://code.google.com/p/chromium/issues/detail?id=40787) and support amongst other browsers may vary.
Edit
You might have some joy by trying to set some PhantomJS command-line options such as --web-security=false. YMMV though: I haven't tried this myself yet, but thought I'd mention it in case it's helpful (or in case anyone else know more about this option and whether it will help).
Update
I did manage to get some joy loading HTML fixtures by adding a /// <reference path="relative/path/to/fixtures" /> comment at the top of my Jasmine spec. But I still have trouble loading JSON fixtures.
Further Update
Loading HTML fixtures by adding a /// <reference path="relative/path/to/fixtures" /> comment merely loads in your HTML fixtures to the Jasmine test runner, which may or may not be suitable for your needs. It doesn't load the fixtures into the jasmine-fixtures element, and consequently your fixtures don't get cleaned up after each test.