I'm using protractor for automation testing, at this time all tests work properly.
But if I set test running parallel
add to config file:
shardTestFiles: true,
maxInstances: 4,
tests will start randomly fail throwing 'no element found' and/or 'failed expectation' exceptions.
Could someone suggest what can be cause of this problem or how to fix it?
Could be a cause that test fails at the moment when start running new browser instance and Protractor focusing on it?
(At this moment i have about 20 spec files and each spec file running starts new browser instance)
environment:
Windows 7 x64
Protractor v2.1.0
Browser Chrome v46
to solve my issue I've created a wrapper of elementFinder and ElementArrayFinder objects with overridden methods and some additional waiting for visibility and/or for presence.
Example for elementFinder.getText() and elementFinder.click()
function ElementFinderWrapper() {
var conditions = protractor.ExpectedConditions;
/**
* Returns a wrapper for ElementFinder element.
* #param {webdriver.Locator} locator
*/
this.get = function(locator){
return new Control(element(locator));
};
/**
* Creates a wrapper for ElementFinder element.
* #param {ElementFinder} element_finder
* #constructor
*/
function Control(element_finder) {
/**
* Returns the visible innerText of this element.
* #returns {!webdriver.promise.Promise.<string>}
*/
this.getText = function () {
return browser.wait(conditions.presenceOf(element_finder), 3000);
.then(function () {
return browser.wait(conditions.visibilityOf(element_finder), 3000);
})
.then(function () {
return element_finder.getText();
});
};
/**
* Clicks on visible element.
* #returns {!webdriver.promise.Promise.<void>}
*/
this.click = function () {
return browser.wait(conditions.presenceOf(element_finder), 3000);
.then(function () {
return browser.wait(conditions.visibilityOf(element_finder), 3000);
})
.then(function () {
return element_finder.click();
});
};
};
};
then use something like:
contol = ElementFinderWrapper.get(by.xpath('some path'));
control.getText();
control.click();
Related
I am working on a small chat app backend in NodeJS.
I am trying to make a plugin system for it, Is there some way that I can end the script any time I want.
My code
pluginLoadder.js
/**
* Starts to load plugins
* #param {function} cb Runs after all plugins are loaded
*/
function startPlugins(cb) {
fs.readdir(pluginPath, function(err, files) {
if (err !== null) {
console.error(err);
} else {
const pluginFileREGEXP = /.+\.js$/;
files = files.filter(function(value) {
return pluginFileREGEXP.test(value);
});
files.forEach(function(val) {
try {
const tempPlugin = require(pluginPath +'/'+ val );
const func = {
events: events,
log: log,
};
tempPlugin.main(func);
events.emit('test');
} catch (e) {
if (e instanceof TypeError) {
log(`ERROR: ${val} is not a talker-backend plugin\n
E: ${e}`, 'error');
} else {
log(e, 'error');
}
}
});
cb();
}
});
}
./plugins/spamer.js
//* This is a plugin thats in the directory ./plugins
/* I want to stop this script through pluginLoader.js
*/
function main(func) {
const {events, log} = func;
setInterval(function (){
log('test', 'event');
}, 1000)
}
module.exports = {
main: main
};
In most cases you could use process.exit which takes in an exit code as an integer parameter (Node.js interprets non-zero codes as failure, and an exit code of 0 as success).
Then if you want to specifically target a process (assuming you are running multiple ones) you could use process.kill which takes process pid as a parameter.
Finally you could use process.abort which immediately kills Node.js without any callback executed and with a possibility to generate a core file.
To sum up:
// You would mostly use
process.exit(exitCode); // where exitCode is an integer
// or
process.kill(processPid);
// or
process.abort();
Just call it anytime you need to exit your program in your code.
I am trying to upload a Map/reduce type script to netsuite following a suitescript 2.0 training guide. I am receiving the following error: "SuiteScript 2.0 entry point scripts must implement one script type function."
I'm using the getInputData() and map() functions. Returning a reference object pointing to a saved search. Then extracting and logging the context value and the parsed context value (comparing json strings and js objects in the lesson).
Anyone see a syntax error, know what I might be missing, or what I can test for?
Code:
/**
* #NApiVersion 2.x
* #NScriptType MapReduceScript
*/
define(['N/search']),
function(search) {
function getInputData() {
return { type: 'search', id: 'customsearch_iii_payments' };
}
function map(context) {
var jsonResult = context.value
var searchResult = JSON.parse(context.value);
log.debug('JSON result' + jsonResult);
log.debug('Search Result' + searchResult);
}
return {
getInputData: getInputData,
map: map
}
}
It was a netsuite specific syntax error my linter didn't catch. My script definition wasn't wrapping the entire script, just the module declarations.
Working Code:
/**
* #NApiVersion 2.x
* #NScriptType MapReduceScript
* #NModuleScope SameAccount
*/
define(['N/search'],
function(search) {
function getInputData() {
return { type: 'search', id: 'customsearch_iii_payments' };
}
function map(context) {
var jsonResult = context.value
var searchResult = JSON.parse(context.value);
log.debug('JSON result' + jsonResult);
log.debug('Search Result' + searchResult);
}
return {
getInputData: getInputData,
map: map
}
});
Also check the #NScriptType notation, in case you have ScheduleScript, netsuite will expect you to have a function called ¨execute¨ on the return object no matter if the syntax is correct.
I found the issue for me was that my script referenced local files which I hadn't yet uploaded.
Upload other local files before creating a script record.
double check for the require vs define keyword in the main method definition. 2.X ScheduledScript use define
Using selenium-webdriver (api docs here), how can you wait for an element to be visible?
I have the following functions, part of a home-made set of testing helpers, and the first one works but the second one fails (eg. it times out wating for and element to be visible even if it exists - as confirmed by the first function that works - and is visible - as confirmed by all imaginable tests and inspections of the page html/css/js).
Here they are:
/**
* Wait for an element to exist
*
* #param {object} locator
* #param {int} timeout (ms)
*
* #return {Promise<element>}
*/
// !! THIS WORKS OK
exports.waitForElement = function (locator, timeout) {
var waitTimeout = timeout || DEFAULT_TIMEOUT;
return this.wait(until.elementLocated(locator), waitTimeout)
.then(() => {
return this.findElement(locator);
});
};
/**
* Wait for an element to exist and then wait for it to be visible
*
* IMPORTANT: this is probable what you want to use instead of
* waitForVisibleElement most of the time.
*
* #param {hash} locator
* #param {number} timeout
*
* #return {Promise<element>}
*/
// !! THIS FAILS TO WORK AS EXPECTED
exports.waitForVisibleElement = function (locator, timeout) {
var waitTimeout = timeout || DEFAULT_TIMEOUT;
return this.waitForElement(locator, waitTimeout)
.then(el => {
console.log('--- element found:', el);
return this.wait(until.elementIsVisible(el), waitTimeout)
.then(() => {
console.log('--- element visible!');
// this is to make sure we are returning the same kind of
// promise as waitForElement
return this.findElement(locator);
});
});
};
...I tested in multiple contexts, so it's no other cause of the problem then the code inside waitForVisibleElement but I can't seem to find any reason for why it does not work!
As clarification, this for that code ends up being the webdriver instance (the result of new webdriver.Builder().withCapabilities(webdriver.Capabilities.chrome()).build()) after an augment method monkeypatches a given webdriver object... probably a questionable design pattern, but no the cause for my problem here :)
UPDATE: Apparently this only happens for XPath locators, like { xpath: '//*[contains(text(), "first name")]' }... not that it makes any more sense now. Also, it's the same for Firefox, so it's not a weird chrome-webdriver thingy...
It most likely is a Promise issue.
Try this instead:
exports.waitForElement = function (locator, timeout) {
var timeout = timeout || DEFAULT_TIMEOUT;
return this.wait(until.elementLocated(locator), timeout);
};
exports.waitForVisibleElement = function (locator, timeout) {
var timeout = timeout || DEFAULT_TIMEOUT;
var element = this.wait(until.elementLocated(locator), timeout);
return this.wait(new until.WebElementCondition('for element to be visible ' + locator, function() {
return element.isDisplayed().then(v => v ? element : null);
}), timeout);
};
Usage:
driver.get("...");
driver.waitForElement(By.id("..."), 2000).getText().then(function(text){
console.log(text);
});
driver.waitForVisibleElement(By.id("..."), 2000).getText().then(function(text){
console.log(text);
});
I am writing a package as part of a small application I am working on and one thing I need to do is fetch json data from an endpoint and populate it to a Server side collection.
I have been receiving error messages telling me I need to put by server side collection update function into a Fiber, or Meteor.bindEnvironment, or Meteor._callAsync.
I am puzzled, because there are no clear and concise explanations telling me what these do exactly, what they are, if and when they are being deprecated or whether or not their use is good practice.
Here is a look at what is important inside my package file
api.addFiles([
'_src/collections.js'
], 'server');
A bit of psuedo code:
1) Set up a list of Mongo.Collection items
2) Populate these using a function I have written called httpFetch() and run this for each collection, returning a resolved promise if the fetch was successful.
3) Call this httpFetch function inside an underscore each() loop, going through each collection I have, fetching the json data, and attempting to insert it to the Server side Mongo DB.
Collections.js looks like what is below. Wrapping the insert function in a Fiber seems to repress the error message but no data is being inserted to the DB.
/**
* Server side component makes requests to a remote
* endpoint to populate server side Mongo Collections.
*
* #class Server
* #static
*/
Server = {
Fiber: Npm.require('fibers'),
/**
* Collections to be populated with content
*
* #property Collections
* #type {Object}
*/
Collections: {
staticContent: new Mongo.Collection('staticContent'),
pages: new Mongo.Collection('pages'),
projects: new Mongo.Collection('projects'),
categories: new Mongo.Collection('categories'),
formations: new Mongo.Collection('formations')
},
/**
* Server side base url for making HTTP calls
*
* #property baseURL
* #type {String}
*/
baseURL: 'http://localhost:3000',
/**
* Function to update all server side collections
*
* #method updateCollections()
* #return {Object} - a resolved or rejected promise
*/
updateCollections: function() {
var deferred = Q.defer(),
self = this,
url = '',
collectionsUpdated = 0;
_.each(self.Collections, function(collection) {
// collection.remove();
url = self.baseURL + '/content/' + collection._name + '.json';
self.httpFetch(url).then(function(result) {
jsonData = EJSON.parse(result.data);
_.each(jsonData.items, function(item) {
console.log('inserting item with id ', item.id);
self.Fiber(function() {
collection.update({testID: "Some random data"}
});
});
deferred.resolve({
status: 'ok',
message: 'Collection updated from url: ' + url
});
}).fail(function(error) {
return deferred.reject({
status: 'error',
message: 'Could not update collection: ' + collection._name,
data: error
});
});
});
return deferred.promise;
},
/**
* Function to load an endpoint from a given url
*
* #method httpFetch()
* #param {String} url
* #return {Object} - A resolved promise if the data was
* received or a rejected promise.
*/
httpFetch: function(url) {
var deferred = Q.defer();
HTTP.call(
'GET',
url,
function(error, result) {
if(error) {
deferred.reject({
status: 'error',
data: error
});
}
else {
deferred.resolve({
status: 'ok',
data: result.content
});
}
}
);
return deferred.promise;
}
};
I am still really stuck on this problem, and from what I have tried before from reading other posts, I still can't seem to figure out the 'best practice' way of getting this working, or getting it working at all.
There are plenty of suggestions from 2011/2012 but I would be reluctant to use them, since Meteor is in constant flux and even a minor update can break quite a lot of things.
Thanks
Good news : the solution is actually much simpler than all the code you've written so far.
From what I've grasped, you wrote an httpFetch function which is using the asynchronous version of HTTP.get decorated with promises. Then you are trying to run your collection update in a new Fiber because async HTTP.get called introduced a callback continued by the use of promise then.
What you need to do in the first place is using the SYNCHRONOUS version of HTTP.get which is available on the server, this will allow you to write this type of code :
updateCollections:function(){
// we are inside a Meteor.method so this code is running inside its own Fiber
_.each(self.Collections, function(collection) {
var url=// whatever
// sync HTTP.get : we get the result right away (from a
// code writing perspective)
var result=HTTP.get(url);
// we got our result and we are still in the method Fiber : we can now
// safely call collection.update without the need to worry about Fiber stuff
});
You should read carefully the docs about the HTTP module : http://docs.meteor.com/#http_call
I now have this working. It appears the problem was with my httpFetch function returning a promise, which was giving rise to the error:
"Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment."
I changed this httpFetch function to run a callback when HTTP.get() had called with success or error.
Inside this callback is the code to parse the fetched data and insert it into my collections, and this is the crucial part that is now working.
Below is the amended Collections.js file with comments to explain everything.
Server = {
/**
* Collections to be populated with content
*
* #property Collections
* #type {Object}
*/
Collections: {
staticContent: new Mongo.Collection('staticContent'),
pages: new Mongo.Collection('pages'),
projects: new Mongo.Collection('projects'),
categories: new Mongo.Collection('categories'),
formations: new Mongo.Collection('formations')
},
/**
* Server side base url for making HTTP calls
*
* #property baseURL
* #type {String}
*/
baseURL: 'http://localhost:3000',
/**
* Function to update all server side collections
*
* #method updateCollections()
* #return {Object} - a resolved or rejected promise
*/
updateCollections: function() {
var deferred = Q.defer(),
self = this,
collectionsUpdated = 0;
/**
* Loop through each collection, fetching its data from the json
* endpoint.
*/
_.each(self.Collections, function(collection) {
/**
* Clear out old collection data
*/
collection.remove({});
/**
* URL endpoint containing json data. Note the name of the collection
* is also the name of the json file. They need to match.
*/
var url = self.baseURL + '/content/' + collection._name + '.json';
/**
* Make Meteor HTTP Get using the function below.
*/
self.httpFetch(url, function(err, res) {
if(err) {
/**
* Reject promise if there was an error
*/
deferred.reject({
status: 'error',
message: 'Error fetching content for url ' + url,
data: err
});
}
else {
/**
* Populate fetched data from json endpoint
*/
var jsonData = res.content;
data = EJSON.parse(res.content);
/**
* Pick out and insert each item into its collection
*/
_.each(data.items, function(item) {
collection.insert(item);
});
collectionsUpdated++;
}
if(collectionsUpdated === _.size(self.Collections)) {
/**
* When we have updated all collections, resovle the promise
*/
deferred.resolve({
status: 'ok',
message: 'All collections updated',
data: {
collections: self.Collections,
count: collectionsUpdated
}
});
}
});
});
/**
* Return the promise
*/
return deferred.promise;
},
/**
* Function to load an endpoint from a given url
*
* #method httpFetch()
* #param {String} url
* #param {Function} cb - Callback in the event of an error
* #return undefined
*/
httpFetch: function(url, cb) {
var res = HTTP.get(
url,
function(error, result) {
cb(error, result);
}
);
}
};
I'm adding a bug report form to my project. When the user hits the send button on the form (after they explain what the bug is) I am getting information of their browser automatically. I'm currently able to get their user-agent and the source code of the page, but I think it would be super useful if I could also get any errors that have been sent to the browser console.
I've googled for stuff like "javascript get console.log content" but haven't really found anything useful.
I read about creating a "wrapper" for window.log, and found this code:
window.log = function(){
log.history = log.history || []; // store logs to an array for reference
log.history.push(arguments);
if(this.console){
console.log( Array.prototype.slice.call(arguments) );
}
};
But it doesn't seem to be getting the errors that the browser (chrome) sends to the console.log.
Does anyone know how I can get ALL of the errors in the console.log?
This seemed like an interesting idea. What I came up with is essentially a small JavaScript class that overrides the console's functions (but allows the default behavior - you can still see the information in Google Chrome's Inspector, for example).
It is pretty simple to use. Save this as 'consolelogger.js':
/**
* ConsoleLogger
*
* Tracks the history of the console.
* #author Johnathon Koster
* #version 1.0.0
*/
var ConsoleLogger = function() {
// Holds an instance of the current object.
var _instance = this;
this._logOverwrite = function(o) {
var _log = o.log;
// Overwrites the console.log function.
o.log = function(e) {
_instance.pushLog(e);
// Calls the console.log function (normal browser behavior)
_log.call(o, e);
}
// Overwrites the console.info function.
o.info = function(e) {
_instance.pushInfoLog(e);
// Calls the console.info function (normal browser behavior)
_log.call(o, e);
}
// Overwrites the console.warn function.
o.warn = function(e) {
_instance.pushWarnLog(e);
// Calls the console.warn function (normal browser behavior)
_log.call(o, e);
}
// Overwrites the console.error function.
o.error = function(e) {
_instance.pushErrorLog(e);
// Calls the console.error function (normal browser behavior)
_log.call(o, e);
}
}(console);
// Holds the history of the console calls made by other scripts.
this._logHistory = [];
this._infoHistory = [];
this._warnHistory = [];
this._errorHistory = [];
this._windowErrors = [];
/**
* This allows users to get the history of items not explicitly added.
*/
window.onerror = function(msg, url, line) {
_instance._windowErrors.push('Message: ' + msg + ' URL: ' + url + ' Line: ' + line);
}
/**
* Adds an item to the log history.
*
* #param {log} object to log
*/
this.pushLog = function(log) {
this._logHistory.push(log);
}
/**
* Adds an item to the information log history.
*
* #param {log} object to log
*/
this.pushInfoLog = function(log) {
this._infoHistory.push(log);
}
/**
* Adds an item to the warning log history.
*
* #param {log} object to log
*/
this.pushWarnLog = function(log) {
this._warnHistory.push(log);
}
/**
* Adds an item to the error log history.
*
* #param {log} object to log
*/
this.pushErrorLog = function(log) {
this._errorHistory.push(log);
}
/**
* Returns the log history.
* #this {ConsoleLogger}
* #return {array} the log history.
*/
this.getLog = function() {
return this._logHistory;
}
/**
* Returns the information log history.
* #this {ConsoleLogger}
* #return {array} the information log history.
*/
this.getInfoLog = function() {
return this._infoHistory;
}
/**
* Returns the warning log history.
* #this {ConsoleLogger}
* #return {array} the warning log history.
*/
this.getWarnLog = function() {
return this._warnHistory;
}
/**
* Returns the error log history.
* #this {ConsoleLogger}
* #return {array} the error log history.
*/
this.getErrorLog = function() {
return this._errorHistory;
}
/**
* Returns the window log history.
* #this {ConsoleLogger}
* #return {array} the window log history.
*/
this.getWindowLog = function() {
return this._windowErrors;
}
/**
* Returns all log histories.
* #this {ConsoleLogger}
* #return {array} the error log(s) history.
*/
this.getLogHistory = function() {
var _return = [];
_return = this._logHistory
_return = _return.concat(this._infoHistory);
_return = _return.concat(this._warnHistory);
_return = _return.concat(this._errorHistory);
_return = _return.concat(this._windowErrors);
return _return;
}
}
And add it to your page like this:
<script src="consolelogger.js"></script>
<script>
// Create a new instance of ConsoleLogger
var logger = new ConsoleLogger;
</script>
Now, you don't have to do anything special to trap 'console.log', 'console.warn', 'console.info', or 'console.error'. The ConsoleLogger will do it for you, and allow you to get the history of what's been added.
To retrieve the history call these functions (all of them return a JavaScript array):
var logHistory = logger.getLog(); // Get the console.log history
var infoHistory = logger.getInfoLog(); // Get the console.info history
var warningHistory = logger.getWarnLog(); // Get the console.warn history
var errorHistory = logger.getErrorLog(); // Get the console.error history
var windowLog = logger.getWindowLog(); // Get the window error history
var allLogs = logger.getLogHistory(); // Returns all log histories as one array.
I apologize for such a lengthy post, but it seems to do the trick! I also created a GitHub repo; if I do any more work on it, changes will be committed there.