I am new to Protractor and I am trying to write a function that will accept 2 values and has to return the element like this
this.getinput = function(x, y) {
var text123 = "by." + x + "('" + y + "')"
return element(text123);
So x and y here can be any values like
x: "buttonText" y:"save"
x: "class" y: ".css"
This works
element(by.buttonText('save'))
but if I try to send the same through the variables as in the function, it fails as "Invalid element locator". Is there a nicer way to get this done dynamically?
Setup a object to mapping by API as following:
var byMap = {
buttontext: by.buttonText,
css: by.css,
xpath: by.xpath,
id: by.id,
tagname: by.tagName,
name: by.name,
linktext: by.linkText,
};
function buildLocator(using, value) {
return byMap[using.toLowerCase()](value);
}
function getInput(using, value) {
return element(buildLocator(using, value));
}
Related
When using a model as a source to an entity, say gltf, is there a way we know the original size? Since the scale attribute works on relative size, it seems to be a trial an error to fit the model to our desired size. I tried using the geometry.getComputingBox() of the mesh of the model but it returns null. Wondering if there is a component that is available that lets us specify the scale in absolute terms.
Ah, figured it out.
var model = this.el.object3D;
var box = new THREE.Box3().setFromObject( model );
var size = box.getSize();
gives you the size. then using the above any desired size can be set.
Created a simple component that can be conveniently used
AFRAME.registerComponent('resize', {
schema: {
axis: {
type: 'string',
default: 'x'
},
value: {
type: 'number',
default: 1
}
},
init: function() {
var el = this.el;
var data = this.data;
var model = el.object3D;
el.addEventListener('model-loaded', function(e) {
var box = new THREE.Box3().setFromObject( model );
var size = box.getSize();
var x = size.x;
var y = size.y;
var z = size.z;
if(data.axis === 'x') {
var scale = data.value / x;
}
else if(data.axis === 'y') {
var scale = data.value / y;
}
else {
var scale = data.value / z;
}
el.setAttribute('scale', scale + ' ' + scale + ' ' + scale);
});
}
});
And it can be used as to proportionately resize the model with x axis length as 0.5
<a-entity resize='axis:x; value:0.5' gltf-model='#model`></a-entity>
(This would have come as a comment but as I don't have enough rep points this is coming as an answer.)
I found that the model doesn't have a size directly after the model-loaded event listener so I trigger the rescale from the update method. Funnily enough though if you don't have the model-loaded event listener then the size of the model will be 0 even after the first update is fired.
This is my variant of the above code with the difference being that the dimension is set in meters:
/**
* Scales the object proportionally to a set value given in meters.
*/
AFRAME.registerComponent("natural-size", {
schema: {
width: {
type: "number",
default: undefined, // meters
},
height: {
type: "number",
default: undefined, // meters
},
depth: {
type: "number",
default: undefined, // meters
},
},
init() {
this.el.addEventListener("model-loaded", this.rescale.bind(this));
},
update() {
this.rescale();
},
rescale() {
const el = this.el;
const data = this.data;
const model = el.object3D;
const box = new THREE.Box3().setFromObject(model);
const size = box.getSize();
if (!size.x && !size.y && !size.z) {
return;
}
let scale = 1;
if (data.width) {
scale = data.width / size.x;
} else if (data.height) {
scale = data.height(size.y);
} else if (data.depth) {
scale = data.depth / size.y;
}
el.setAttribute("scale", `${scale} ${scale} ${scale}`);
},
remove() {
this.el.removeEventListener("model-loaded", this.rescale);
},
});
Then:
<a-entity natural-size='width:0.72' gltf-model='#model`></a-entity>
box.getSize has changed, I combined what I found here with what I found in another answer and noticed in the console to produce a more minimalist answer just to determine the size itself of a model:
getDimensions(object3d) {
// e.g., object3d = document.querySelector('#goban').object3D
var box = new THREE.Box3().setFromObject( object3d );
var x = box.max.x - box.min.x
var y = box.max.y - box.min.y
var z = box.max.z - box.min.z
return {x,y,z}
}
I'm using protractor with Jasmine and I use page object pattern. In one of my page object I'm trying to hover the mouse over a pie chart. But when I use the following method it fails to get value for x coordinate using getDisHoverPoint(). When I put a logger for getDisHoverPoint(), it returns ManagedPromise::2516 {[[PromiseStatus]]: "pending"}. Please help.
this.hoverMouse = function() {
var dis = element(by
.css('#piecontainer .highcharts-series>path[fill="#434348"]'));
function getDisHoverPoint() {
return dis.getSize().then(function(text) {
return (text['height'] / 2).toFixed(0);
});
}
browser.actions().mouseMove(dis, {
x : getDisHoverPoint(),
y : 0
}).perform();
}
You have to resolve getDisHoverPoint() to get the actual value for x:
this.hoverMouse = function() {
var dis = element(by
.css('#piecontainer .highcharts-series>path[fill="#434348"]'));
function getDisHoverPoint() {
return dis.getSize().then(function(text) {
return (text['height'] / 2).toFixed(0);
});
}
getDisHoverPoint().then(function (value) {
browser.actions().mouseMove(dis, {
x : value,
y : 0
}).perform();
});
}
I'm trying to test if a todo app has the right number of elements.
The docs seem to deal almost exclusively with single elements, so I had to use the Selenium Protocol functions. Would this be the right way to test the count of matching selectors (in this case, checking for 2 li elements)?
client.elements('css selector','#todo-list li', function (result) {
client.assert.equal(result.value.length, 2);
});
This works in my test, but I wasn't sure if there were gotchas around using a callback for this. Also not sure why Nightwatch doesn't have any helper functions dealing with more than one element.
I found the following very elegant solution within a VueJS template. It shows how to add a custom assertion into Nightwatch that counts the number of elements returned by a selector. See http://nightwatchjs.org/guide#writing-custom-assertions for details of how to write custom assertions within Nightwatch.
Once installed, usage is as simple as:
browser.assert.elementCount('#todo-list li', 2)
The plugin:
// A custom Nightwatch assertion.
// the name of the method is the filename.
// can be used in tests like this:
//
// browser.assert.elementCount(selector, count)
//
// for how to write custom assertions see
// http://nightwatchjs.org/guide#writing-custom-assertions
exports.assertion = function (selector, count) {
this.message = 'Testing if element <' + selector + '> has count: ' + count;
this.expected = count;
this.pass = function (val) {
return val === this.expected;
}
this.value = function (res) {
return res.value;
}
this.command = function (cb) {
var self = this;
return this.api.execute(function (selector) {
return document.querySelectorAll(selector).length;
}, [selector], function (res) {
cb.call(self, res);
});
}
}
This code was added to vuejs-templates by yyx990803 in 2016. So full credit goes to yyx990803.
Just to reassure you I do a similar thing when trying to grab all matching elements, ex:
browser.elements("xpath","//ul[#name='timesList']/h6", function(result){
els = result.value;
var i = 0;
els.forEach(function(el, j, elz){
browser.elementIdText(el.ELEMENT, function(text) {
dates[i] = text.value;
i++;
});
});
});
Alternatively, if you want to be sure that n number of elements exist, you can use a combination of :nth-of-type/:nth-child selectors and nightwatch's expect.
For example, if you want to test if #foo has nine direct children:
function(browser) {
browser
.url('http://example.com')
.expect.element('#foo > *:nth-child(9)').to.be.present;
browser.end();
}
Or if #bar has three direct article children:
function(browser) {
browser
.url('http://example.com')
.expect.element('#bar > article:nth-of-type(3)').to.be.present;
browser.end();
}
You could use expect.elements(<selector>).count():
browser.expect.elements('div').count.to.equal(10);
browser.expect.elements('p').count.to.not.equal(1);
I adapted Chris K's answer to support XPath expressions by using the built-in method this.api.elements:
exports.assertion = function elementCount(selector, count) {
this.message = 'Testing if element <' + selector + '> has count: ' + count
this.expected = count
this.pass = function pass(val) {
return val === this.expected
}
this.value = function value(res) {
return res.value.length
}
this.command = function command(callback) {
return this.api.elements(this.client.locateStrategy, selector, callback)
}
}
For usage instructions and credits see his answer
And if you like a bit of TypeScript, here is an assertion that will confirm the element count:
import { NightwatchCallbackResult, NightwatchAssertion, NightwatchAPI } from "nightwatch";
module.exports.assertion = function (selector: string, count: number, description?: string) {
this.message = description || `Testing if element <${selector}> has count: ${count}`;
this.expected = count;
this.pass = (value: number) => value === this.expected;
this.value = (result: number) => result;
this.command = (callback: (result: number) => void): NightwatchAPI => {
const self: NightwatchAssertion = this;
return self.api.elements(this.client.locateStrategy, selector, (result: NightwatchCallbackResult) => {
callback(result.value.length);
});
}
}
Use like this:
browser.assert.elementCount('body', 1, 'There is only one body element');
i use NVD3 for a scatter chart but when hovering for the tooltip i want the label instead of the key.
this is my json:
long_data = [
{
key: 'PC1',
color: '#00cc00',
values: [
{
"label" : "Lichtpuntje" ,
"x" : 11.16,
"y" : -0.99,
"size":1000,
"color": '#FFCCOO'
} ,
{ ....
this is the js
nv.addGraph(function() {
chart = nv.models.scatterChart()
.showDistX(true)
.showDistY(true)
.useVoronoi(true)
.color(d3.scale.category10().range())
.transitionDuration(300)
...
chart.xAxis.tickFormat(d3.format('.02f'));
chart.yAxis.tickFormat(d3.format('.02f'));
chart.tooltipContent(function(i) { return labelArray[i]; });
d3.select('#test1 svg')
.datum(long_data)
.call(chart);
...
how i can i get the tooltip to have the label value? or how can i have i as index parameter?
ok, not a clean solution, but works:
chart.tooltipContent(function(key, y, e, graph) {
labelIndex=arrayContains(resultYVal, e);
return resultLabel[labelIndex];
});
function arrayContains(a, obj) {
var i = a.length;
while (i--) {
if (a[i] == obj) {
return i;
}
}
return false;
}
You can access your label variable like this:
chart.tooltipContent(function(graph) {
console.log(graph); //examine the graph object in the console for more info
return graph.point.label;
});
Newer NVD3 versions use tooltipGenerator. Also I don't understand why shev72 is iterator over the whole series, we're getting the index in the series by NVD3 directly, e.g. if our data looks like this:
data = [{"key": "Group 0", "values": [{"y": 1.65166973680992, "x": 0.693722035658137, "z": "SRR1974855"}, {"y": 1.39376073765462, "x": -0.54475764264808, "z": "SRR1974854"}]
then you can get your z values like this:
chart.tooltip.contentGenerator(function (d) {
var ptIdx = d.pointIndex;
var serIdx = d.seriesIndex;
z = d.series[serIdx].values[ptIdx].z
return z;
});
For anyone here having issues trying to show a custom tooltip with InteractiveGuideline enabled, you must use the following:
chart.interactiveLayer.tooltip.contentGenerator(function (obj) { ... });
As a bonus, here's the code for the default table layout with an added custom value 'z':
chart.interactiveLayer.tooltip.contentGenerator(function (obj) {
var date = obj.value; // Convert timestamp to String
var thead = '<thead><tr><td colspan="4"><strong class="x-value">'+ date +'</strong></td></tr></thead>';
var table = '<table>' + thead + '<tbody>';
// Iterate through all chart series data points
obj.series.forEach(dataPoint => {
// Get all relevant data
var color = dataPoint.color;
var key = dataPoint.key;
var value = dataPoint.value;
var string = dataPoint.data.z; // Custom value in data
var row = '<tr><td class="legend-color-guide"><div style="background-color: '+ color +';"></div></td><td class="key">'+ key +'</td><td class="string">'+ string +'</td><td class="value">'+ value +'</td></tr>';
table += row;
});
// Close table & body elements
table += '</tbody></table>';
return table;
});
I found the answer from davedriesmans quite useful, however note that in the code
chart.tooltipContent(function(key, y, e, graph)) is not for a scatterPlot.
This looks like the function for a pie chart. In that case you can use the e.pointIndex directly to allow you to index into the series by graph.series.values[e.pointIndex].
However, I built on the function davedriesmans suggested for a scatterplot as follows.
function getGraphtPt(graph, x1, y1) {
var a = graph.series.values;
var i = a.length;
while (i--) {
if (a[i].x==x1 & a[i].y==y1) {
return a[i];
}
}
return null;
}
the main chart call to insert the tooltip is just
chart.tooltipContent(function (key, x, y, graph) {
s = "unknown";
pt = getGraphtPt(graph, x, y);
if (pt != null) {
//below key1 is a custom string I added to each point, this could be 'x='+pt.x, or any custom string
// I found adding a custom string field to each pt to be quite handy for tooltips
s = pt.key1;
}
return '<h3>' + key + s + '</h3>';
});
In my test -> http://jsfiddle.net/olragon/642c4/12/, KendoUI Combobox cannot run with 5000 items, how can I make it work without calling severside data source or this is limit of KendoUI?
HTML
<h3>T-shirt Fabric</h3>
<input id="fabric" placeholder="Select fabric..." />
JS
/**
* Returns a random integer between min and max
* Using Math.round() will give you a non-uniform distribution!
*/
function getRandomInt (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
$(document).ready(function() {
var superData = []
, data = [
{ text: "Cotton", value: "1" },
{ text: "Polyester", value: "2" },
{ text: "Cotton/Polyester", value: "3" },
{ text: "Rib Knit", value: "4" }
];
for(var _i=0; _i<5000; _i++) {
var randomEntry = data[getRandomInt(0,data.length-1)];
randomEntry.text += '-' + _i;
randomEntry.value += _i;
superData.push(randomEntry);
}
// create ComboBox from input HTML element
$("#fabric").kendoComboBox({
dataTextField: "text",
dataValueField: "value",
dataSource: superData,
filter: "contains",
suggest: true,
index: 3
});
});
Update
Fiddle link was updated.
Virtual scrolling and paging for Combobox was not yet supported by KendoUI
The problem is not in Kendo UI ComboBox but in your loop. Did you check what it does (not what you want it to do)? I would say that there is an error since data[getRandomInt(0,data.length-1)] does not return a new element but a reference so you are appending "_i" to the same 5 elements many times building up a very long string.
Try this instead:
for (var _i = 0; _i < 5000; _i++) {
var randomEntry = data[getRandomInt(0, data.length - 1)];
var newEntry = {
text: randomEntry.text + '-' + _i,
value : randomEntry.value += _i
};
superData.push(newEntry);
}
Check the modified version of the Fiddle here: http://jsfiddle.net/OnaBai/642c4/14/