Then("a Cucumber sentence", async function () {
await driver.findElements(By.xpath('/html/body'))
});
I try to use findElements from Selenium but keep getting
ReferenceError: By is not defined
anything I can do for this?
I've seen it imported like this (looks weird to me):
var webdriver = require('selenium-webdriver'),
By = webdriver.By,
until = webdriver.until;
Related
I'm using Mocha and Chai for unit testing.
Now I want to simulate a click event on an element. In a regular dom environment you could use .click() method As shown here: https://jsfiddle.net/Spindle/jwyx6L84/
However, since the tests I'm running in a Node environment, this isn't working obviously.
The following is the test scenario example:
import { describe } from 'mocha';
const expect = require('chai').expect;
const jsdom = require('jsdom');
const { JSDOM } = jsdom;
describe('example...', function() {
beforeEach(function() {
const { window } = new JSDOM(`
<!doctype html><html>
<button class="trigger" onclick="this.classList.add('test-class')">
</button>
</html>`, { runScripts: "dangerously" });
// setting it to dangerously works...
global.window = window;
});
it(`click event should be fired`, function() {
const triggerElement = window.document.getElementsByClassName('trigger')[0];
triggerElement.click();
expect(triggerElement.classList.contains('test-class')).to.equal(true);
});
});
So far my search has shown a few different options:
enzyme Tried this, it will require a react adapter to work. Seems to be an overkill since I want to test vanillaJS?
simulant While implementing I don't get past an error: ReferenceError: window is not defined. Even when I implement something like jsdom-global (which makes the window object available in Node env) Can this only be used in a browser environment?
selenium This seems to be quite verbose.
puppeteer This seems promising for the use-case. However is this compatible with JSDOM?
What should I be using to simulate a click event for vanilla JS?
update:
Setting JSDOM to dangerously works. However, comes as the name 'dangerously' suggests with a risk. (source)
Just running your scripts in the node environment (and in the CI for that matter) doesn't seem desired.
background
I have written an ethereum smart-contract in the Solidity language. In order to test things, I can run a local node using Ganache and deploy my contract on it using truffle migrate.
requirements
I want to test my contract using JavaScript. I want to create a new instance of my contract for each test.
what i've tried
I created a test file tests/test.js in my project:
const expect = require('chai').expect
const Round = artifacts.require('Round')
contract('pledgersLength1', async function(accounts) {
it('1 pledger', async function() {
let r = await Round.deployed()
await r.pledge(5)
let len = (await r.pledgersLength()).toNumber()
expect(len).to.equal(1)
})
})
contract('pledgersLength2', async function(accounts) {
it('2 pledgers', async function() {
let r = await Round.deployed()
await r.pledge(5)
await r.pledge(6)
let len = (await r.pledgersLength()).toNumber()
expect(len).to.equal(2)
})
})
I run it with truffle test. It's basically Mocha, but truffle defines artifacts for you with a JavaScript connection to the smart contracts.
The truffle contract function is almost the same as Mocha's describe function, with a small change that I don't understand! I assumed that contract would make my contract new each time. It doesn't. Perhaps I can use something like new Round() instead of Round.deployed(), but I just don't know how.
The solution does not have to use truffle.
Please note that .new and .deployed are not the same. See what I've found here.
Follow this example and it should solve your problem:
// Path of this file: ./test/SimpleStorage.js
var simpleStorage = artifacts.require("./SimpleStorage.sol");
contract('SimpleStorage', function(accounts) {
var contract_instance;
before(async function() {
contract_instance = await simpleStorage.new();
});
it("owner is the first account", async function(){
var owner = await contract_instance.owner.call();
expect(owner).to.equal(accounts[0]);
});
});
The .new keyword will deploy an instance of your contract in a new context.
But, .deployed will actually use the contract that you have deployed earlier i.e. when you are using truffle migrate command.
In the context of unit test, it's better to use .new so that you will always start with fresh contract.
I'm trying to test functions while developing a chrome extension. For my testing framework, I'm using sinon (to spy, mock, and stub), mocha, chai, and jsdom (to create a dom that executes my chrome extension background and popup scripts).
However, I can't seem to be able to spy on the functions in the script (background.js) that I'm injecting into the jsdom.
This is what I'm doing:
background.js
function searchTabs() {...}
searchTabs();
module.exports.searchTabs = searchTabs;
background.test.js
var fs = require('fs');
var sinon = require('sinon');
var chrome = require('sinon-chrome');
var assert = require('chai').assert;
var jsdom = require('jsdom');
var bg = require('background.js');
var window;
var spy;
describe('background page', function () {
beforeEach(function () {
jsdom.env({
html: '<html></html>',
src: [
fs.readFileSync('background.js', 'utf-8'), // Inject script into jsdom
],
created: ...,
done: ...,
});
});
afterEach(function () {
chrome.reset();
window.close();
});
it('should call searchTabs', function () {
spy = sinon.spy(bg.searchTabs);
sinon.assert.calledOnce(spy); // This is not called :(
});
});
I suspect the problem has to do with either an incorrect import/export or that the background.js script injected into the jsdom isn't wrapped by the spy. I'd really appreciate it if someone can shed some light on this problem!
You're requiring the background in the NodeJS context
bg = require('background.js');
and then in your test you apply a spy to that.
JSDOM however gets its own copy of the file through fs.readFileSync and executes it in a sandbox, separated from the NodeJS context - so you're not applying the fake there.
Besides that, judging from your provided example, the searchTabs function is already executed when you load (or require) the background.js - so applying a spy after the function got called wont get you the expected results.
What you could do instead is separating the function from the actual execution and apply fakes, inside the JSDOM sandbox, inbetween. That could look this:
background.js
window.searchTabs = () => {
// I'm searching for tabs
}
run.js
window.searchTabs();
test.js
const fs = require('fs');
const chrome = require('sinon-chrome');
const {JSDOM} = require('jsdom');
const html = '<!DOCTYPE html><html><head></head><body></body></html>';
const dom = new JSDOM(html, {
runScripts: 'outside-only',
beforeParse(window) {
window.chrome = chrome;
}
});
dom.window.eval(fs.readFileSync('background.js'));
const spy = sinon.spy(dom.window, 'searchTabs');
dom.window.eval(fs.readFileSync('run.js'));
sinon.assert.calledOnce(spy);
If you're looking for an easy way to load popups or background in JSDOM based on your manifest.json then webextensions-jsdom might be interesting too.
I am having an issue with selenium js
I have created my components in json like:
"usernameInputField": {
"selector": {
"xpath": "//*[#id='username']"
}
}
and I am calling webdriver :
var webdriver = require('selenium-webdriver');
using the data like this:
console.log(webdriver.By.xpath("//*[#id='username']"));
it calls correctly
however when I try to run console.log(webdriver.By(usernameInputField.selector));
I get an error (TypeError: Class constructors cannot be invoked without 'new')
what am I doing wrong here?
You can use regular findElement directly without having to use the By "class":
driver.findElement(usernameInputField.selector);
var sample1 = browser.findElements(driver.By.xpath('//somenode')).getXpathCount();
console.log( sample1.intValue() );
while printing the count I am getting error:
error occuredTypeError: undefined is not a function
Like #alecxe stated, the syntax for getXpathCount() is browser.getXpathCount("//somenode").
I saw you opened an issue on the selenium git and had more code there. What isn't showing here is you have just the following.
var browser = require('selenium-webdriver');
var sample1 = browser.findElements(driver.By.xpath('//somenode')).getXpathCount();
console.log( sample1.intValue() );
I haven't used WebDriverJs, so someone please correct me if I am wrong, but I think you need to create a browser object. Right now you only have created a driver object named browser.
Can you try the following snippet?
var webdriver = require('selenium-webdriver');
var browser = new webdriver.Builder().usingServer().withCapabilities({'browserName': 'chrome' }).build();
browser.get('http://en.wikipedia.org/wiki/Wiki');
browser.getXpathCount('//*[#id="www-wikipedia-org"]/div[1]/div');
findElements method returns an Array promise, so you have to do something like this:
browser.findElements(driver.By.xpath('//somenode')).then(function(elements) {
var count = elements.length;
...
})
I think you are not using getXpathCount() correctly. You should do it this way:
browser.getXpathCount("//somenode");