I want to test content of the iFrame Tag using jest.
For e.g I have a small html file that shows google homepage in iframe.
I want to test that google homepage is coming in iFrame or not.
<!DOCTYPE html>
<html>
<body>
<h2>Google</h2>
<iframe src="http://www.google.com" style="border:none;"></iframe>
</body>
</html>
can someone suggest me that how can I test that iframe using jest ?
Thanks in Advance.
const fs = require('fs');
const path = require('path');
const html = fs.readFileSync(path.resolve(__dirname, './index.html'), 'utf8'); //--> get the file content
describe('Iframe Test Suit', function () {
beforeEach(() => {
document.body.innerHTML = html.toString();
});
it('Should load iframe content', function (done) {
const iframe = document.querySelector("iframe"); //--> get the iframe element
expect(iframe).toBeTruthy();
onLoad();
function onLoad() {
const iframeContent = iframe.contentDocument || iframe.contentWindow.document;
if (iframeContent.readyState == "complete") {
const input = iframeContent.querySelector("input");
expect(input).toBeTruthy();
done();
} else {
setTimeout(() => onLoad(), 100); //--> call again if iframe content is not loaded
}
}
});
});
By default, jsdom will not load any sub-resources like iframes. To load such resources you can pass the resources: "usable" option, which will load all usable resources.
jest.config.js
"testEnvironmentOptions": { "resources": "usable" }
Related
I'm trying to test an a page with an external component which contains a following iframe:
<iframe id="iframe1" src="about:blank" ... >
This iframe has an empty body initially but is populated with content after some actions.
When trying to run the following piece of code:
.expect(myIFrameSelector().visible).ok()
.switchToIframe(myIFrameSelector())
.expect(firstRowSelector()).eql("Hello")
I receive the following error on line 3:
The content of the iframe in which the test is currently operating did not load.
I tried waiting for the contents to appear with wait() and I checked it with debug() too.
Any ideas what could be the problem?
I assume that this is because the content was probably populated using JS, so can I somehow tell testcafe that the content is actually ready?
You can try to wait and check if the document in the iframe is loaded using ClientFunction.
For example:
import { Selector, ClientFunction } from 'testcafe';
const waitForIframeLoad = ClientFunction((iframeSelector) => new Promise((resolve, reject) => {
var i = 0;
var intervalId = null;
intervalId = window.setInterval(() => {
var iframeElement = document.querySelector(iframeSelector);
if (iframeElement
&& iframeElement.contentWindow
&& iframeElement.contentWindow.location.href !== 'about:blank'
&& iframeElement.contentDocument) {
window.clearInterval(intervalId);
resolve();
}
if (i > 60) {
window.clearInterval(intervalId);
reject(new Error('Iframe content loading timeout'))
}
i++;
}, 1000);
}));
fixture`fixture`
.page`http://example.com`;
test('test', async t => {
const iframeSelector = '#simulatorFrame';
await waitForIframeLoad(iframeSelector);
await t
.switchToIframe(iframeSelector)
.click(Selector('button'));
});
I'm using cypress to test my VueJS application. The one thing I'm having trouble with is mocking an image to be displayed on the page. For my use case, I'm simply loading a user profile with the following code:
describe('Test Login', () => {
it('Can Login', () => {
cy.server();
cy.route({
method: 'GET',
url: '/api/account/',
response: 'fx:profile.json',
});
cy.route('**/media/demo1.png', 'fx:demo1.png');
});
});
fixtures/profile.json
{
"avatar": "http://localhost:8080/media/demo1.png",
"username": "cypress",
"email": "email#cypress.io",
"pk": 1,
"is_staff": true,
"is_superuser": true,
"is_active": true
}
The profile fixture data is loading correctly in the test. In my fixtures folder, I also have a demo1.png file. I am expecting this image to be loaded and displayed on the page during my test, but it is being displayed as a broken image.
In the network tab, it shows demo1.png as a broken image with a 200 response code and type of text/html.
The cypress documentation mostly discusses images in the context of uploading images, but I haven't been able to find an example of how I can mock an image that is loaded through a <img> tag. Is there an easier way of doing this?
I am not sure if this answer can help you. But at least it is a workaround for this problem ;-)
Say we have a HTML like this:
<html>
<body>
<button id="button">load</button>
<div id="profile">
</div>
<script>
function httpGetAsync(theUrl, callback)
{
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
callback(JSON.parse(xmlHttp.responseText));
}
xmlHttp.open("GET", theUrl, true); // true for asynchronous
xmlHttp.send(null);
}
document.getElementById("button").addEventListener("click", () => {
httpGetAsync("/api/account/", (result) => {
var div = document.querySelector("#profile");
var img = document.createElement("img");
img.src = result.avatar;
div.appendChild(img)
})
})
</script>
</body>
</html>
source: HTTP GET request in JavaScript?
And you want to load the profile after the click was done. Then you can use MutationObserver to replace the img.src.
First, write the MutationObserver:
var observeDOM = (function(){
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
return function( obj, callback ){
if( !obj || !obj.nodeType === 1 ) return; // validation
if( MutationObserver ){
// define a new observer
var obs = new MutationObserver(function(mutations, observer){
callback(mutations);
})
// have the observer observe foo for changes in children
obs.observe( obj, { childList:true, subtree:true });
}
else if( window.addEventListener ){
obj.addEventListener('DOMNodeInserted', callback, false);
obj.addEventListener('DOMNodeRemoved', callback, false);
}
}
})();
(heavily copy & pasted from Detect changes in the DOM)
Now you are able to do this:
describe('Test Login', () => {
it('Can Login', () => {
var win = null;
cy.server();
cy.route({
method: 'GET',
url: '/api/account/',
response: 'fx:profile.json'
});
cy.visit("index.html").then(w => {
cy.get("#profile").then(pro => {
var e = pro[0];
observeDOM(e, (m) => {
// add a red dot image
m[0].addedNodes[0].src = "data:image/png;base64,"+
"iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAALGP"+
"C/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9YGARc5KB0XV+IA"+
"AAAddEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAF1J"+
"REFUGNO9zL0NglAAxPEfdLTs4BZM4DIO4C7OwQg2JoQ9LE1exdlYvBBeZ7jq"+
"ch9//q1uH4TLzw4d6+ErXMMcXuHWxId3KOETnnXXV6MJpcq2MLaI97CER3N0"+
"vr4MkhoXe0rZigAAAABJRU5ErkJggg=="
})
})
cy.get("button").click()
})
});
});
(yeah at least some lines of code are written on my own ;-P)
You can read the image from the img.src attribute from the fixtures folder. For the sake of simplicity I have used a static base64 string here.
And the result:
We are not using this kind of stuff in our aurelia app but I tried similar things in a private project some time ago.
So I have an iFrame that displays a four page pdf on my SharePoint site. This iFrame is very handy for displaying the PDF, but I would like to make it so that clicking anywhere within the frame will open the PDF in browser. I have tried implementing some different strategies using and tags but have not found anything to accomplish this goal. The code I am using for the iFrame is below.
Any suggestions would be welcome.
<iframe width="500" height="550" src="URLtoPDF"></iframe>
You can use below jQuery code on document ready if both domains are same (iframe and where it is located)
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$('iframe').load(function () {
$(this).contents().find("body").on('click', function (event) {
window.open($(this).attr("src"), "_blank");
});
});
});
</script>
Use below code in case of simple javascript without any js file reference
<script type="text/javascript">
window.onload = function () {
var frame = document.getElementById('iframe');
frame.onload = function () {
var body = frame.contentWindow.document.body;
body.onclick = function () {
var src = frame.getAttribute("src");
window.open(src, "_blank");
};
}
}
</script>
iframe is the id of the iframe element
I am trying to automatically test tracking code and I am using the RequestLogger from Testcafé. I succeeded to intercept calls to example.com and localhost but not to https://www.google-analytics.com/. What could be the reason?
Expected
This test should be green
Test code
import { RequestLogger } from 'testcafe';
const logger_ga = RequestLogger('https://www.google-analytics.com/');
fixture `localhost`
.page('http://localhost:8000')
test
.requestHooks(logger_ga)
('logs calls to Google Analytics', async t => {
await t.click("#ga-button");
console.log(logger_ga.requests); // is empty due to timing
await t.expect(logger_ga.contains(record => record.response.statusCode === 200)).ok();
});
Fixture for this test
I am serving the following index.html page via python -m SimpleHTTPServer 8000
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Test page</title>
</head>
<body>
<p>Hello world!</p>
<!-- Google Analytics: change UA-XXXXX-Y to be your site's ID. -->
<script>
window.ga = function () { ga.q.push(arguments) }; ga.q = []; ga.l = +new Date;
ga('create', 'UA-XXXXX-Y', 'auto'); ga('send', 'pageview')
</script>
<script src="https://www.google-analytics.com/analytics.js" async defer></script>
<a onclick="ga('send', 'event', 'my_event_category', 'my_event_action', 'my_event_label');" href="#" id="ga-button">Google Analytics</a>
</body>
</html>
Observed
The above test is red
However, these tests are green
import { RequestLogger } from 'testcafe';
const logger = RequestLogger('http://example.com');
fixture `example`
.page('http://example.com');
test
.requestHooks(logger)
('logs calls to example.com', async t => {
await t.expect(logger.contains(record => record.response.statusCode === 200)).ok(); // green
});
const logger_localhost = RequestLogger('http://localhost:8000');
fixture `localhost`
.page('http://localhost:8000');
test
.requestHooks(logger_localhost)
('logs calls to localhost', async t => {
await t.expect(logger_localhost.contains(record => record.response.statusCode === 200)).ok(); // green
});
How can I intercept calls to Google Analytics successfully?
As Marion suggested it is probably due to timing. The following code works:
import { Selector, RequestLogger } from 'testcafe';
const gaCollect = 'https://www.google-analytics.com/collect';
const gaLogger = RequestLogger({gaCollect}, {
logRequestHeaders: true,
logRequestBody: true,
});
fixture `Fixture`
.page('http://localhost:8000')
.requestHooks(gaLogger);
test('Log Google Analytics call', async t => {
await t.click('#ga-button')
await t.expect(gaLogger.contains(record =>
record.request.url.match(/ec=my_event_category&ea=my_event_action&el=my_event_label/))).ok();
for(let r of gaLogger.requests) {
console.log("*** logger url: ", r.request.url);
}
});
The timing factor #Marion mentioned seems to play a role. Compare the previous with the following snippet and its output. Here, we do not see the calls logged to https://google-analytics.com/collect.
fixture `Fixture`
.page('http://localhost:8000')
.requestHooks(gaLogger);
test('Log Google Analytics call', async t => {
await t.click('#ga-button')
for(let r of gaLogger.requests) {
console.log("*** logger url: ", r.request.url);
}
await t.expect(gaLogger.contains(record =>
record.request.url.match(/ec=my_event_category&ea=my_event_action&el=my_event_label/))).ok();
});
I'm trying to build an isomorphic app using express and react-router with data-fetching and first render server-side and data manipulation client side.
I managed to fetch initial data server side and render the jsx components but it works only if the url is directly hit, not following links. In fact, as in all the examples I've read, the app is server-rendered only once and then everything happen client side.
Plus, if I fetch some data, render a component server-side and then follow a link, the data are not updated for the new page.
I don't know if I'm trying to do something that makes no sense?
What I would like to obtain is:
A pre-render server-side for every page, regardless of whether the user arrives directly or through a link
Fetching just the needed initial data the component corresponding to the route is asking for
[BONUS] apply small changes to the layout regarding the component (title, additional css, etc.)
Here is what I have so far:
Express app:
var express = require('express');
require('node-jsx').install();
var React = require('react');
var Router = require('react-router');
var routes = require('./routes');
var url = require('url');
var resolveHash = require('when/keys').all;
var app = express();
/*
....
*/
app.use(function(req, res, next) {
Router.run(routes, url.parse(req.url).pathname, function(Handler, state){
// create the promises hash
var promises = state.routes.filter(function (route) {
// gather up the handlers that have a static `fetchData` method
return route.handler.fetchData;
}).reduce(function (promises, route) {
// reduce to a hash of `key:promise`
promises = route.handler.fetchData(state.params);
return promises;
}, {});
resolveHash(promises).then(function (data) {
var html = '<!DOCTYPE html>' + React.renderToString(React.createFactory(Handler)({path:url.parse(req.url).pathname, initialData:safeStringify(data)}));
res.send(html);
});
});
// A utility function to safely escape JSON for embedding in a <script> tag
function safeStringify(obj) {
return JSON.stringify(obj).replace(/<\/script/g, '<\\/script').replace(/<!--/g, '<\\!--')
}
});
routes.js:
var React = require("react");
var Router = require("react-router");
var Route = Router.Route;
var DefaultRoute = Router.DefaultRoute;
var NotFoundRoute = Router.NotFoundRoute;
var Layout = require("./components/layout.jsx");
var Stuff = require("./components/stuff.jsx");
var Home = require("./components/home.jsx");
var routes = (
<Route path="/" handler={Layout}>
<Route path="/stuff" handler={Stuff} />
<DefaultRoute handler={Home}/>
</Route>
);
module.exports = routes;
if (typeof document !== 'undefined') {
var initialData = JSON.parse(document.getElementById("initialData").innerHTML);
Router.run(routes, Router.HistoryLocation, function (Handler) {
React.render(<Handler initialData={initialData}/>, document);
});
}
layout.jsx:
'use strict';
var React = require('react');
var Router = require('react-router');
var RouteHandler = Router.RouteHandler;
var Link = Router.Link;
var Layout = React.createClass({
render: function () {
return (
<html>
<head>
<title>{this.props.initialData.title}</title>
</head>
<body>
<nav>
<Link to={'/'}>Home</Link>
<Link to={'/stuff'}>Stuff</Link>
</nav>
<RouteHandler/>
<script id='initialData' type="application/json" dangerouslySetInnerHTML={{__html:this.props.initialData}}></script>
// actually bundle.js is just made of routes.js as I put the client side render there
<script src="/bundle.js"></script>
</body>
</html>
);
}
});
module.exports = Layout;
home.jsx:
'use strict';
var React = require('react');
var Home = React.createClass({
statics: {
fetchData: function(params){
return {test:[1,2,3], title:'Home'};
}
},
render: function () {
return (
<div className="content">
<section>
<article>
<h2>Title</h2>
<p>Body</p>
</article>
</section>
<aside>
Ads
</aside>
</div>
);
}
})
module.exports = Home;
stuff.jsx:
'use strict';
var React = require('react');
var Stuff = React.createClass({
statics: {
fetchData: function(params){
return {test:[4,5,6], title:'Stuff'};
}
},
render: function(){
return (<h1>Hello world from thingy!</h1>)
}
})
module.exports = Stuff;
What are all the things I'm missing, misunderstanding, doing the wrong way?
This is how it is intended to work, it will give you that first load and build that html and render it, after that it can't change that html that was generated unless you do a refresh of the page by clicking the refresh button or having your code window.location.