Injecting javascript function in nightmareJS - javascript

I was given a javascript function that I need to inject into a page in order to get a list of values that would be used later on. I can call this function directly on the webpage using the Chrome console but I want to replicate what I did in the Chrome console in nightmareJS on the webpage that is currently loaded.
This is the function:
function getList() {
require(['Service/List'],
function (Service)
{
Service.getList
({
onComplete: function (listOfServices)
{
console.log('List:success:' + JSON.stringify(listOfServices));
},
onFailure: function (error)
{
console.log('List:error:' + error);
}
});
});
}
getList();
I've tried injecting the file but I have had no success, I've also tried adding additional code to that function to write the output to a file but I do not think its being called at all.
Here is the nightmareJS
describe('Open login page', () => {
it('Login', done => {
nightmare
.goto('http://loginURL.com')
.wait('input[type="text"]')
.wait('input[type="password"]')
.insert('input[type="text"]', 'username')
.insert('input[type="password"]', 'password')
.click('input[type="submit"]')
.evaluate(function() {
nightmare.inject('js', 'getList.js' )
})
//.inject('js', 'getList.js' )
.then(function() {
console.log('Done');
})
})
})
})
This is the sample output after injecting the javascript file into the page:
List:success:"Test":"https://someURL.com/resource/test","Design":"https://someURL.com/resource/Design"},"NewSpace":"https://someURL.com/resource/NewSpace","Generator":"https://Generator.someURL.com/resource/test","SomethingElse":"https://someURL.com/SomethingElse/test","Connection":"https://someURL.com/Connection/test","WorldWide":"https://someURL.com/resource/WorldWide","Vibes":"https://Vibes.someURL.com/resource/test","GoogleSearch":"https://someURL.com/resource/GoogleSearch",
I want to be able to get that output from calling the javascript file on the page and save it to a file so that I can use it later to call other services in that list.

You can read the local javascript files that needs to be injected:
var fileData = [];
fileData.push(fs.readFileSync(path.resolve('../getList.js'), 'utf-8'));
It can be loaded into head section of the page via code:
browser.win
.evaluate(function(fileData) {
var elem = null;
for(var ii=0;ii<fileData.length; ii++ ) {
elem = document.createElement('script');
if (elem) {
elem.setAttribute('type', 'text/javascript'); //elem.src = localjs;
//console.log(fileData[ii]);
elem.innerHTML = fileData[ii];
document.head.appendChild(elem);
}
}
console.log("Testing loaded scripts");
console.log(getList());
return "Injected Scripts";
}, fileData)
.then(function(result) {
console.log(result);
}).catch(function(error) {
console.log("Load Error: ", error);
});

Related

Why is it only executed once when generating dynamic javascript?

I'm making a web application based on Express and axios. I'm trying to create a static navigation and load an HTML file on each menu click while dynamically loading the associated script file.
Page Router
...
router.get('/page/home/:location', (req, res) => {
const location = req.params.location;
fs.readFile(path.resolve(__dirname + `../../../views/pages/${location}.ejs`), (error, html) => {
if(error) {
res.status(500).end();
} else {
res.status(200).end(html);
}
});
});
Main(.ejs)
...
<button class="nav-grid-item-menu-button" type="button" onclick="mainSidebarUiMouseclick(this)" value="diary">Diary</button>
...
<div id="content"></div>
Controller
const mainSidebarUiMouseclick = (event) => {
const content = document.getElementById('content');
let script;
// Remove all child
content.innerHTML = '';
axios.get(`${window.location.href}/${event.value}`)
.then((res) => {
content.innerHTML = res.data;
})
.then(() => {
switch(event.value) {
case 'diary':
script = document.createElement('script');
script.setAttribute('src', `/js/pages/ui-${event.value}-controller.mjs`);
script.setAttribute('crossorigin', 'anonymous');
script.setAttribute('type', 'module');
script.defer = true;
script.addEventListener('load', () => {
console.info(`#system, Dynamic script loading complete`);
});
content.appendChild(script);
break;
...
}
})
.catch((error) => { console.error(`#error, ${error}`); });
};
The script is always executed the first time it is clicked without any problem. However, if you return to another menu after moving to another menu, the script is not executed normally. This structure is not available for document.body. The reason is that all existing script files are dynamically erased when the menu is clicked. How should I solve it?

