Cypress and Script Injection inside test scenario - javascript

I am pretty new to Cypress and at the moment I am trying to test a webpage that uses browser extension I created. This extension only injects a set of JS and CSS files to the webpage and I want to simulate the same thing in my integration tests to be able to inject the libraries and test the behavior. I was wondering if there is a way to access document object from Cypress test to inject CSS or JavaScript to the head of the webpage.

Yes, there is. Cypress is actually running in the browser, and although commands are queued asynchronously, you can queue up native JS code to be run, like so:
cy.get("html").then(() => {
document.querySelector("div.myDiv").innerHTML = "...";
// ...
});
If you are trying to target or modify a specific element, you can get it via Cypress to take advantage of automatic retries to wait for the element to exist before operating on it:
cy.get("div.myDiv").then(elem => {
elem.innerHTML = "...";
// ...
});

Related

How to use libraries installed via <script> tags in React

I'm trying to create a Facebook Instant HTML5 application in React.
As per their Quick Start documentation, they want me to install their SDK using a script tag, like so:
<script src="https://connect.facebook.net/en_US/fbinstant.6.3.js"></script>
I've created my app using create-react-app. I've placed that snippet inside /public/index.html so it looks like:
...
<body>
<noscript>You need to enable javascript to run this app.</noscript>
<script src="https://connect.facebook.net/en_US/fbinstant.6.3.js"></script>
...
They also provide the following snippet:
// Once all assets are loaded, tells the SDK
// to end loading view and start the game
FBInstant.startGameAsync()
.then(function() {
// Retrieving context and player information can only be done
// once startGameAsync() resolves
var contextId = FBInstant.context.getID();
var contextType = FBInstant.context.getType();
var playerName = FBInstant.player.getName();
var playerPic = FBInstant.player.getPhoto();
var playerId = FBInstant.player.getID();
// Once startGameAsync() resolves it also means the loading view has
// been removed and the user can see the game viewport
game.start();
});
Which I've placed in src/index.tsx'.
This then gives me errors, saying:
'FBInstant' is not defined no-undef
Which likely means that the library is not being installed properly / brought into the proper namespace so that my React code can access it.
How can I get around this? Facebook tells me not to download and include it myself - they'll reject my app.
Important notes:
Do not download and add the SDK to your bundle as it will be rejected in later steps.
This is a new way to build games on Facebook and does not support the Graph API.
So it seems I must use these <script> tags. How can I make React recognise it?
Once you have added the fbinstant script tag in the index.html
In your src/index.tsx, Add this to the top (before your snippet):
const FBInstant = window.FBInstant;
As you explained, you're getting this error because the typescript compiler does not recognize FBInstant, Since it is not installed.
However in this case seems all you need is to make the compiler ignore these warning. The most suitable in my opinion is export it to a seperate js file (and not ts / tsx), since it is a javascript code. Than import it as any node module.
If you don't like this option, alternative methods can be:
including the SDK code on your index.html, right after your script tag
Using #ts-ignore to suppress typescript errors
define FBInstant before the SDK script (more about this here)
interface Window {
[key:string]: any; // Add index signature
}
const FBInstant = (window as Window)['FBInstant'];

How to handle asynchronous completion of Javascript in an iOS share extension?

I am making a simple iOS share extension that will operate on web pages. It is not unlike Pinterest, in that it grabs certain info from the page. But it is different from Pinterest in that the JS used to do the grabbing is sometimes custom to the web page domain.
So in the ExtensionPreprocessingJS run() function I do the following to load and execute the custom JS:
var Action = function() {};
Action.prototype = {
run: function(arguments) {
var e = document.createElement('script');
e.setAttribute('type','application/javascript');
e.setAttribute('id','my-unique-id');
e.setAttribute('src','//mydomain.com/bookmarklet?d='+document.domain);
document.body.appendChild(e);
<some code to extract results and generate JSON>
arguments.completionFunction({ <my JSON here> });
}
};
var ExtensionPreprocessingJS = new Action
My bookmarklet endpoint provides some JS that finds the relevant info from the page for "pinning".
My problem is that this happens asynchronously and not before I pass back the results at the end of the run method using
arguments.completionFunction( <my JSON here> );
I have tried waiting for the result to be set before calling completionFunction() but the extension seems to treat that as completionFunction() not being called at all. That is, it seems the design of the share extension framework doesn't accommodate asynchronous JS. It assumes the result from the JS is available after the run() function returns.
Am I misunderstanding the share extension design, or do I need to look for a different way to do this? I do notice that Instapaper created a share extension with no native UI and I'm wondering whether this is why they did so.

Phantomjs disable javascript in page but enable included javascript

