Cypress: intercept and modify part of the response - response

Based on Cypress docs, I want to modify a field on the response and leave everything else unchanged, after first loading the fixture. I know I could easily do this with 2 fixtures but I would not like to duplicate it for a simple field change. I tried variations of the following code but to no success. Any ideas?
it('Should have the correct values in monthly', () => {
cy.intercept('POST', `**/full`, (req) => {
req.continue(res => {
res.body.data.monthly = 5000;
res.send(res);
})
});
cy.fixture('calculator/monthlyRepayment.json').as('fixtures:monthlyRepayment');
cy.route('POST', `**/full`, '#fixtures:monthlyRepayment').as(`request:fullData`);
cy.get('[data-test="calculator:monthlyRepayment"]').should('contain', '$5000.00');
})

I left a comment, but this will solve your problem, too. You'll want to modify your fixture data before using it:
it('Should have the correct values in monthly', () => {
cy.fixture('calculator/monthlyRepayment.json').then((json) => {
json.monthly = 5000;
cy.intercept('POST', '**/full', json);
// cy.visit called somewhere here
cy.get('[data-test="calculator:monthlyRepayment"]').should('contain', '$5000.00');
});
})

Related

Run several insert/update at once with typeorm

I'm facing an issue with typeorm.
In order to init my database for my repository tests, I wanted to create a sql file with several inserts, and execute the file directly with the entity manager.
It seams that it allows only one query at the time.
Somebody has an idea how to digest directly an sql file in one execution?
Here is an example I'm trying to do.
(it works well with entities and saving them, but for more complicated tests, I'd rather use an sql file).
I don't want to use migration globally because I want all the tests to be independant.
describe('Lead time repository', () => {
let app: INestApplication;
let leadTimeRepository: LeadTimeRepository;
function populateDB() {
const initScript = fs.readFileSync(path.join(__dirname, 'populate.sql'), 'utf-8');
console.log(initScript);
leadTimeRepository.manager.query(initScript);
}
beforeAll(async () => {
const module = await Test.createTestingModule({
imports: [DatabaseModule, LeadTimeModule],
}).compile();
app = module.createNestApplication();
await app.init();
leadTimeRepository = app.get<LeadTimeRepository>(LeadTimeRepository);
populateDB();
});
afterAll(async () => {
await app.close();
});
describe('findLeadTimeForEvent', () => {
it('Should do something.', () => {
console.log('should do something');
});
});
});
Thanks by advance.

Cypress: Is it possible to complete a test after failure

Overview
I want to automatically test all 200 pages of our website every week after the update to see if the update broke any of them
The test case
Login and go to the sitemap
Get the URLs and check each for their HTTP status
The code
it('check HTTP status', () => {
cy.visit(Cypress.config('siteMapUrl'))
cy.get('.portlet-separator').should('contain', 'Available Links')
cy.get('.inputwrapper>a')
.each(($el, index, $list) => {
if($list){
cy.get($el)
.invoke('attr', 'href')
.then(href => {
cy.request(Cypress.config('url')+href)
.should('have.property', 'status', 200)
})
}
})
What happens:
Once an URL returns anything else than status 200 the test fails.
What I would like:
I would like Cypress to iterate through the complete list of URLs before returning the URLs that failed.
Why?
If more than one URL in the list is broken, I will not find the 2nd broken URL with this test until our devs have fixed the first one. Yet I need to produce a list with all broken URLs at the beginning of the week
I have already seen this answer but I would like to know if there is a different solution before I try to implement this
You should not use .should() after each URL - that will fail immediately, even if setting failOnStatus: false.
Instead save the results and check at the end.
const failed = []
cy.get(".inputwrapper>a").each(($el, index, $list) => {
cy.get($el)
.invoke("attr", "href")
.then((href) => {
cy.request({
url: Cypress.config("url") + href,
failOnStatusCode: false,
})
.its('status')
.then(status => {
if (status !== 200) {
failed.push(href)
}
})
})
}
})
.then(() => {
// check inside then to ensure loop has finished
cy.log(`Failed links: `${failed.join(', ')`)
expect(failed.length).to.eq(0)
})

Oidc-client-js trigger login when app start.What is the correct way?

i am using identity server 4 and oidc-client-js for auth, with angular frame work. I faced it this issue,
I am trying to trigger login redirect when application start. First i tried this code;
this.authService.userManager.getUser().then((user) => {
if (!(user)) {
this.authService.userManager.signinRedirect();
}
});
and user always returning null. Then i tried the same code with timeout, like this;
this.authService.userManager.getUser().then((user) => {
setTimeout(() => {
if (!(user)) {
this.authService.userManager.signinRedirect();
}
}, 2000);
});
after that, everything works good. But i'm not comfortable about using timeout. I tried using subject in callback component signinRedirectCallback, i tried userLoaded event but i can't succeeded. And finally i wrote this code;
In app component ngOnInit;
if (!this.authService.currentUser) {
this.authService.userManager.signinRedirectCallback().then((user) => {
// this.authService.userLoadedSub.next(user);
this.authService.currentUser = user;
console.log("01");
//navigate related route
this.initData();
}).catch((err) => {
console.log("signinRedirectCallback Error", err);
this.authService.userManager.signinRedirect();
});
}
Is this a good way to what i need? Is there any other way?
Many thanks for yor helps.

cy.url() and/or cy.location('href') does not return a string

I have an editor page. When I add any content and click the "Save" button my URL will change, adding a random id in the URL. I want to check if my ID's are changing every time when I click the "Save button".
I save the URL result in variable and want to check it, I do it like this:
const currentURL = cy.url();
cy.get('.editor-toolbar-actions-save').click();
cy.url().should('not.eq', currentURL);
But my currentURL variable's type is not string:
expected http://localhost:8080/editor/37b44d4d-48b7-4d19-b3de-56b38fc9f951 to not equal { Object (chainerId, firstCall) }
How I can use my variable?
tl;dr
Cypress commands are asynchronous, you have to use then to work with their yields.
cy.url().then(url => {
cy.get('.editor-toolbar-actions-save').click();
cy.url().should('not.eq', url);
});
Explanation
A similar question was asked on GitHub, and the official document on aliases explains this phenomenon in great detail:
You cannot assign or work with the return values of any Cypress command. Commands are enqueued and run asynchronously.
The solution is shown too:
To access what each Cypress command yields you use .then().
cy.get('button').then(($btn) => {
// $btn is the object that the previous
// command yielded us
})
It is also a good idea to check out the core concepts docs's section on asynchronicity.
These commands return a chainable type, not primitive values like strings, so assigning them to variables will require further action to 'extract' the string.
In order to get the url string, you need to do
cy.url().then(urlString => //do whatever)
I have been having the same issue and so far most consistent method has been to save the URL to file and read it from file when you need to access it again:
//store the url into a file so that we can read it again elsewhere
cy.url().then(url => {
const saveLocation = `cypress/results/data/${Cypress.spec.name}.location.txt`
cy.writeFile(saveLocation, getUrl)
})
//elsewhere read the file and do thing with it
cy.readFile(`cypress/results/data/${Cypress.spec.name}.location.txt`).then((url) => {
cy.log(`returning back to editor ${url}`)
cy.visit(url)
})
Try this:
describe("Test Suite", () => {
let savedUrl;
beforeEach(() => {
cy.visit("https://duckduckgo.com/");
cy.url().then(($url) => {
savedUrl = $url;
});
});
it("Assert that theURL after the search doens't equal the URL before.", () => {
cy.get("#search_form_input_homepage").type("duck");
cy.get("#search_button_homepage").click();
// Check if this URL "https://duckduckgo.com/?q=duck&t=h_&ia=web"
// doesn't equal the saved URL "https://duckduckgo.com/"
cy.url().should("not.eq", savedUrl);
});
});
Refer below code snippet, Here you can get the current URL and store it in a variable, do print via cy.log()
context('Get Current URL', () => {
it('Get current url and print', () => {
cy.visit('https://docs.cypress.io/api/commands/url')
cy.url().then(url => {
const getUrl = url
cy.log('Current URL is : '+getUrl)
})
})
})
#Max thanks this helped to get some ideas on different versions.
The way I did it is:
Create a .json file in your fixtures folder (name it whatever you want).
On the new .json file, only add: { } brackets and leave the rest blank. The function will self populate that .json file.
Create a new function on the commands page to easily call it on your test.
It would probably be best to create two functions, 1 function to write url or the sliced piece of the url, and the another function to call it so you can use it.
A. Example of 1st method, this method cuts the id off of the URL and stores it on the .json file:
Cypress.Commands.add('writeToJSON', (nameOfJSONSlicedSection) =>
{
cy.url().then(urlID =>
{
let urlBit = urlID.slice(urlID.indexOf('s/') + 2, urlID.indexOf('/edit'))
cy.writeFile('cypress/fixtures/XYZ.json', {name: nameOfJSONSlicedSection, id: urlBit}) /*{ }<-- these will populate the json file with name: xxxxx and id:xxxxx, you can changes those to whatever meets your requirements. using .slice() to take a section of the url. I needed the id that is on the url, so I cut only that section out and put it on the json file.*/
})
})
B. 2nd example function of calling it to be used.
This function is to type in the id that is on the url into a search box, to find the item I require on a different it() block.
Cypress.Commands.add('readJSONFile', (storedJSONFile) =>
{
cy.readFile('cypress/fixtures/XYZ.json').its('id').then((urlSetter) => {
cy.log(storedJSONFile, 'returning ID: ' + urlSetter)
//Search for Story
cy.get('Search text box').should('be.visible').type(urlSetter, {delay: 75})
})
})
/*here I use a .then() and hold the "id" with "urlSetter", then I type it in the search box to find it by the id that is in the URL. Also note that using ".its()" you can call any part section you require, example: .its('name') or .its('id') */
I hope this helps!

Select2 AJAX doesn't update when changed programatically

I have a Select2 that fetches its data remotely, but I would also like to set its value programatically. When trying to change it programatically, it updates the value of the select, and Select2 notices the change, but it doesn't update its label.
https://jsfiddle.net/Glutnix/ut6xLnuq/
$('#set-email-manually').click(function(e) {
e.preventDefault();
// THIS DOESN'T WORK PROPERLY!?
$('#user-email-address') // Select2 select box
.empty()
.append('<option selected value="test#test.com">test#test.com</option>');
$('#user-email-address').trigger('change');
});
I've tried a lot of different things, but I can't get it going. I suspect it might be a bug, so have filed an issue on the project.
reading the docs I think maybe you are setting the options in the wrong way, you may use
data: {}
instead of
data, {}
and set the options included inside {} separated by "," like this:
{
option1: value1,
option2: value2
}
so I have changed this part of your code:
$('#user-email-address').select2('data', {
id: 'test#test.com',
label: 'test#test.com'
});
to:
$('#user-email-address').select2({'data': {
id: 'test#test.com',
label: 'test#test.com'
}
});
and the label is updating now.
updated fiddle
hope it helps.
Edit:
I correct myself, it seems like you can pass the data the way you were doing data,{}
the problem is with the data template..
reading the docs again it seems that the data template should be {id, text} while your ajax result is {id, email}, the set manual section does not work since it tries to return the email from an object of {id, text} with no email. so you either need to change your format selection function to return the text as well instead of email only or remap the ajax result.
I prefer remapping the ajax results and go the standard way since this will make your placeholder work as well which is not working at the moment because the placeholder template is {id,text} also it seems.
so I have changed this part of your code:
processResults: function(data, params) {
var payload = {
results: $.map(data, function(item) {
return { id: item.email, text: item.email };
})
};
return payload;
}
and removed these since they are not needed anymore:
templateResult: function(result) {
return result.email;
},
templateSelection: function(selection) {
return selection.email;
}
updated fiddle: updated fiddle
For me, without AJAX worked like this:
var select = $('.user-email-address');
var option = $('<option></option>').
attr('selected', true).
text(event.target.value).
val(event.target.id);
/* insert the option (which is already 'selected'!) into the select */
option.appendTo(select);
/* Let select2 do whatever it likes with this */
select.trigger('change');
Kevin-Brown on GitHub replied and said:
The issue is that your templating methods are not falling back to text if email is not specified. The data objects being passed in should have the text of the <option> tag in the text property.
It turns out the result parameter to these two methods have more data in them than just the AJAX response!
templateResult: function(result) {
console.log('templateResult', result);
return result.email || result.text;
},
templateSelection: function(selection) {
console.log('templateSelection', selection);
return selection.email || selection.id;
},
Here's the fully functional updated fiddle.

Resources