Force Nuxt to run script tag

I have a script on a page:
<script type="text/javascript">app_id="ID_HERE";distribution_key="dist_6";</script><script type="text/javascript" src="https://loader.knack.com/ID_HERE/dist_6/knack.js"></script><div id="knack-dist_6">Loading...</div>
If I go to the page via a NuxtLink in the navigation the script runs, however if I type the URL to the browser address bar it doesn't.
Is there a way to force the NuxtLink result when the page is accessed directly?
The script tag is coming from a CMS so there isn't a way to hardcode it somewhere nice.
There is a head method (Docs) which will let you to load external scripts but
I can't find the documentation for the script property.
I would dynamically load the script only on the client side like this:
<template>
<div id="knack-dist_6">Loading...</div>
</template>
<script>
export default {
// ...
async created() {
// Do not load the script on the server side
if (!process.client) return
// Function to load the external script
function loadLib(id, distKey) {
const scriptId = 'knack-js'
return new Promise((resolve, reject) => {
// If script already exists than do not load it again
if (document.getElementById(scriptId)) {
resolve()
return
}
const s = document.createElement('script')
s.src = `https://loader.knack.com/${id}/${distKey}/knack.js`
s.id = scriptId
s.type = 'text/javascript'
s.onload = () => {
resolve()
}
s.onerror = (e) => {
reject(e)
}
document.head.appendChild(s)
})
}
try {
if (!window.app_id) {
window.app_id = 'ID_HERE'
window.distribution_key = 'dist_6'
}
await loadLib('ID_HERE', 'dist_6')
} catch (e) {
// Handle script loading error
console.error(e)
}
}
}
</script>

Angularjs code not running in anonymous function

I wrote an anonymous javascript function that dynamically loads jQuery and Angular. However, my angular code is not running for some reason. The code works find until i hit my main function. I then try to make an http request and print the results in my angular controller, but theyre not printing for some reason.. Can someone help? Here are my fiddle and code:
https://jsfiddle.net/5f44yb7k/1/
Thanks in advance!
(function() {
var jQuery;
var jquery_tag = document.createElement('script');
var angular_tag = document.createElement('script');
function getJqueryTag() {
console.log('getting jquery tag');
jquery_tag.setAttribute("type","text/javascript");
jquery_tag.setAttribute("src", "https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js");
jquery_tag.onload = scriptLoadHandler;
(document.getElementsByTagName("head")[0] || document.documentElement).appendChild(jquery_tag);
if(jquery_tag.onload) { //ensure angular loads after jQuery
getAngularTag();
}
}
function getAngularTag() {
console.log('now getting angular tag...');
angular_tag.setAttribute("type","text/javascript");
angular_tag.setAttribute("src", "https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js");
angular_tag.onload = scriptLoadHandler;
(document.getElementsByTagName("head")[0] || document.documentElement).appendChild(angular_tag);
}
getJqueryTag();
function scriptLoadHandler() {
if(window.jQuery) {
jQuery = window.jQuery
main();
}
}
function main() {
jQuery(document).ready(function($) {
console.log('now in main function'); //this prints fine
var test = angular.module('test', []);
function main_controller($scope, $http) {
$http.get('https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22fairfax%2C%20va%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys')
.success(function(data) {
console.log('---data returned---'); //not printing
console.log(data); //not printing
})
.error(function(data) {
console.log('error: ' + data);
});
}
});
}
})();
Angular is falling out of scope. You'll need to call the bootstrap function to get angular up and running if you're loading it in this fashion.
I've never loaded it this way, but see: https://docs.angularjs.org/guide/bootstrap, specifically the section on Manual Initialization.

Using CollectionFS how can the url of a image be returned after upload?

