Nightwatch JS command chaining doesn't work in async functions - javascript

I'm trying to call an async page object command in my Nightwatch test but the test is failing. As you can see by the code sample, I'm attempting to chain a couple of .pause commands together but the chaining mechanism is not working. If I remove the 'async' keywords and comment out the 'await' code then the chained commands work. So it seems that using 'async' breaks command chaining. Is there a solution for this?
Nightwatch test ...
module.exports = {
'Test Commmand Queue': async function (browser) {
browser.page.clients.sandbox.simpleTestPageObject().testCommandQueue() // async function
}
}
page object file ...
module.exports = {
elements: {},
commands: [{
testCommandQueue: async function () {
this
.pause(1)
.pause(1)
console.log("0001")
await this.pause(3000, function () {
console.log("0002")
})
console.log("0003")
}
}]
}
output ...
Running: Test Commmand Queue
_________________________________________________
TEST FAILURE: 1 error during execution; 0 tests failed, 0 passed (4.939s)
TypeError: this.pause(...).pause is not a function
Nightwatch v 1.5.0

As much as it looks appealing to use chaining commands I suggest use this.api or browser.
It sounds like a chore but in the long run you will encounter less issues with methods and custom commands.
For example I can't use chaining commands if I need to use the expect by Mocha so I just rather use browser

Related

Detect missing await in JavaScript methods in VSCode

