Stubbing link inside an iframe - 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')
})
})

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.

Cypress - Trying to target an iframe within a pane-html div element that pops out from the side

I am working on learning how to write tests in cypress and have run into quite a bit of a headache trying to target an iframe within a panel-html div element that pops out from the side. No matter what I try I get a reference error saying it is the app and not cypress but I have friends working on the same app that got this to work what am I doing wrong here? Will post code for reference below:
/// <reference types="cypress" />
describe('login test', () => {
})
Cypress.Commands.add('iframeLoaded',
{ prevSubject: 'element' },
($iframe) => {
const contentWindow =
$iframe.prop('contentWindow')
return new Promise(resolve => {
if (
contentWindow &&
contentWindow.document.readyState === 'complete'
) {
resolve(contentWindow)
} else {
$iframe.on('load', () => {
resolve(contentWindow)
})
}
})
})
Cypress.Commands.add(
'getInDocument',
{ prevSubject: 'document' },
(document, selector) =>
Cypress.$(selector, document)
)
it('LOGS IN', () => {
cy.visit('https://app.alchemer.com/login/v1')
cy.get('#username.form-control').type('xxxxxx')
cy.get('#password.form-control').type('xxxxx')
cy.get('button.btn.btn-primary.btn-block.btn-lg.login-btn.btn-wrap').click()
cy.get('button[type=submit]'),cy.visit('https://app.alchemer.com/?switch-account-done=1')
cy.visit('https://app.alchemer.com/builder/build/id/6827242')
cy.pause()
})
it('adds questions', () => {
cy.get('#section-1 > .section-body > .insert-question-container > .hidden-xs > .list-inline > :nth-child(1) > a')
.click()
cy.get('div#question-edit-pane.pane.pane-has-header.pane-has-header-nav.pane-has-footer.pane-open.pane-focused.pane-level-1')
.iframeLoaded('#cke_1_contents.cke_wysiwyg_frame')
.getInDocument('#cke_editable.cke_editable_themed.cke_contents_ltr.cke_show_borders')
.should('be.visible')
.click()
//.find('iframe.cke_1_contents.cke_wysiwyg_frame')
// .click()
//.its('0.contentDocument.body')
//.find('#cke_editable.cke_editable_themed.cke_contents_ltr.cke_show_borders')
.type('Hello World')
})
My suggestion would be to use the cypress-iframe plugin for this.
To install npm install -D cypress-iframe
In your cypress/support/commands.js file, add the following:
import 'cypress-iframe';
In your test write:
cy.frameLoaded('#cke_1_contents.cke_wysiwyg_frame')
cy.iframe()
.find('#cke_editable.cke_editable_themed.cke_contents_ltr.cke_show_borders')
.should('be.visible')
.click()
You can check out the plugin page for more commands.
Try accessing the editor from the current window
cy.get('div#question-edit-pane')
.iframeLoaded('#cke_1_contents.cke_wysiwyg_frame')
.getInDocument('#cke_editable')
.should('be.visible')
.click() // change to edit mode
cy.window().then(win => {
const editor = Object.values(win.CKEDITOR.instances)[0];
cy.wrap(editor)
.type('Hello World')
})

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

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)
})
})
})

cypress - click iframe element while the body of the iframe change after few seconds

I have a problem when using cy.getIframeBody().find('#some-button') that the #some-button element is not yet available, because the iframe is still loading, but the body element is not empty so the .find() is triggered.
This is the custom command to get the iframe body
Cypress.Commands.add('getIframeBody', ()=> {
return cy.get('iframe.cy-external-link-container')
.its('0.contentDocument.body')
.should('not.empty')
.then(cy.wrap)
});
How I can do it without using cy.wait()?
You can add random timeouts and .should() assertions, but if they work at all the test is likely to be flaky.
The key is to repeat the query of the body element (this line)
.its('0.contentDocument.body')
until the button shows up.
So not
.its('0.contentDocument.body')
.should('not.empty')
but something like
.its('0.contentDocument.body')
.should('have.child', '#some-button') // force a retry on body query
.should() with callback will do this
Cypress.Commands.add('getIframeBodyWithSelector', (waitForSelector) => {
return cy.get('iframe.cy-external-link-container')
.its('0.contentDocument.body')
.should(body => {
expect(Cypress.$(body).has(waitForSelector).length).gt(0)
})
.then(cy.wrap)
})
it('finds some button', () => {
cy.getIframeBodyWithSelector('#some-button')
.find('#some-button') // passes
})
You can add an timeout and also add should('be.visible'). should assertion will make sure that till the timeout value is reached it rerties and make sure that the iframe is loaded successfully.
Cypress.Commands.add('getIframeBody', () => {
return cy
.get('iframe.cy-external-link-container', {timeout: 7000})
.should('be.visible')
.its('0.contentDocument.body')
.should('not.empty')
.then(cy.wrap)
})

Scraping infinite scroll href with Cypress

I'm using Cypress to scrape a site with an infinite scroll.
The site is made with React, and after the user enters a search term in an input, as they scroll more products appear on the page matching the search term entered.
The code I've got so far opens a URL, navigates to the URL and collects all the hrefs that are currently visible.
I'm wondering is how I can tell cypress to scroll down further, slowly harvesting all the hrefs as it scrolls down the page, and then finally writing the hrefs to the json.
This is the code I have so far, minus the scrolling:
const arrayOfHrefs = [];
describe('Get links', () => {
it.only('should do a product search', () => {
cy.visit('https://www.testsite.com');
cy.wait(5000);
cy.get('#product_input').type('socks');
cy.contains('socks').click(); // renders new content on the client side
cy.wait(10000);
cy.get('a').each(($a) => {
const link = $a.attr('href');
arrayOfHrefs.push(link); // grabs all visible links and pushes them to array
}).then(() => {
console.log(arrayOfHrefs)
cy.writeFile('data.json', { urls: arrayOfHrefs }) // writes array to disk
})
});
});
You did not detail what you have tried so far and what issues you're currently having regarding scrolling, but I assume scrolling down the window and then adding some logic to wait until more links become visible is sufficient.
This command scrolls down the whole window to the bottom over 5000ms:
cy.scrollTo('bottom', {duration: 5000})
Note that it's not chained off from an element like:
cy.get('#some-scrollable-element').scrollTo(...)
I googled a page that has some similar dynamic infinite scroll behaviour, maybe you could base your code on the following snippet:
describe('', () => {
before('', () => {
cy.server()
cy.route('GET', '**/blog/page/**').as('blog')
})
it('', () => {
let numberOfChildren = 4
cy.visit('http://www.drewleague.com/blog/')
for (let i = 0; i < 5; i++) {
cy.get('.posts--desktop')
.children()
.then(children => {
cy.wrap(children)
.its('length')
.should('eq', numberOfChildren)
})
cy.scrollTo('bottom', {duration: 5000})
.wait('#blog')
.then(() => numberOfChildren += 4)
}
})
})
This code scrolls down the page to the bottom 5 times, and in each iteration we check the number of children which are dynamically added, also we wait until the xhr request finishes. Not very useful on its own but you get the idea.

Resources