I am trying to return the url of the uploaded image and make it equal to uploadedurl. This is all in a function that is triggered when a photo is dropped into the upload box. uploadedurl is currently being set to null and is returning this error The provided value 'undefined' is not a valid enum value of type XMLHttpRequestResponseType. in the client console. I am using amazon S3 to store the images That part works the images are stored in the S3 and do have usable urls under the domain. What did I do wrong?
var user = Meteor.user();
var uploadedurl;
Images.insert(newFile, function (error, fileObj) {
if (error) {
//do error
} else {
fileObj.once("uploaded", function () {
uploadedurl=fileObj.url();
document.getElementById("phototag").innerHTML = '<img src=&quot'+uploadedurl+'&quot >';
});
}
});
});
},
CollectionFS objects include a url() method. In your case use:
fileObj.url();
Note that it may take awhile for large files to finish uploading. fileObj.isUploaded() will be true when the upload is done. fileObj.url() will be null until that time.
In this github ticket #aldeed mentions attaching an event handler to the fileObj to be able to get a callback once the file uploads. This is better than polling with a setTimeout. In your case:
fileObj.once("uploaded", function () {
uploadedurl=fileObj.url();
});
try this..It worked for me
FS.Utility.eachFile(event, function(file) {
Images.insert(file, function(err, fileObj) {
if (err) {
console.log(err);
} else {
var cursor = Images.find(fileObj._id);
var liveQuery = cursor.observe({
changed: function(newImage, oldImage) {
if (newImage.isUploaded()) {
liveQuery.stop();
$("#image" + postId).attr("fileId", fileObj._id);
var fielname = fileObj.original.name;
setTimeout(function() {
var imageUrl = '/cfs/files/images/' + fileObj._id + '/' + fielname;
$("#imagefile" + postId).attr("src", imageUrl);
$("#imagediv" + postId).show();
}, 5000);
}
}
});
}
});});

Get configs from background page to content script in chrome extension

I'm trying to get some user configs from the background page of my chrome extension to the content script (or popup) but I'm having some problems, I think the problem is that chrome.storage.sync.get is async, I tried using callbacks but I also read that callbacks can't return the value so I have no idea how to solve this.
Here's kinda how the code looks:
popup.js:
(function() {
chrome.runtime.sendMessage({
message: "loadconfig"
}, function(response) {
console.log(response);
if (response.status === 'success') {
console.log(response);
} else {
console.log(response.except);
}
});
})();
background.js
(function() {
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
switch (request.message) {
case "loadconfig":
sendResponse(loadStuff());
break;
default:
sendResponse({
reply: null
});
break;
}
});
function loadStuff() {
var to_return_configs = {
blocked_characters: '',
good_post: ''
};
var function_status = 'failed';
var exception = '';
var blocked_characters_parsed, good_post_parsed;
try {
var to_get = ["blocked_characters_saved", "good_post_saved"];
chrome.storage.sync.get(to_get, function(result) {
to_get.forEach(function(got) {
if (got === "good_post_saved") {
to_return_configs.good_post = result[got];
}
if (got === "blocked_characters_saved") {
to_return_configs.blocked_characters = result[got];
}
});
});
exception = '';
function_status = 'success';
} catch (err) {
exception = String(err);
function_status = 'failed';
}
var to_return = {
status: function_status,
configs: to_return_configs,
except: (exception)
};
return to_return;
}
})();
The problem here is that when I'm looking at the popup.js console, "blocked_characters" and "good_post" are both empty.
How can I solve this?
You do not need Message API for communication between Popup and Background. Popup in chrome extension can directly call methods of Background .
You can do something like this
BG = chrome.extension.getBackgroundPage();
And then you can call BG.loadStuff() in your popup js.
From within loadStuff, you can pass a callback which can return data to you. So it should look like
BG.loadStuff(function(items) {
console.log(items);
});
background.js
function loadStuff(cb) {
chrome.storage.sync.get(null, function(superObj) {
cb.call(null, superObj);
});
}
For more understanding, read these
http://blog.papersapp.com/chrome-development-parent-and-child-windows/
https://stackoverflow.com/a/17276475/816213
https://stackoverflow.com/a/17378016/816213
sendResponse(function) becomes invalid when the event listener returns, unless you return true from the event listener to indicate you wish to send a response asynchronously (this will keep the message channel open to the other end until sendResponse is called). See the reference: onMessage.
Because sendResponse is called asynchronously in chrome.storage.sync.get's callback, you need to return true from the onMessage listener to prevent the function from being invalidated. Code similar is Like:
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.message === 'loadconfig') {
sendResponse(loadStuff());
return true;
}
return false;
});

Categories

Resources