I am using phantomjs to retrieve CSS information from a page without execute its javascript. For example here is the code snippet.
page.settings.javascriptEnabled = false;
page.open('file:///home/sample.html', function(status) {
if (status !== 'success') {
console.log('Unable to access network');
} else {
page.includeJs("file:///home/sample.js", function() {
var class = page.evaluate(function() {
return document.querySelector('body').className;
});
console.log(class);
});
}
}
If I disabled the javascript, the evaluate function always return null. But when I tried to enable the javascript, the evaluate function will return some value. Is there any idea to disable the javascript in the page, but my included javascript have to work ?
No
page.evaluate() executes JavaScript on the page. If you disable JavaScript in PhantomJS, then you effectively can't use page.evaluate() anymore. And with it goes every way of accessing DOM elements. page.includeJs() will also not work, because it the script cannot be executed on the page.
You can still access page.content which provides access to the current page source (computed source). You may try to use some DOM library to parse the source into a DOM object1 or if the task is simple, you may try to use Regular Expressions.
1 Note that PhantomJS and node.js have different execution environments, so most node.js modules that deal with the DOM won't work
As suggested by Artjom, there is no way to disable execution of the target website JavaScript without disabling PhantomJS ability to execute JavaScript on the page. However, there is a simple way to ensure that no scripts are executed by the target website (which achieves the same result, at the end).
Create a HTTP proxy that intercepts all requests.
Detect responses with Content-Type: text/html.
Remove all <script> tags from the document.
You can configure phantomjs to use proxy using --proxy configuration.
Use http-proxy to create a proxy server.
Use cheerio to remove, comment out, or otherwise invalidate the <script> tags.

CKEditor variable is available in console, but not from a Chrome userscript?

I'm writing a Chrome userscript to locally auto-save content in a CKEditor. I'm using this CKEditor auto-save plugin as inspiration.
I have written a function that fires every half second (via an interval) to register the CKEditor event handler:
var intervalId = window.setInterval(function() {
if (CKEDITOR) {
window.clearInterval(intervalId);
CKEDITOR.plugins.add("user-script-auto-save", {
init : function(editor) {
editor.on('key', startTimer);
}
});
}
}, 500);
However, it never properly completes, and complains that "CKEDITOR is undefined" on the if (CKEDITOR) statement.
Meanwhile, if I drop into Chrome's console and type CKEDITOR, the console prints out the expected object.
What am I missing? The editor is embedded within an iframe; might that have an impact on scoping? Or am I fighting against Chrome's sandboxing here? And if so, is there some other way I can dig into CKEditor to pull out the content every second or something to do the auto-saves?
I have not yet tried the script in Firefox; that's next on my list.
Worth noting: I'm a long-time JavaScript novice. So I could easily be doing something dumb with scoping or something like that.
According to this little tutorial video on YouTube, all the 3 "devices" are separated from each other in order to prevent XSS attacks from the user script to the browser / website and vice versa. Although the user scripts / content scripts are running in the website's context, they are still kept separated from the actual website script context. You can easily acknowledge this by simply trying to access for example jQuery from a content script. Just as the CKEditor, it will not be available.
So what I've come up with in order to deal with this is using the content script to include external JavaScripts in the head tag. AFAIK, this is not possible for files directly in the extension's root directory, so I've taken a remote server to host my files.
I'm not sure if this is the best approach and I think it is an ugly bypass, possibly way to powerfull and disabled by the Chromium Project some time.
(Edited by OP, so I can select this answer and route karma appropriately)
This answer, combined with some of the suggestions and links in the comments, ended up getting me to where I needed to be.
I ended up with the following function:
var insertScriptIntoDocument = function(scriptUrl, doc) {
// inspired by http://blog.afterthedeadline.com/2010/05/14/how-to-jump-through-hoops-and-make-a-chrome-extension/
var scriptText = doc.createTextNode(
'(function(loc) { \
var embeddedScript = document.createElement("script"); \
embeddedScript.type = "text/javascript"; \
embeddedScript.src = loc; \
document.getElementsByTagName("head")[0].appendChild(embeddedScript); \
})("' + scriptUrl + '");');
var injectorElement = doc.createElement('script');
injectorElement.appendChild(scriptText);
doc.body.appendChild(injectorElement);
};
Usage looks like so:
var embeddedScriptUrl = chrome.extension.getURL("embedded-script.js");
insertScriptIntoDocument(embeddedScriptUrl, document);
For now, I'm executing this from within a Chrome extension, but I suspect that the pattern might work in a GreaseMonkey script deployed via the Chrome TamperMonkey extension provided that the URL of the script to be embedded was hosted somewhere reachable.
FTR, as it turns out, I did not actually need to get to the iframe -- the CKEDITOR variable was defined in the top-level document, but was simply not visible because of the rules of the Chrome sandbox

PowerShell JavaScript Button Click is there any Force Command?

I want to create automated web tests with PowerShell.
My problem is now that when I click a button with a JavaScript Popup the script hangs at the following line because the event is not finished.
The Popup is loaded but the script hangs.
Is there any force command? Because in the next lines I want to use a Commandlet to click the ok button.
$ie.Document.getElementById("ButtonID")|foreach{$_.Click()} #never finished
There are two approaches to this common problem with automated testing in browsers. Your first option is to redefine all javascript functions with your own mock implementation.
window.alert = function(msg) { log_info(msg); };
window.prompt = function(msg) { return fixture(msg); };
window.confirm = function(msg) { return randomBool; };
Modifying these functions (and others like print, etc) before you start your automation will stop the prompts from blocking the process. But you may still have to worry about security alerts and credential dialogs.
So the second approach is to run a monitoring utility that can interact with the windows on the OS level. Take a look at IEUnit - http://code.google.com/p/ieunit/ It uses a custom activex control called Desktoop (mispelled on purpose) that allows interacting with windows and security alerts from the Windows Script Host.
So you could either use the activex object in powershell or follow this reference - http://msdn.microsoft.com/en-us/magazine/cc163301.aspx for ideas on implementing your own desktoop like module using native powershell features

Categories

Resources