I'm searching for some eslint option, or some other way to detect missing the 'await' keyword before calling async methods inside a class. Consider the following code:
const externalService = require('./external.service');
class TestClass {
constructor() { }
async method1() {
if (!await externalService.someMethod()) {
await this.method2();
}
}
async method2() {
await externalService.someOtherMethod();
}
module.exports = TestClass;
There will be no warning if I will convert method1 to:
async method1() {
if (!await externalService.someMethod()) {
this.method2();
}
}
I tried to do on the '.eslintrc' file:
"require-await": 1,
"no-return-await": 1,
But with no luck. Anyone have an idea if it is even possible?
Thanks a lot!
typescript-eslint has a rule for this: no-floating-promises
This rule forbids usage of Promise-like values in statements without handling their errors appropriately ... Valid ways of handling a Promise-valued statement include awaiting, returning, and either calling .then() with two arguments or .catch() with one argument.
As you probably figured out from the name, typescript-eslint is designed to add TypeScript support to eslint, but you can use it with JavaScript as well. I guess it's your call to decide whether it's overkill for this one rule, but here are the steps:
Generate a tsconfig.json file
npx tsc --init
Install dependencies
npm install --save-dev eslint #typescript-eslint/eslint-plugin #typescript-eslint/parser
Modify your .eslintrc file
Based on my testing it looks like you'll need these entries at a minimum:
{
"parser": "#typescript-eslint/parser",
"parserOptions": { "project": "./tsconfig.json" },
"plugins": ["#typescript-eslint"],
"rules": {
"#typescript-eslint/no-floating-promises": ["error"]
}
}
If there are places where you want to call an async function without using await, you can either:
Use the void operator as mentioned in the documentation for the rule, e.g.
void someAsyncFunction();
Or just change error to warn in the .eslintrc configuration above
The documentation for setting up typescript-eslint is here for more info: https://typescript-eslint.io/docs/linting/linting
Next time you run eslint you should see the rule applied:
$ npm run lint
...
./services/jobService.js
11:5 warning Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator #typescript-eslint/no-floating-promises
Since you mentioned VS Code specifically, this also integrates great with the ESLint plugin:
require-await says "Don't make a function async unless you use await inside it".
This is because async has two effects:
It forces the function to return a promise
It lets you use await inside it
The former is rarely useful which mean if you aren't using await inside the function you need to question why you marked it as async.
no-return-await stops you from doing:
return await something
Because await unwraps a value from a promise, but returning a value from an async function wraps it in a promise.
Since just returning a promise causes that promise to be adopted, combining return with await is just bloat.
So neither of those do what you want.
Which brings us to your actual desire.
Such a feature does not (as far as I know) exist in ESLint, and I don't think it would be useful to have one.
There are lots of use cases where you don't want to await something returned by an async function.
e.g.
const array_of_promises = array_of_values.map( value => do_something_async(value) );
const array_of_resolved_values = await Promise.all(array_of_promises);
The above is a common use-case where you want to run a bunch of async functions in parallel and then wait for them all to resolve.
Another example is the case that no-return-await is designed to detect!
Cases like these are common enough that most people wouldn't want their toolchain calling them out for doing it.
There is an ESLint plugin for this that you can use as an alternative to the typescript-eslint route:
https://github.com/SebastienGllmt/eslint-plugin-no-floating-promise

Running a test with Mocha also launches the main program

I'm trying to use Mocha to test a CLI app. The tests are running fine but, when I launch the testing procedure, it also launches the main app:
$ npm run test
> standardize-js#0.2.2 test C:\Users\Gaspard\Documents\Code\standardize-js
> mocha "./source/**/*.spec.js"
? Choose your project language or framework (Use arrow keys) //<-- THIS IS THE PROGRAM
> Javascript
Typescript
AngularJS
Main function //<-- THIS IS THE TEST
ask if the configuration is valid
Configuration is not valid, terminating program.
√ should return false if the configuration is not accepted
1 passing (29ms)
I'm kind of new to the testing world and I'm really struggling to understand what I'm doing wrong.
Here is the NPM script used to launch mocha :
"test": "mocha \"./source/**/*.spec.js\""
Here is my testing method:
/* eslint-disable func-names */
const { expect } = require("chai");
const main = require("./index").test;
describe("Main function", function() {
describe("ask if the configuration is valid", function() {
it("should return false if the configuration is not accepted", function() {
const fakeAnswer = false;
expect(main.validateConfiguration(fakeAnswer)).to.equal(false);
});
});
});
And here is my index.js file:
function validateConfiguration(answer) {
if (answer === false) {
console.log(chalk.red("Configuration is not valid, terminating program."));
return false;
}
return true;
}
const run = async () => {
//MAIN FUNCTION
};
run();
// Export functions and variables to be able to test
exports.test = {
validateConfiguration
};
It's not a problem with mocha. It is simply now node.js modules work.
When you do this:
const main = require("./index").test;
Node.js will execute index.js and then check the value of module.exports. If the module (index.js) sets or modifies module.exports then node will export it for use by require(). But note, in order for node to know that the module has exported anything it must execute the javascript file.
Node.js does not have any ability to parse and analyze javascript syntax (that's V8's job). Unlike other languages such as C or Java, modules in node.js are not implemented at the syntax level. Therefore the javascript language does not need to be modified (eg. ES6 modules) for node.js to support modules. Modules are simply implemented as a design pattern.
In your index.js file you call run:
run();
When require() loads index.js it will therefore also cause run() to be called.
Test libraries, not main
The solution to this is to implement your own logic as modules and test that, not test index.js:
mylib.js:
function validateConfiguration(answer) {
if (answer === false) {
console.log(chalk.red("Configuration is not valid, terminating program."));
return false;
}
return true;
}
// Export functions and variables to be able to test
exports.test = { validateConfiguration };
index.js:
const validateConfiguration = require("./mylib").test;
const run = async () => {
//MAIN FUNCTION
};
run();
You can now use your test script as written.
How can you not test code??
The strategy to keep index.js bug free without testing is to remove all logic from it except for the minimum amount of code to wire all your other code up together to run the app. The code should be as simple as "Hello World". That way, the code in main is so small and so simple that you can test it for bugs using your eyeballs.
Any code in index.js that causes a bug should be refactored into its own library so that it can be tested separately. There are a small handful of corner cases, such as loading environment variables or opening port 80 where you can't really separate into a library because they literally are wiring logic. For such cases you just have to be really careful.
It's calling run because you are telling it to right after defining the method.

Testing node.js Command Line tool with Jest

I'm currently trying to get some test coverage for a command line tool I built in node with Jest. My code is split up in modles, most of which have asynchronous http requests that I'm trying to mock. I'm wondering if there is a way to mock these requests that exist inside the module functions?
module.exports = function() {
client.apiGet() // How do I mock this?
.then(() => {
// more logic, runs several fs operations
})
.catch((err) => { console.error(err) });
}
You will need to mock the client module. I assume you had require or import it further up. Once you have it mocked you can decide to either stub all of it's methods' implementations or just the apiGet.
https://jestjs.io/docs/en/manual-mocks.
Finally, you will probably use the .resolves and .rejects to have the proper expectation.
https://jestjs.io/docs/en/expect#resolves
#Koen Van Gilst wrote a great blog about mocking API calls with Jest:
https://hackernoon.com/api-testing-with-jest-d1ab74005c0a
Hope this sends you in the right direction

How do I get Promises to work when using Jest CLI?

I'm attempting to test a promise with Jest CLI, this code executes as it should when it's run in the browser. However I want to start writing tests for it.
class ListCollection {
constructor() {
this.items = new Array();
}
addItem(string) {
const addItemPromise = new Promise(
function (resolve, reject) {
// set up async getting like a XMLHttpRequest
setTimeout( () => {
this.items.push(string);
resolve(string);
}.bind(this), 2000);
}.bind(this)
);
return addItemPromise;
}
}
Currently I'm trying to get this very basic test to work. I'm testing with pit as per the documentation which links to jasmine-pit.
jest.dontMock('../collections');
import Collection from '../collections';
describe("Collection", () => {
let collection;
beforeEach(() => {
collection = new Collection();
});
describe("Adding an item", () => {
pit('Spec 1', function () {
return collection.addItem("Hello World").then(function (string) {
expect(string).toBe("Hello World");
});
});
});
})
When I run my tests with babel-node ./node_modules/.bin/jest, the test above fails with this as it's stack trace. Notably I get Promise is not defined.
Rollos-Mac-Pro:react-boilerplate Rollo$ babel-node ./node_modules/.bin/jest
Using Jest CLI v0.4.0
FAIL app/collections/__tests__/collectionTests.js
ReferenceError: /Users/Rollo/react-boilerplate/app/collections/__tests__/collectionTests.js: /Users/Rollo/react-boilerplate/app/collections/collections.js: **Promise is not defined**
at ListCollection.addItem (/Users/Rollo/react-boilerplate/app/collections/collections.js:24:34)
at /Users/Rollo/react-boilerplate/app/collections/collections.js:48:12
at Object.runContentWithLocalBindings (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/lib/utils.js:361:17)
at Loader._execModule (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/HasteModuleLoader/HasteModuleLoader.js:250:9)
at Loader.requireModule (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/HasteModuleLoader/HasteModuleLoader.js:916:12)
at Loader.requireModuleOrMock (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/HasteModuleLoader/HasteModuleLoader.js:937:17)
at /Users/Rollo/react-boilerplate/app/collections/__tests__/collectionTests.js:7:34
at Object.runContentWithLocalBindings (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/lib/utils.js:361:17)
at Loader._execModule (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/HasteModuleLoader/HasteModuleLoader.js:250:9)
at Loader.requireModule (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/HasteModuleLoader/HasteModuleLoader.js:916:12)
at jasmineTestRunner (/Users/Rollo/react-boilerplate/node_modules/jest-cli/src/jasmineTestRunner/jasmineTestRunner.js:242:16)
at /Users/Rollo/react-boilerplate/node_modules/jest-cli/src/TestRunner.js:371:12
at _fulfilled (/Users/Rollo/react-boilerplate/node_modules/jest-cli/node_modules/q/q.js:798:54)
at self.promiseDispatch.done (/Users/Rollo/react-boilerplate/node_modules/jest-cli/node_modules/q/q.js:827:30)
at Promise.promise.promiseDispatch (/Users/Rollo/react-boilerplate/node_modules/jest-cli/node_modules/q/q.js:760:13)
at /Users/Rollo/react-boilerplate/node_modules/jest-cli/node_modules/q/q.js:574:44
at flush (/Users/Rollo/react-boilerplate/node_modules/jest-cli/node_modules/q/q.js:108:17)
at /Users/Rollo/react-boilerplate/node_modules/jest-cli/src/lib/FakeTimers.js:325:7
at process._tickCallback (node.js:448:13)
I don't know how to fix this. The node version I'm using has to be 0.10.x, otherwise I can't run the Jest-CLI. But node version 0.10.x doesn't have promises. I also don't understand how Jest-CLI works with my ES6 classes and syntax but won't recognize Promises.
Any idea how to get promises to work in my setup?
EDIT
I've added the es6-promise polyfill to the top of my test file and flagged it not to be mocked. This provides an adequate fix.
jest.dontMock('es6-promise');
require('es6-promise').polyfill();
The output mentions babel-node, which means that babel is running as a require hook, compiling your ES6 to ES5 on the fly. You need to include a shim for native promises, and expose it as global.Promise. I'd recommend this one: https://github.com/jakearchibald/es6-promise
replace jest scriptPreprocessor with babel-jest should work fine for all es6 comparability issues
https://babeljs.io/docs/using-babel/#jest

How can I use dijit/regisrty in an Intern functional test

I am having problems using any dojo modules with my functional test, I keep seeing window not defined errors or document not defined.
I am currently trying to use the dijit/registry like so (only importing its module so far)..
define([
'intern!object',
'intern/chai!assert',
'require',
'dijit/registry'
], function (registerSuite, assert, require, registry) {
registerSuite({
name: 'login',
'load and login': function () {
return this.remote.get(require.toUrl('http://application.co.uk/'))
.elementById('input1')
.click()
.type("username")
.end()
.elementById('input2')
.click()
.type("password")
.end()
.elementById('rememberMe')
.click()
.end()
.elementByName('Login.Submit')
.click()
.end()
.wait(5000)
.title()
.then(function (title) {
assert.strictEqual(title, 'Application title');
});
}
});
});
...and am getting the following error from node...
$ ./libs/intern/bin/intern-runner.js config=test/intern
Defaulting to "runner" reporter
Listening on 0.0.0.0:9000
c:/.../libs/dojo/_base/unload.js:6
var win = window;
^
ReferenceError: window is not defined
at c:/.../libs/dojo/_base/unload.js:6:11
at execModule (c:\...\libs\intern\node_modules\dojo\dojo.js:512:54)
at c:\...\libs\intern\node_modules\dojo\dojo.js:501:12
at Array.map (native)
at execModule (c:\...\libs\intern\node_modules\dojo\dojo.js:496:17)
at c:\...\libs\intern\node_modules\dojo\dojo.js:501:12
at Array.map (native)
at execModule (c:\...\libs\intern\node_modules\dojo\dojo.js:496:17)
at c:\...\libs\intern\node_modules\dojo\dojo.js:501:12
at Array.map (native)
I have read a previous question about using dojo/text! in a similar way which seemed to indicate the geezer version of Intern could handle this maybe?
The test runs fine without the registry module.
UPDATE
Ok so based on C Snover's response, you cant leverage anything like dijit/registry outside of a webdriver execute() method as the code needs to be within the context of the web browser not the functional test.
Functional tests run from within Node.js, not the browser environment. If you want to access the dijit/registry instance that was loaded in the page you are testing, you need to use execute to run a function within the remote environment:
return this.remote
.get('http://application.co.uk')
.execute(function () {
// this function runs in the browser!
var registry = require('dijit/registry');
// ... do things with registry
return something;
})
.then(function (something) {
// this function runs inside the test runner!
assert.isTrue(something);
});
You won’t be able to define dependencies that have DOM requirements (like dijit/registry) from functional test modules. Only browser-based unit test modules will be able to load such dependencies.
There's also the dijit-intern-helper -> https://github.com/SitePen/dijit-intern-helper
so this
executeAsync(function (done) {
require(['dijit/registry'], function (registry) {
done(registry.byId('titlePane').titleBarNode);
});
})
.then(function (node, setContext) {
setContext(node);
})
.click()
becomes
.then(dijit.nodeById('titlePane', 'titleBarNode'))
.click()
A blog post about it -> https://www.sitepen.com/blog/simplified-dijit-functional-testing/

Categories