Edit: I found the code that Stack Overflow uses: https://github.com/gh-canon/stack-snippet-console
I've found a bunch of answers that show how to output the console to a webpage, but I'm trying to make it so that the messages are also logged to the console. Ironically, if you run snippets on Stack Overflow, they do what I'm trying to.
// This causes a stack overflow
var native = window;
native.console = {
log: function(message){
$('ul.messages').append('<li>Log: ' + message + '</li>');
console.log(message);
},
error: function(message){
$('ul.messages').append('<li>Error: ' + message + '</li>');
console.error(message);
},
warn: function(message){
$('ul.messages').append('<li>Warn: ' + message + '</li>');
console.warn(message);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="messages"></ul>
I think you just need to cache the original console methods and call them from the cache-- the way you have it now calls your stubbed log, which causes infinite recursion (and thus a stackoverflow):
$(document).ready(function(){
console.log('You should know...');
console.error('Something went wrong...');
console.warn('Look out for this...');
})
// This should work
var native = window;
var originalConsole = native.console;
native.console = {
log: function(message){
$('ul.messages').append('<li>Log: ' + message + '</li>');
originalConsole.log(message);
},
error: function(message){
$('ul.messages').append('<li>Error: ' + message + '</li>');
originalConsole.error(message);
},
warn: function(message){
$('ul.messages').append('<li>Warn: ' + message + '</li>');
originalConsole.warn(message);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h3>messages</h3>
<ul class="messages"></ul>
You can create a wrapper function that takes in a function and outputs your modified function.
const wrapper = (fn, name) => {
return function(msg) {
$('ul.messages').append(`<li>${name}: ${msg}</li>`);
fn(msg);
};
}
$(document).ready(() => {
window.console.log = wrapper(console.log, "Log");
window.console.warn = wrapper(console.warn, "Warn");
window.console.error = wrapper(console.error, "Error");
});
I do not recommend you modify the original function, you can create a new one, and that can show the message on both console and your node.
Blow is pure javascript code.
Example 1
function decorator(wrap, func) {
return (...para) => {
func(para)
return wrap(para)
}
}
const mylog = decorator(window.console.log, (...para)=>{
const ul = document.querySelector(`ul[class=msg]`)
const range = document.createRange()
const frag = range.createContextualFragment(`<li>${para}</li>`)
ul.append(frag)
})
mylog("Hello world!")
<h3>messages</h3>
<ul class="msg"></ul>
Example 2
window.onload = () => {
const decoratorFunc = (methodName, msg) => {
const symbol = methodName === "info" ? "🐬" :
methodName === "error" ? "❌" :
methodName === "warn" ? "⚠" : ""
const ul = document.querySelector(`ul[class=msg]`)
const range = document.createRange()
const frag = range.createContextualFragment(`<li>${symbol} ${msg}</li>`)
ul.append(frag)
}
const myConsole = new MethodDecorator(window.console, decoratorFunc)
myConsole.Apply(["log", "info", "error", "warn", // those will run {decoratorFunc + ``window.console.xxx``}
"others" // 👈 I created, it doesn't belong method of window.console so it only run ``decoratorFunc``
])
myConsole.log("log test")
myConsole.info("info test")
console.info("not influenced") // not influenced
myConsole.error("error test")
myConsole.warn("warn test")
myConsole.others("others test")
myConsole.Reset()
// myConsole.warn("warn test") // error
}
class MethodDecorator {
constructor(obj, func) {
this.obj = obj
this.func = func
}
Apply(nameArray) {
const orgMethodNameArray = Object.getOwnPropertyNames(this.obj)
for (const methodName of nameArray) {
const orgMethodName = orgMethodNameArray.find(e => e === methodName) // if not found return undefined
const prop = {}
prop[methodName] = {
"configurable": true,
"value": (...args) => {
this.func(methodName, args) // decorator function
if (orgMethodName) {
this.obj[orgMethodName](args) // for example, console.log(args)
}
}
}
Object.defineProperties(this, prop)
}
}
Reset() {
const extraMethodNameArray = Object.getOwnPropertyNames(this).filter(name => name !== "obj" || name !== "func")
for (const extraMethodName of extraMethodNameArray) {
const prop = {}
prop[extraMethodName] = {
value: undefined
}
Object.defineProperties(this, prop)
}
}
}
<h3>messages</h3>
<ul class="msg"></ul>
👆 The console of Run code snippet will block some contents. Click the full page after you click run code snippet to see all contents.
Reference
window.console
Related
I have a very simple html page where I put this script at the end:
<?php echo $this->Html->script(['studiomain.js']); ?>
</html>
The script contains an IIF in JS:
window.studiomain = window.studiomain || (function ($) {
let _dataTable = '';
let _modalTemplates = {};
let _webroot = 'studioasq';
function setDataTable (t, options={}) {
_dataTable = $(t);
if (typeof $(t).DataTable == 'function') {
options.language = {
"url": "/" + _webroot + "/js/datatable/i18n/Italian.json"
}
$(t).DataTable(options);
}
}
function setModal(key='',template='') {
_modalTemplates[key] = template;
}
function renderModal(key,data={}) {
if (_modalTemplates[key] !== undefined) {
let copy = _modalTemplates[key];
Object.keys(data).forEach((key) => {
copy.replace(new RegExp("{{" + value + "}}","g"),data[key]);
})
}
return $('#'+key);
}
return {
setDataTable,
setModal,
renderModal
}
})($);
But when the page finishes loading, I have no studiomain in window:
window.studiomain => undefined.
I think the problem is the renderModal function: If I delete it all is fine.
What am I missing?
**** UPDATE ****
Following suggestions, I think the problem is in the order of loading scripts and passing the reference to JQuery.
I discovered also that passing (jQuery) and NOT ($) to the IIF works.
I guess you are trying to achieve modular pattern.
In your code, you'll need to return every thing inside a function, otherwise every code without return will be in private state.
Fix of your code, you need to return window.studiomain as a parameter, you code will work, $ is not defined therefore it's not storing inside window object
window.studiomain = window.studiomain || (function($) {
let _dataTable = '';
let _modalTemplates = {};
let _webroot = 'studioasq';
function setDataTable(t, options = {}) {
_dataTable = $(t);
if (typeof $(t).DataTable == 'function') {
options.language = {
"url": "/" + _webroot + "/js/datatable/i18n/Italian.json"
}
$(t).DataTable(options);
}
}
function setModal(key = '', template = '') {
_modalTemplates[key] = template;
}
function renderModal(key, data = {}) {
if (_modalTemplates[key] !== undefined) {
let copy = _modalTemplates[key];
Object.keys(data).forEach((key) => {
copy.replace(new RegExp("{{" + value + "}}", "g"), data[key]);
})
}
return $('#' + key);
}
return {
setDataTable,
setModal,
renderModal
}
})(window.studiomain);
console.log(studiomain);
I checked my chrome network tab, when inspecting, and found this script, that looked a bit fishy. It's called inject.js and has this code. I don't have any extensions, called something like this, so I'm worried it might be a malware script?
(() => {
try {
const detectJs = (chain) => {
const properties = chain.split('.');
let value = properties.length ? window : null;
for (let i = 0; i < properties.length; i++) {
const property = properties[i];
if (value && value.hasOwnProperty(property)) {
value = value[property];
} else {
value = null;
break;
}
}
return typeof value === 'string' || typeof value === 'number' ? value : !!value;
};
const onMessage = (event) => {
if (event.data.id !== 'patterns') {
return;
}
removeEventListener('message', onMessage);
const patterns = event.data.patterns || {};
const js = {};
for (const appName in patterns) {
if (patterns.hasOwnProperty(appName)) {
js[appName] = {};
for (const chain in patterns[appName]) {
if (patterns[appName].hasOwnProperty(chain)) {
js[appName][chain] = {};
for (const index in patterns[appName][chain]) {
const value = detectJs(chain);
if (value && patterns[appName][chain].hasOwnProperty(index)) {
js[appName][chain][index] = value;
}
}
}
}
}
}
postMessage({ id: 'js', js }, '*');
};
addEventListener('message', onMessage);
} catch (e) {
// Fail quietly
}
})();
Is this some sort of malware?
I saw the same code and determined it was part of Wappalyzer by using the Developer Tools in Chrome, clicking on the Sources tab and searching for inject.js on the Page sub-tab.
I have the following code:
var tableRequiredList = [];
var requireListPath = [
'./slimShady.js',
'./chickaChicka.js'
];
var getRequires = function() {
for (var i = 0; i < requireListPath.length; i++) {
((requireNamePath) => {
try {
console.log("INSIDE LOOP RESULT", i, require(requireNamePath)().getName()); // Outputs correct result for the index ("ChickaChicka")
tableRequiredList.push({ "name": requireNamePath, "theReq": require(requireNamePath)() });
// tableRequiredList.push({ "name": requireNamePath, "theReq": ((thePath) => { return require(thePath)(); })(requireNamePath) }); // This also doesn't seem to work.
} catch(err) {
console.log("Error importing: ", requireNamePath, " Error reported: ", err);
}
})(requireListPath[i]);
};
console.log("NAME", tableRequiredList[0].name); // Outputs the correct result ("slimShady.js")
console.log("FUNC NAME", tableRequiredList[0].theReq.getName()); // Always outputs the last item in requireListPath list ("ChickaChicka")
};
getRequires();
Example Module 1 - slimShady.js
((module) => {
module.exports = {};
var exampleModuleName1 = function() {
this.getName = function() {
return 'myNameIsSlimShady';
};
return this;
};
module.exports = exampleModuleName1;
})(module);
Example Module 2 - chickaChicka.js
((module) => {
module.exports = {};
var exampleModuleName2 = function() {
this.getName = function() {
return 'ChickaChicka';
};
return this;
};
module.exports = exampleModuleName2;
})(module);
Why does it output:
INSIDE LOOP RESULT 0 myNameIsSlimShady
INSIDE LOOP RESULT 1 ChickaChicka
NAME ./slimShady.js
FUNC NAME ChickaChicka
When it should be outputting the first index of the tableRequiredList array? This seems to only happen with require(). I have tried using map and forEach, along with the closure example above. All have the same results.
Thanks to #liliscent, I figured it out.
Just needed to change modules to this:
((module) => {
module.exports = {};
var exampleModuleName2 = function() {
var retr = {};
retr.getName = function() {
return 'ChickaChicka';
};
return retr;
};
module.exports = exampleModuleName2;
})(module);
Heyo,
I have a following function
async function fnIsOnScreenOnce(img, desc,iCounter,client,repeatDelay=0) {
await timeout(repeatDelay);
let screenshot= await client.screenshot()
let buf = new Buffer(screenshot.value, 'base64');
let img1 = cv.imdecode(buf)
let result = img1.matchTemplate(img, 5).minMaxLoc();
result.screenshot=img1;
if (result.maxVal <= 0.65) {
// Fail
const msg = "Can't see object yet";
throw new Error(result);
}
// All good
console.log("result:"+result)
logger.info("Found image on screen: "+desc);
return result;
}
Call of the function
function fnIsOnScreen(img,client, repeats = 5, desc, wait = 2000,repeatDelay) {
logger.info("Looking for image on screen:" +desc +" with " + repeats + " repeats ");
let iCounter = 0;
let init = ()=> timeout(wait).then((asd)=>{
const attempt = () => fnIsOnScreenOnce(img, desc, iCounter,client,repeatDelay).then((data=>{
let imagepath=fnMarkOnImage(data.screenshot,img,data,outputDir)
let description={};
description.action="Is image on screen ?";
description.desc=desc;
description.repeats=repeats;
description.wait=wait;
description.img=imagepath;
description.message="is this correct element ? if is then it was found correctly";
fnPushToOutputArray(description)
return data;
})).catch(err => {
console.log(JSON.stringify(err));
console.log(err);
console.log(err.result);
iCounter++;
if (iCounter === repeats) {
// Failed, out of retries
logger.info("Object not found : " + desc);
return Promise.reject("Object not found : " + desc);
}
// Retry after waiting
return attempt();
});
return attempt();
})
return init();
}
result object contains some date.
On error result contains {} object with no values in it. I would need to get all the values. So how can i pass result object through throw new error to retrieve it in catch ?
One way to return extra data with error is to extend Error class and add them your self
class MyError extends Error {
constructor(message, errorExtraParams) {
super(message);
this._errorExtraParams = errorExtraParams;
}
get errorExtraParams() {
return this._errorExtraParams;
}
}
throw new MyError("Error!!!", {})
//or
let mError = new MyError("Error!!!", {})
console.log(mError.errorExtraParams)
But I suggest you don't use throw Error, because I don't like to throw Errors for insignificant reasons. What I mean is that in your case there is no reason to throw error cause there is no error and no reason to create an error just to tell you code "Hey I didnt find the image" instead just return false.
async function fnIsOnScreenOnce(img, desc, iCounter, client, repeatDelay = 0) {
await timeout(repeatDelay);
let screenshot = await client.screenshot()
let buf = new Buffer(screenshot.value, 'base64');
let img1 = cv.imdecode(buf)
let result = img1.matchTemplate(img, 5).minMaxLoc();
result.screenshot = img1;
if (result.maxVal <= 0.65) {
const msg = "Can't see object yet";
return false;
}
// All good
console.log("result:" + result)
logger.info("Found image on screen: " + desc);
return result;
}
function fnIsOnScreen(img, client, repeats = 5, desc, wait = 2000, repeatDelay) {
logger.info("Looking for image on screen:" + desc + " with " + repeats + " repeats ");
let iCounter = 0;
let init = () => timeout(wait).then((asd) => {
let found = false;
do {
let found = await fnIsOnScreenOnce(img, desc, iCounter, client, repeatDelay)
} while (found !== false && iCounter++ < 10)
let imagepath = fnMarkOnImage(found.screenshot, img, found, outputDir)
let description = {};
description.action = "Is image on screen ?";
description.desc = desc;
description.repeats = repeats;
description.wait = wait;
description.img = imagepath;
description.message = "is this correct element ? if is then it was found correctly";
fnPushToOutputArray(description)
return found;
})
return init();
}
You should pass a String to the Error Object, so if you want to exchange an object you could use JSON.stringify() like this:
try {
throw new Error(JSON.stringify({result:"Hello, World"}));
}
catch(error) {
console.log(JSON.parse(error.message))
}
As you can see, this is how you would send data from a try to a catch through throwing errors. You can ofc make the second part in the catch way shorter:
error = JSON.parse(error.message);
You can try an approach like this
try {
const err = new Error("My Error Occurred");
err.extra ='Extra details';
throw err;
}
catch (error) {
console.log(error.extra)
}
As Error itself is an Object ,We can make use of this to pass extra variables of our choice
I am still pretty new to this, so forgive me if I dont' say this correctly. We have an array.reduce that calls a method with a returning promise that iterates through a list of files and post results to the db. Everything was working great, until it ran into a field that had an apostrophe in it and then the db insert fails. This is the field value. 'Expected 100002822' to be 100002822.'
I tried adding a replaceAll on the field and now get an error in the array.reduce.
Here is the .reduce
console.log('Found test results in ' + matches.length + ' files. Parsing and posting to the database now...');
var startTime = moment();
var parser = new Parser();
matches.reduce(function (p, val) {
return p.then(function () {
return parser.parseResults(val);
});
}, Promise.resolve()).then(function (finalResult) {
var endTime = moment();
var testDuration = moment.duration(endTime.diff(startTime));
console.log(chalk.blue('*** File parsing time: ' + testDuration.humanize() + ' ***'));
if (finalResult.insertSuccess == matches.length) {
var publishOut = {
totalFiles: matches.length,
totalTests: 0,
totalTestsSuccess: 0,
totalTestsFailed: 0
}
publishOut.totalTests += finalResult.totalTests;
publishOut.totalTestsSuccess += finalResult.testPassedCount;
publishOut.totalTestsFailed += finalResult.testFailedCount;
console.log(`Successfully inserted ${finalResult.insertSuccess} of ${publishOut.totalTests} test results.`);
// for (var i = 0; i < matches.length; i++) {
// var currentFile = `./testing/results/${matches[i]}`;
// fs.unlinkSync(currentFile);
// }
resolve(publishOut);
} else {
reject('Only ' + finalResult.insertSuccess + ' of ' + matches.length + ' successfully posted to the database');
}
}, function (err) {
reject('error in reduce', err);
});
I have tried several different ways of using the replaceAll with the same failure. It hits this code from the array.reduce
}, function (err) {
reject('error in reduce', err);
});
And this is the called method. The added code causing the failure in the .reduce is this Message = expectation.message.replaceAll("'", "");
protractorParser.prototype.parseResults = function (fileName) {
return new Promise((resolve, reject) => {
//console.log('In parseresults', fileName);
var currentFile = './testing/results/' + fileName
json.readFile(currentFile, function (err, obj) {
if (err != null) {
console.log('error reading file', err);
reject(err);
} else {
resolve(obj);
}
});
}).then(function (obj) {
var results = [];
for (var suite in obj) {
var specs = obj[suite].specs;
for (let i = 0; i < specs.length; i++) {
const assert = specs[i];
const tcR = /TC[\d]+/;
const tc = assert.description.match(tcR);
let Passed = 1;
let Message = '';
let Stack = '';
testResults.totalTests++;
if (assert.failedExpectations.length) {
const expectation = assert.failedExpectations[assert.failedExpectations.length - 1];
Passed = 0;
Message = expectation.message.replaceAll("'", "");
Stack = expectation.stack.split('\n')[1].trim();
testResults.testFailedCount++
} else {
testResults.testPassedCount++
}
if (tc != null) {
const time = moment().utcOffset(config.get('settings.timeOffset')).format('YYYY-MM-DDTHH:mm:ss');
const promise = utility.TestDataManager.insertAutomationResults(tc[0], assert.description, Passed, process.env.testBuild, 'P', Message, Stack, 0, time, '');
results.push(promise.then(() => {
//fs.unlinkSync(currentFile);
testResults.insertSuccess++;
//console.log('insertSuccess', testResults.insertSuccess);
},
err => { console.log('… failed', err); throw err; }
));
} else {
console.log('no test case found for test: ' + assert.description + ' -- skipping');
// I don't think you want to `throw err` here, right?
}
}
}
return Promise.all(results).then(() => testResults);
});
};