I'm running a Nightmare.js script, and within it I'm trying to require lodash
(installed locally with npm, in node_modules, registered in package.json).
index.js :
var Nightmare = require('nightmare');
var _ = require('lodash');
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
function getElms(elm) {
var elsm = document.querySelectorAll(elm);
return _.map(elsm, function(elem){ return elem.innerHTML; });
}
var someWebsite = new Nightmare({ show: false })
.goto( ***some url*** )
.wait(getRandomInt(2500, 5000))
.evaluate(function () {
var elsm = document.querySelectorAll(***some selector***);
return _.map(elsm, function(elem){ return elem.innerHTML; });
// return getElms(***some selector***);
}).then(function(linkstexts) {
console.log(linkstexts)
});
but I'm getting this:
UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): _ is not defined
similar issue if I uncomment and use return getElms(***some selector***);
This is package.json
{
"name": "nightxp",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {
"lodash": "^4.17.4",
"nightmare": "^2.10.0"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
What am I missing?
UPDATE
I tried passing the lodash to the browser scope, and completing with catch(),
but somehow the lodash variable still isnt being passed to the scope (example with hackernews):
var Nightmare = require('nightmare');
var lodash = require('lodash');
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
var facebook = new Nightmare({ show: false })
.goto('https://news.ycombinator.com')
.wait(getRandomInt(5500, 6000))
.evaluate( (lodash) => {
var elsm = document.querySelectorAll('tr td.title a.storylink');
return lodash.map(elsm, function(elem){ return elem.innerHTML; });
}, lodash).then(function(titles) {
console.log(titles)
}).catch(function (error) {
console.error('Error:', error);
});
and this is what I get:
C:\Projects\nightmare>set DEBUG=nightmare & node index.js
nightmare queuing process start +0ms
nightmare queueing action "goto" for https://news.ycombinator.com +4ms
nightmare queueing action "wait" +2ms
nightmare queueing action "evaluate" +0ms
nightmare running +1ms
Error: Cannot read property 'map' of undefined
When you run
var someWebsite = new Nightmare({ show: false })
.evaluate(function () {
/* ... */
}
You are now in browser scope — you can think of Nightmare.evaluate as running the contained code in the context of an independent browser, which means your global scope, including _ is not available.
See here for a bit more info and some tips on passing data into the scope:
https://github.com/segmentio/nightmare/issues/89
https://github.com/segmentio/nightmare/issues/333
Edit:
While you can pass simple values into evaluate from the node context, you can't pass complex values with functions and methods. This is because the arguments are serialize in transit and this is difficult with functions. Here's an issue that explains it a bit:
https://github.com/segmentio/nightmare/issues/349
In this particular case it seems like the easiest thing to do is to work without lodash, which these days isn't hard. For example:
var facebook = new Nightmare({ show: false })
.goto('https://news.ycombinator.com')
.wait(getRandomInt(5500, 6000))
.evaluate( () => {
var elsm = document.querySelectorAll('tr td.title a.storylink');
return Array.prototype.map.call(elsm, elem => elem.innerHTML)
})
.then(function(titles) {
console.log("title", titles)
})
.catch(function (error) {
console.error('Error:', error);
});
Related
EDIT:
As per the comment on the answer below: removing "type": "module" from package.json, which as I understand it is what makes Node understand 'import' and 'export' statements, and reverting everything to 'require' and 'module.exports' solved the issue.
Is there a way to keep 'import' and 'export' and still make Mocha work?
I have a very simple Node file that I'm trying to test with Mocha/Chai. The actual code is trivial, this is just to learn a bit about Mocha and how to use it. But when I run the Mocha test, I get the error ERROR: ReferenceError: require is not defined
`
I did some googling for people experiencing the same problem but the examples that I came up with were when they were running the test in the browser (see, for example, Mocha, "require is not defined" when test in browser).
The file I want to test, index.js
const argv = require('minimist')(process.argv.slice(2));
const digitTester = /\d/g;
const capTester = /[A-Z]/g;
const dict = {
length:10,
must_have_numbers: true,
must_have_caps: true
}
export default function passwordCheck(password) {
if (!password) return false;
if (typeof password !== "string") return false;
if (password.length < dict.length) return false; // assumes that 10 is a MINIMUM length
if (dict.must_have_numbers && !digitTester.test(password)) return false;
return !(dict.must_have_caps && !capTester.test(password));
}
if (argv._.length) {
console.log(passwordCheck(argv._[0]))
}
/**
* alternate version to check a lot of passwords:
*
* if (argv._.length) {
* for (const pwd of argv_) console.log(passwordCheck(pwd)
*}
*
*/
the mocha file, test/index.test.js
const chai = require('chai')
const expect = chai.expect
const passwordCheck = require('../index.js')
const tooShort = "A2a"
const noCaps = "a2abcdefghijklmnop"
const noNumber = "Aabcdefghijklmnop"
const good = "A2abcdefghijklmnop"
describe('password checking', () => {
it('should return false for passwords less than length 10', () => {
expect(passwordCheck(tooShort)).to.be.false;
});
it('should return false for passwords without a capital letter', () => {
expect(passwordCheck(noCaps)).to.be.false;
});
it('should return false for passwords without a number', () => {
expect(passwordCheck(noNumber)).to.be.false;
});
it('should return true for passwords that match criteria', () => {
expect(passwordCheck(good)).to.be.true;
});
});
and package.json
{
"name": "codetest",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"scripts": {
"test": "mocha"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"#types/minimist": "^1.2.1",
"#types/node": "^14.14.20",
"chai": "^4.2.0",
"minimist": "^1.2.5",
"mocha": "^8.2.1"
}
}
and the error message is
✖ ERROR: ReferenceError: require is not defined
at file:///Users/r/Documents/Projects/sandbox/pwd_checker/index.js:2:14
at ModuleJob.run (node:internal/modules/esm/module_job:152:23)
at async Loader.import (node:internal/modules/esm/loader:166:24)
at async exports.handleRequires (/Users/r/Documents/Projects/sandbox/pwd_checker/node_modules/mocha/lib/cli/run-helpers.js:94:28)
at async /Users/r/Documents/Projects/sandbox/pwd_checker/node_modules/mocha/lib/cli/run.js:341:25
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
Node 15.
Remove this line - "type": "module" from package.json and check whether it’s working or not.
Prepend your tests with the following:
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
This is because you cannot require from an ESM module; for more info please see this comment on a nodejs issue.
Documentation: https://nodejs.org/api/esm.html#differences-between-es-modules-and-commonjs
I'm using MongoMemoryServer to write an integration test. I have two integration test files. When I run the IT tests I see the following. I don't understand why. I'm using jestjs test framework.
I'm seeing the following error when I have two IT test files
MongoError: pool is draining, new operations prohibited
37 | for (const key in collections) {
38 | const collection = collections[key];
> 39 | await collection.deleteMany();
| ^
40 | }
41 | };
Here is my setup
//db-handler.js
const mongoose = require("mongoose");
const { MongoMemoryServer } = require("mongodb-memory-server");
const mongod = new MongoMemoryServer();
module.exports.connect = async () => {
const uri = await mongod.getConnectionString();
const mongooseOpts = {
useNewUrlParser: true,
autoReconnect: true,
reconnectTries: Number.MAX_VALUE,
reconnectInterval: 1000,
};
await mongoose.connect(uri, mongooseOpts);
};
module.exports.closeDatabase = async () => {
await mongoose.connection.dropDatabase();
await mongoose.connection.close();
await mongod.stop();
};
module.exports.clearDatabase = async () => {
const collections = mongoose.connection.collections;
for (const key in collections) {
const collection = collections[key];
await collection.deleteMany();
}
};
All my IT tests setup looks like this
//example.it.test.js
const supertest = require("supertest");
const dbHandler = require("./db-handler");
const app = require("../../src/app");
const request = supertest(app);
const SomeModel = require("../Some");
beforeAll(async () => await dbHandler.connect());
afterEach(async () => await dbHandler.clearDatabase());
afterAll(async () => await dbHandler.closeDatabase());
describe("Some Test Block", () => {
it("Test One", async (done) => {
await SomeModel.create({a: "a", b "b"});
const response = await request.get("/endPointToTest");
expect(response.status).toBe(200);
done();
});
When I have just a single IT test files, everything works fine. When I introduce a a new IT test file similar setup up as example.it.test.js, then the new test fails. The example error message above.
What am I missing? Does my setup need to change when I have multiple IT test files?
UPDATE ONE:
My package.json file looks like
{
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js",
"test": "jest --runInBand ./tests"
},
"devDependencies": {
"jest": "^25.2.7",
"mongodb-memory-server": "^6.5.2",
"nodemon": "^2.0.2",
"supertest": "^4.0.2"
},
"jest": {
"testEnvironment": "node",
"coveragePathIgnorePatterns": [
"/node_modules/"
]
}
}
I think the error message can be somewhat misleading for some cases.
It seems like the issue is related to "pool size being too small" or "jest runs tests in parallel", so the pool eventually ran out of usable pool.
However, if you look at what happens when mongo destroys the pool, you'll get the idea.
/**
* Destroy pool
* #method
*/
Pool.prototype.destroy = function(force, callback) {
...
// Set state to draining
stateTransition(this, DRAINING);
...
If you try to close the connection, the pool changes its internal state into 'draining' which causes the problem.
So what the error message is telling us is that there's still unfinished writing operation(s) going on after pool.destroy.
For my case, that was an active event listener callback runs every x seconds which does write to mongo.
I changed my test code to call event callback function directly instead of being called every x seconds.
That resolved my issue.
By default Jest runs tests in parallel with a “a worker pool of child processes that run tests” (Jest CLI docs). As per the Jest documentation.
Each test file is making a new connection to the mogodb server which is causing the error.
You could run the Jest test sequentially to avoid this issue.
The --runInBand or -i option of the Jest CLI allows you to run tests sequentially (in non-parallel mode).
This error (pool is draining, new operations prohibited) might occur if you are using the official mongodb library for node js or mongoose. Please specifying pool size while establishing mongodb connection.
module.exports.connect = async () => {
const uri = await mongod.getConnectionString();
const mongooseOpts = {
useNewUrlParser: true,
autoReconnect: true,
reconnectTries: Number.MAX_VALUE,
reconnectInterval: 1000,
poolSize: 10,
};
await mongoose.connect(uri, mongooseOpts);
};
I did the same way and faced into the same error.
This was resolved by removing by schema name in test only like I did below in some.test.js
** Rest all setup remains same **
//example.it.test.js
const dbHandler = require("./db-handler");
const SomeModel = require("../Some");
const SomeOtherModel = require("../SomeOther");
beforeAll(async () => await dbHandler.connect());
afterEach(async () => {
await SomeModel.remove({});
await SomeOtherModel.remove({});
});
afterAll(async () => await dbHandler.closeDatabase());
describe("Some Test Block", () => {
it("Test One", async (done) => {
await SomeModel.create({a: "a", SomeOther: 'SomeOther Data' });
const data = await SomeModel.getAll();
expect(data.length).toBe(1);
done();
});
});
For every It test case, it connects to the mongoose, and if any error occurred, mongoose does not disconnect.
Solution
You can put the it test case in the try catch block, but if possible use 2 point
Try to handle the error in the controllers and service, and so on
files if possible.
And whenever you write test case, there should be no error comes from the development code.
Don't write nested describe in a single describe, max 2 nested
describe we should use.
One temporary solution, I skip the all describe expect outmost where mongoose was connected.
One more temporary solution, disconnect the mongod and then see the status, then reconnect.
I'm trying to implement a few e2e tests in my aurelia-cli app. I've tried looking for docs or blogs but haven't found anything on e2e setup for the cli. I've made the following adjustments to the project.
first I added this to aurelia.json
"e2eTestRunner": {
"id": "protractor",
"displayName": "Protractor",
"source": "test/e2e/src/**/*.ts",
"dist": "test/e2e/dist/",
"typingsSource": [
"typings/**/*.d.ts",
"custom_typings/**/*.d.ts"
]
},
Also added the e2e tasks on aurelia_project/tasks:
e2e.ts
import * as project from '../aurelia.json';
import * as gulp from 'gulp';
import * as del from 'del';
import * as typescript from 'gulp-typescript';
import * as tsConfig from '../../tsconfig.json';
import {CLIOptions} from 'aurelia-cli';
import { webdriver_update, protractor } from 'gulp-protractor';
function clean() {
return del(project.e2eTestRunner.dist + '*');
}
function build() {
var typescriptCompiler = typescriptCompiler || null;
if ( !typescriptCompiler ) {
delete tsConfig.compilerOptions.lib;
typescriptCompiler = typescript.createProject(Object.assign({}, tsConfig.compilerOptions, {
// Add any special overrides for the compiler here
module: 'commonjs'
}));
}
return gulp.src(project.e2eTestRunner.typingsSource.concat(project.e2eTestRunner.source))
.pipe(typescript(typescriptCompiler))
.pipe(gulp.dest(project.e2eTestRunner.dist));
}
// runs build-e2e task
// then runs end to end tasks
// using Protractor: http://angular.github.io/protractor/
function e2e() {
return gulp.src(project.e2eTestRunner.dist + '**/*.js')
.pipe(protractor({
configFile: 'protractor.conf.js',
args: ['--baseUrl', 'http://127.0.0.1:9000']
}))
.on('end', function() { process.exit(); })
.on('error', function(e) { throw e; });
}
export default gulp.series(
webdriver_update,
clean,
build,
e2e
);
and the e2e.json
{
"name": "e2e",
"description": "Runs all e2e tests and reports the results.",
"flags": []
}
I've added a protractor.conf file and aurelia.protractor to the root of my project
protractor.conf.js
exports.config = {
directConnect: true,
// Capabilities to be passed to the webdriver instance.
capabilities: {
'browserName': 'chrome'
},
//seleniumAddress: 'http://0.0.0.0:4444',
specs: ['test/e2e/dist/*.js'],
plugins: [{
path: 'aurelia.protractor.js'
}],
// Options to be passed to Jasmine-node.
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000
}
};
aurelia.protractor.js
/* Aurelia Protractor Plugin */
function addValueBindLocator() {
by.addLocator('valueBind', function (bindingModel, opt_parentElement) {
var using = opt_parentElement || document;
var matches = using.querySelectorAll('*[value\\.bind="' + bindingModel +'"]');
var result;
if (matches.length === 0) {
result = null;
} else if (matches.length === 1) {
result = matches[0];
} else {
result = matches;
}
return result;
});
}
function loadAndWaitForAureliaPage(pageUrl) {
browser.get(pageUrl);
return browser.executeAsyncScript(
'var cb = arguments[arguments.length - 1];' +
'document.addEventListener("aurelia-composed", function (e) {' +
' cb("Aurelia App composed")' +
'}, false);'
).then(function(result){
console.log(result);
return result;
});
}
function waitForRouterComplete() {
return browser.executeAsyncScript(
'var cb = arguments[arguments.length - 1];' +
'document.querySelector("[aurelia-app]")' +
'.aurelia.subscribeOnce("router:navigation:complete", function() {' +
' cb(true)' +
'});'
).then(function(result){
return result;
});
}
/* Plugin hooks */
exports.setup = function(config) {
// Ignore the default Angular synchronization helpers
browser.ignoreSynchronization = true;
// add the aurelia specific valueBind locator
addValueBindLocator();
// attach a new way to browser.get a page and wait for Aurelia to complete loading
browser.loadAndWaitForAureliaPage = loadAndWaitForAureliaPage;
// wait for router navigations to complete
browser.waitForRouterComplete = waitForRouterComplete;
};
exports.teardown = function(config) {};
exports.postResults = function(config) {};
and I added a sample test in my test/e2e/src folder it doesn't get executed. I've also tried implementing a e2e test within the unit test folder since when I run au test I see that a chrome browser opens up.
describe('aurelia homepage', function() {
it('should load page', function() {
browser.get('http://www.aurelia.io');
expect(browser.getTitle()).toEqual('Home | Aurelia');
});
});
But this throws the error browser is undefined. Am I missing something with e2e testing with the cli? I know aurelia-protractor comes pre-installed but I don't see any way to run it.
I know this is a very late answer, but perhaps for others looking for an answer, you could try to import from the aurelia-protractor plugin
import {browser} from 'aurelia-protractor-plugin/protractor';
OS: Windows 10
Node version: 0.10.36
Mocha global version: 1.21.4
I'm attempting to use mocha to unit-test my code, but a local variable inside the code I'm trying to test is persisting between tests, causing problems.
When I look inside require.cache, between tests, I don't see my module in there. It is my understanding that I should be clearing the cache if I want to reset this module between tests.
I made a small node project to demonstrate this issue:
package.js:
{
"name": "cache-test",
"version": "0.0.1",
"dependencies": {
"lodash": "4.5.0"
},
"devDependencies": {
"chai": "1.9.2",
"mocha": "1.21.4",
"mockery": "1.4.0",
"sinon": "1.10.3",
"app-root-path":"*"
}
}
module.js:
var foo = "default value";
exports.init = function(){
foo = 'init';
}
exports.returnFoo = function(){
return foo;
}
test/test-module.js
var chai = require("chai"),
expect = chai.expect,
mockery = require("mockery"),
appRoot = require('app-root-path');
var module;
describe("module", function () {
before(function () {
mockery.enable({ useCleanCache: true });
});
beforeEach(function () {
mockery.registerAllowable(appRoot + "/module", true);
module = require(appRoot + "/module");
});
afterEach(function () {
console.log('deleting', require.cache[require.resolve(appRoot + "/module")]);
delete require.cache[require.resolve(appRoot + "/module")];
module = null;
mockery.deregisterAll();
});
after(function () {
mockery.disable();
});
describe("test",function(){
it("foo should be 'init' after running init()",function(){
module.init();
console.log('foo is ',module.returnFoo());
expect(module.returnFoo()).to.equal('init');
});
it("foo should be 'default value' if init() is not run",function(){
console.log('foo is ',module.returnFoo());
expect(module.returnFoo()).to.equal("default value");
});
});
});
running mocha prints
module
test
foo is init
√ foo should be 'init' after running init()
deleting undefined
foo is init
1 failing
Oh, I needed to add
mockery.resetCache() to my afterEach function. That solved it.
It seems like the useCleanCache option, and deleting the entry from require.cache aren't compatible with each-other, as the former keeps it from appearing in the latter.
So it's either:
Don't use useCleanCache
Delete it "manually" from require.cache
OR
Use useCleanCache
Use resetCache()
but don't attempt to mix and match.
I developing a web program with TDD methodology. Therefore, tests are using Selenium WebDriver library. But I wonder my program doesn't work correctly. Following is source codes.
public/index.html:
<h1 class="hello">Hello, world!</h1>
test/test.js:
'use strict'
var assert = require('assert')
var webdriver = require('selenium-webdriver'),
By = webdriver.By,
until = webdriver.until
var driver;
before(() => {
driver = new webdriver.Builder()
.forBrowser('chrome')
.build()
})
describe('Index page', () => {
before(() => {
driver.get('http://localhost:8080/')
})
it('should show hello greetings', () => {
let hello = driver.findElement(By.css('h1.hello'))
assert.equal(hello.getText(), 'Hello, world!')
})
})
package.json:
{
"name": "foobar",
"version": "1.0.0",
"description": "",
"main": "index.js",
"devDependencies": {
"mocha": "^2.5.3"
"selenium-webdriver": "^2.53.2"
},
"scripts": {
"start": "http-server public",
"test": "mocha"
},
"author": "",
"license": "ISC"
}
And I've run following command.
C:\Projects\foobar>npm install
So, when I run all tests with npm test command, it always failed regardless to npm start command was run or not.
C:\Projects\foobar>npm test
> foobar#1.0.0 test C:\Projects\foobar
> mocha
Index page
1) should show hello greetings
0 passing (62ms)
1 failing
1) Index page should show hello greetings:
AssertionError: ManagedPromise {
flow_:
ControlFlow {
propagateUnhandledRejections_: true,
activeQueue_:
TaskQueue {
== 'Hello, world!'
at Context.<anonymous> (C:\Projects\foobar\test\test.js:22:16)
at callFn (C:\Projects\foobar\node_modules\mocha\lib\runnable.js:326:21)
at Test.Runnable.run (C:\Projects\foobar\node_modules\mocha\lib\runnable.js:319:7)
at Runner.runTest (C:\Projects\foobar\node_modules\mocha\lib\runner.js:422:10)
at C:\Projects\foobar\node_modules\mocha\lib\runner.js:528:12
at next (C:\Projects\foobar\node_modules\mocha\lib\runner.js:342:14)
at C:\Projects\foobar\node_modules\mocha\lib\runner.js:352:7
at next (C:\Projects\foobar\node_modules\mocha\lib\runner.js:284:14)
at Immediate._onImmediate(C:\Projects\foobar\node_modules\mocha\lib\runner.js:320:5)
npm ERR! Test failed. See above for more details.
I think the main issue is in hello.getText() snippet. The getText() method couldn't get text of hello element.
Most driver operations are asynchronous and return a promise, not the actual return value of that operation. In your case you should change findElement but also getText to work in that manner. Also, you should set a timeout since the default one is set to 2000 ms and most pages won't have finished loading by then. Try this:
'use strict'
var assert = require('assert');
var webdriver = require('selenium-webdriver');
var By = webdriver.By;
var until = webdriver.until;
var driver;
before(function () {
driver = new webdriver.Builder()
.forBrowser('chrome')
.build();
});
describe('Index page', function () {
this.timeout(5000);
before(function () {
driver.get('http://localhost:8080/');
});
it('should show hello greetings', function (done) {
driver.findElement(By.css('h1.hello'))
.then(elem => elem.getText() )
.then(text => assert.equal(text, 'Hello, world!') )
.then(done);
});
});
Note that arrow function don't define their own this object. You have to use regular callback functions to access this.timeout. Their use is, in any case, discouraged in mocha, see following:
Passing arrow functions to Mocha is discouraged. Their lexical binding of the this value makes them unable to access the Mocha context, and statements like this.timeout(1000); will not work inside an arrow function.