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)
})
Related
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');
});
})
The project is at this Github Repository. The file with the code is at components/Soundboard.js
This code was working previously, but now it looks like the promise is running forever. It looks like neither the resolve function, nor the reject function are executing because if I uncomment all the commented lines below and call the function askForPurchase() the only things printed to the console are
an object that looks like "_40": 0, "_55": {"_40": 0, "_55": null, "_65": 0, "_72": null}, "_65": 3, "_72": null} for the line console.log(RNIap.getPurchaseHistory())
and then the word end.
The buyProduct() function also is no longer initializing an IAP.
const buyProduct = function(){
RNIap.requestPurchase("1985162691", false).then(purchase => {
store.dispatch(setPurchases(purchase))
await RNIap.finishTransaction(purchase, false) //developerPayloadAndroid?: string Should I use this argument? I don't get how to use it
}).catch((error) => {
console.log(error.message);
})
}
const askForPurchase = function(){
if (!store.getState().purchase){
//console.log(RNIap.getPurchaseHistory())
RNIap.getPurchaseHistory().then(purchase => {
//console.log(`test1`)
store.dispatch(setPurchases(purchase))
if (purchase.length == 0){
//console.log(`test if`)
buyProduct()
}else{
//console.log(`test else`)
RNIap.getAvailablePurchases()
}
}, reason => {
console.log(reason)
})
//console.log(`end`)
}
}
EXTRA
This code was working a few months ago and I even pulled a commit(1b9cb81f229680e173ce910892dddedc632c1651, comment: "Made the seal pic more cartoony") from that time to test out. After pulling this commit, I deleted my node_modules and pods, and cleaned my build folder, but the askForPurchase() and buyProduct() functions no longer work in that commit either.
I am testing this on a real iPhone SE running ios 13.6.1
I created a sandbox tester if you need to test it out, but I don't think you'll need it
email: rniapsandbox#gmail.com
pw: Somepassword1
hello #Sam problem is async await problem they are not able to get value because they are not waiting to get data before getting data its firing without data and it was returning promise so you have to use async function
so your code be like
const buyProduct = async()=>{
await RNIap.requestPurchase("1985162691", false).then(purchase => {
store.dispatch(setPurchases(purchase))
await RNIap.finishTransaction(purchase, false) //developerPayloadAndroid?: string Should I use this argument? I don't get how to use it
}).catch((error) => {
console.log(error.message);
})}
const askForPurchase = async()=>{
if (!store.getState().purchase){
//console.log(await RNIap.getPurchaseHistory())
await RNIap.getPurchaseHistory().then(purchase => {
//console.log(`test1`)
store.dispatch(setPurchases(purchase))
if (purchase.length == 0){
//console.log(`test if`)
buyProduct()
}else{
//console.log(`test else`)
RNIap.getAvailablePurchases()
}
}, reason => {
console.log(reason)
})
//console.log(`end`)
}}
You will need to change from
console.log(RNIap.getPurchaseHistory())
to
console.log(await RNIap.getPurchaseHistory())
I am trying to add pagination to my Zapier trigger.
The API I am using for the trigger supports pagination, but not using a page number in the traditional sense (ie. page 1,2,3,...). Instead, the API response includes a key (ie. "q1w2e3r4") which should be passed as a parameter to the next request to get the next page of results.
From looking at the docs, I can use {{bundle.meta.page}} (which defaults to 0 unless otherwise set).
I am trying to set {{bundle.meta.page}} in the code editor, with an example shown below:
const options = {
url: 'company_xyz.com/api/widgets',
method: 'GET',
...,
params: {
...,
'pagination_key': bundle.meta.page,
}
}
return z.request(options)
.then((response) => {
response.throwForStatus();
const json_response = response.json;
widgets = json_response.widgets
...
bundle.meta.page = json_response["next_pagination_key"]
return widgets;
});
The problem is that when Zapier tries to retrieve the next page, bundle.meta.page will be 1 instead of the value of "next_pagination_key" from the result of the previous request.
There are docs on cursor-based pagination in the CLI docs.
The relevant block is:
const performWithAsync = async (z, bundle) => {
let cursor;
if (bundle.meta.page) {
cursor = await z.cursor.get(); // string | null
}
const response = await z.request(
'https://5ae7ad3547436a00143e104d.mockapi.io/api/recipes',
{
// if cursor is null, it's sent as an empty query
// param and should be ignored by the server
params: { cursor: cursor }
}
);
// we successfully got page 1, should store the cursor in case the user wants page 2
await z.cursor.set(response.nextPage);
return response.items;
};
This should work in the Zapier Visual Builder, but you might need to use the CLI instead. You can export your integration using the zapier convert CLI command (docs).
I am writing some code to test action in Zapier's CLI. I want to add one more condition here something like response.status == 200 or 201; to check API response code is 200 or 201.
How can I do it? when I log response it gives me whole JSON object that
API is returning.
describe("contact create", () => {
it("should create a contact", done => {
const bundle = {
inputData: {
firstName: "Test",
lastName: "Contact",
email: "Contact#test.com",
mobileNumber: "+12125551234",
type: "contact"
}
};
appTester(App.creates.contact.operation.perform, bundle)
.then(response => {
// Need one more condition whether response status is 200 or 201.
response.should.not.be.an.Array();
response.should.have.property('id');
done();
})
.catch(done);
});
});
appTester returns the result of the perform method, which isn't an API response. It's the data that's passed back into Zapier.
The best thing to do is to add a line like this to your perform:
// after your `z.request`
if (!(response.status === 200 || response.status === 201)) {
throw new Error('need a 200/201 response')
}
That will ensure you're getting exactly the response you want. But, more likely, you can add a response.throwForStatus() to make sure it's not an error code and not worry if it's exactly 200/201.
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!