How to have Cypress go through every page on site to see if there are any console errors and if so, make it known to the user running the test - automated-tests

I want Cypress to go through every page to see on a website to see if there are any console errors and if so, make it known to the user running the test. (I'm thinking it would be useful for CSP checking to see if the site is throwing a console error because of a domain not being whitelisted.)

This package cypress-fail-on-console-error
may make it easier
test
import failOnConsoleError from 'cypress-fail-on-console-error';
failOnConsoleError();
const pages = [ "/page1", "/page2" ]
pages.forEach(page => {
it(`verifies the page ${page}`, () => {
cy.visit(page)
})
})
There's some interesting stuff on Cypress and CSP here
Testing Content-Security-Policy using Cypress ... Almost

You can use a combination of Cypress functionality to achieve this. You could store the list of links in an array of strings, use Cypress Lodash to iterate through each string as a separate test, and use the onBeforeLoad callback within cy.visit() to spy on console.error.
describe('Tests', () => {
// Define links
const links = ['/1', '/2', '/3'...]
// Iterate through the links array using Cypress Lodash
Cypress._.times(links.length, (index) => {
it('validates site loads with no errors', () => {
cy.visit(links[index], {
// set the `onBeforeLoad` callback to save errors as 'error'
onBeforeLoad(win) {
cy.stub(win.console, 'error').as('error');
}
});
// Validate error was not called
cy.get('#error').should('not.have.been.called');
});
});
});
A good deal of this answer was taken from this answer.

If you'd like to be specific about the errors that fail, try catching uncaught:exception
Cypress.on('uncaught:exception', (err) => {
if (err.message.includes('Content Security Policy')) {
return true
} else {
return false // only fail on the above message
}
})
describe('Testing Content Security Policy', () => {
const pages = [ "/page1", "/page2" ]
pages.forEach(page => {
it(`visiting page ${page}`, () => {
cy.visit(page)
})
})
})

Related

Nuxt 3: Problem with wildcard pages and useFetch

I have a problem with a wildcard page /pages/[...slug].vue and fetching from backend.
I have a computed called url that I use in:
const { data, refresh } = await useFetch(url.value)
Then I have a watcher to refresh the useFetch:
watch(url, (url, oldUrl) => {
console.log(url)
console.log(oldUrl)
refresh()
})
In the browser, the console log shows the correct url, but useFetch just loaded the old url again.
Any idea what's wrong here? Thanks.
[edit: clarification: this is when navigation in browser, that triggers the watch]
I believe because computed is a 'getter' behind the scenes you need to use a deep watcher on it or watch the returned value directly otherwise the watch function will not run.
You could change your watch function to to:
watch(url, (url, oldUrl) => {
console.log(url)
console.log(oldUrl)
refresh()
},
{ deep: true }
)
Alternatively you could directly watch the value of the computed property:
watch( () => url.value, (url, oldUrl) => {
console.log(url)
console.log(oldUrl)
refresh()
},
{ deep: true }
)
There is a bit more information here: https://vuejs.org/guide/essentials/watchers.html#basic-example
Hi i had the same issue 2 days ago.. i guess that the issue happens because of the default keep alive props and internal caching.
My solution was to use $fetch() instead of useFetch().
Like that you also don't need the watcher anymore.

Stubbing link inside an iframe

I have an iframe containing an element with an href, when I click I want to stub the call and verify the url.
This is my code so far, not working. The error is "Timed out retrying after 8000ms: expected open to have been called at least once"
cy.window().then((win) => {
cy.stub(win, 'open').as('open')
.callsFake(url => {
cy.wrap(url).as('href')
});
})
cy.getIframeBody().find('#container a').click();
cy.get('#open').should('be.called');
cy.get('#href').should(url => {
expect(url).to.eq('xyz')
});
The <iframe> has a different window to the one returned by cy.window().
You can obtain the iframe contentWindow first and place the stub on that.
cy.get('iframe')
.its('0.contentWindow')
.then(iframeWin => {
cy.stub(iframeWin, 'open').as('open')
.callsFake(url => {
cy.wrap(url).as('href')
});
cy.getIframeBody().find('#container a').click();
cy.get('#open').should('be.called');
cy.get('#href').should(url => {
expect(url).to.eq('xyz')
})
})

error when i use cy.xpath inside cy.origin

I'm using Cypress to test a website.
When I use xpath inside cy.origin(), It's not working.
it('t1', function() {
cy.origin(('BaseUrl'), () => {
cy.visit('/profile')
cy.xpath("//input[#name='username']").type('user')
cy.xpath("//input[#name='password']").type('pass')
cy.xpath("//button[#type='button']").click()
})
})
Error:
TypeError
cy.xpath is not a function
Note that it works correctly outside cy.origin()
TLDR: Stick with standard Cypress commands inside cy.origin().
It's a current limitation of cy.orgin(), in fact any custom command must be treated specially and cy.xpath() is a custom command.
See Callback restrictions
It is also currently not possible to use require() or dynamic import() within the callback. Because of this limitation, it cannot use npm packages or other third-party libraries inside the callback, as there is no mechanism to reference them. This functionality will be provided in a future version of Cypress.
While third-party packages are strictly unavailable, it is possible to reuse your own code between cy.origin() callbacks. The workaround is to create a custom Cypress command within the secondary origin in a before block:
before(() => {
cy.origin('somesite.com', () => {
Cypress.Commands.add('clickLink', (label) => {
cy.get('a').contains(label).click()
})
})
})
it('clicks the secondary origin link', () => {
cy.origin('somesite.com', () => {
cy.visit('/page')
cy.clickLink('Click Me')
})
})
But you can't use this pattern with cy.xpath() as you currently need to require('cypress-xpath') and that can't be done inside cy.origin().
A Workaround
Navigate to /node_modules/cypress-xpath/src/index.js
Copy the entire contents
Add a new command file in support: /cypress/support/xpath.js
Add this to the file, pasting in the copied code
before(() => {
cy.origin('somesite.com', () => { // your cross-origin URL here
// paste here code from /node_modules/cypress-xpath/src/index.js
})
})
Import xpath.js into /cypress/support/commands.js
Now cy.xpath() will work within cy.orgin() in any of your tests.
Check to see if non-xpath works.
cy.origin(('BaseUrl'), () => {
cy.visit('/profile')
cy.get("input[#name='username']").type('user')
...
If not, you've probably not set the experimentalSessionAndOrigin flag correctly.

How are tests organised in cypress

We are currently looking at swapping from selenium to cypress for our automated End to End tests as it looks as if it could stop some of the wait headaches caused by selenium.
As the login time can take a while we use a [OneTimeSetUp] at the start of a test class in NUnit to to do our initial log in then we run our tests from there.
So my question is how are tests organised in cypress? and can we run multiple tests on the same instance?
Cypress uses mocha for the structure of the tests.
describe() block in mocha groups the tests.
it() block tells that this is a test.
e.g.
describe('Login Functionality', function() {
it('Check Login with Correct Credentials', function() {
//Your code
})
it('Check Login with Incorrect Username', function() {
//Your code
})
it('Check Login with Incorrect Password', function() {
//Your code
})
})
Everything you need is in the official documentation :
https://docs.cypress.io/guides/core-concepts/writing-and-organizing-tests.html#Hooks
Enjoy Cypress!
Sample :
beforeEach(function () {
cy.visit('/users/new')
cy.get('#first').type('Johnny')
cy.get('#last').type('Appleseed')
})
it('displays form validation', function () {
cy.get('#first').clear() // clear out first name
cy.get('form').submit()
cy.get('#errors').should('contain', 'First name is required')
})
it('can submit a valid form', function () {
cy.get('form').submit()
})
})

Use setState to change the value of a grandchild object

Everything I have tried from what I can find doesn't seem to be working. I'm really curious how to access and edit grandchild objects located in the state with react. If anyone could tell me what I'm doing wrong, it would be very helpful.
https://codesandbox.io/s/0mo32q85pp
Take a look at the following code...
App.js
lines: 41-58
getHomeworld = URL => {
fetch(URL)
.then(res => {
return res.json();
})
.then(homeWorldObject => {
console.log(homeWorldObject);
// this.setState({ <- Why isn't this working????
// ...this.state.starwarsChars,
// ...this.state.nextPage,
// ...this.state.prevPage,
// ...this.state.starwarsChars.homeworld = homeWorldObject
// });
})
.catch(err => {
throw new Error(err);
});
};
lines: 86-89
<CharacterList
characters={this.state.starwarsChars}
getHomeworld={this.getHomeworld}
/>
CharacterList.js
lines: 8-12
<Character
key={character.name}
characterDetails={character}
getHomeworld={props.getHomeworld}
/>
Character.js
lines: 18-29
{Object.keys(props.characterDetails).includes("homeworld") ? (
<div className="character-homeworld">
<Homeworld
homeworld={props.getHomeworld(props.characterDetails.homeworld)}
/>
</div>
) : (
<div className="character-homeworld">
<h4>Homeworld</h4>
<p>None</p>
</div>
)}
Homeworld.js
lines: 7-10
<div className="homeworld-details">
<p>Name: {props.name}</p>
<p>Rotation Period: {props.rotation_period}</p>
</div>
Expected Output:
If you look on the sandbox webpage, the "Name" and "Rotation Period" (Under "Homeworld") should display the values from here: https://swapi.co/api/planets/1/
Is there anyone who can help me figure this out?
EDIT:
I got really close making these changes (using my local machine, the code on the sandbox is still the original)...
App.js
let temp = {...this.state.starwarsChars} // use spread operator to clone it, so you don't mutate state on next line;
for (let character in temp) {
if (temp[character].homeworld === URL) {
temp[character].homeworld = homeWorldObject;
}
}
// console.log(temp);
this.setState({
starwarsChars: temp
});
Character.js
const Character = props => {
props.getHomeworld(props.characterDetails.homeworld);
console.log(props.characterDetails); // returns object{homeworld: {object}}
console.log(props.characterDetails.homeworld); // returns url
and...
<div className="character-homeworld">
<Homeworld
homeworld={props.characterDetails.homeworld}/>
</div>
However, the issue now is if I do console.log(props.characterDetails.homeworld);, it logs homeworld: url
and...
if I do console.log(props.characterDetails);, it logs the property of the character object as homeworld: {object}
...
What I want is the 2nd one, and I'm not sure why it's not consistent.
Update
For some strange reason, codesandbox is console logging both urls, and when I run with yarn start, it logs the url for one, and the object for another. Because of this... I am adding the github link here -> https://github.com/jamespagedev/Sprint-Challenge-React-Wars (so the error can properly be reproduced)
Edit 2:
I changed the sandbox code to the following so we are now only worrying about the code in 1 file -> https://codesandbox.io/s/0mo32q85pp
Here is the issue I am now seeing, and I'm not sure how to solve it...
getHomeworld = URL => {
let home;
fetch(URL)
.then(res => res.json())
.then(homeWorldObject => {
home = homeWorldObject;
console.log(home); // home is an object
});
console.log(home); // why is home undefined?
return home;
};
I've tried doing return homeWorldObject, but the main function just returns undefined. After doing the console logging, that was what I found, and I'm not sure why that is happening...

Resources