I have a generator-generator app with its own local sub-generator app. The situation is that I want the sub-generator to invoke atleast once with pre-defined argument when I run the main 'app' generator. Everything works fine except the npm test fails.
Main 'app' generator: yo mygen.
Sub generator: yo mygen:foo "lorem ipsum".
Here is the test.js
/*global describe, beforeEach, it*/
'use strict';
var path = require('path'),
yg = require('yeoman-generator');
var helpers = require('yeoman-generator').test;
var assert = require('yeoman-assert');
describe(' running `yo mygen`', function () {
before(function (done) {
var deps = [
[helpers.createDummyGenerator(), 'mygen:foo', 'blah blah blah']
];
helpers.run(path.join(__dirname, '../app'))
.inDir(path.join(__dirname, './temp')) // Clear the directory and set it as the CWD
.withOptions({ mongoose: 'app', 'skip-install': true }) // Mock options passed in
.withPrompts({
'dbName': 'demo',
'useUserAuth': false
})
.withGenerators(deps)
.on('end', done);
//done();
});
it('can be imported without blowing up', function () {
var app = require('../app');
assert(app !== undefined);
});
it('creates all required MVC files', function (done) {
var expected = [
// add files you expect to exist here.
'package.json',
'app.js',
'bower.json',
'routes/index.js',
'public/css/style.css',
'public/js/script.js',
'views/index.html',
'README.md',
'.editorconfig',
'.jshintrc'
];
assert.file(expected);
done();
});
});
describe('í ¼ running `yo mygen:foo`', function () {
before(function (done) {
helpers.run(path.join(__dirname, '../schema'))
.inDir(path.join(__dirname, './temp')) // Clear the directory and set it as the CWD
.withOptions({ mongoose: 'schema' }) // Mock options passed in
.withArguments(['ha ha ha ha hha'])
.on('end', done);
//done();
});
describe('foo generator', function () {
it('foo can be imported without blowing up', function () {
var app = require('../foo');
assert(app !== undefined);
});
it('created new MVC files for foo', function (done) {
var expected = [
// add files you expect to exist here.
'view/t1.js',
'models/t1.js',
'controller/t1.js'
];
assert.file(expected);
done();
})
});
});
And in app/index.js, in order to invoke the sub-generator, i have used:
mygenGenerator.prototype.install = function install(){
this.installDependencies();
this.composeWith("mygen:foo", {args: ["humpty dumpty saton a wall"]});
};
Searched all possible answers on stackoverflow and everywhere else. Can't figure out what to do.
Test Cases for npm test fails:
Remove the composeWith line from index.js and the test passes.
keep the composeWith line, the test goes to infinity, eventually exceed 2000ms quota and failing.
Related
Config file
this is my conf.js file
// An example configuration file.
exports.config = {
directConnect: true,
// Capabilities to be passed to the webdriver instance.
capabilities: {
'browserName': 'chrome'
},
// Framework to use. Jasmine is recommended.
framework: 'jasmine',
// Spec patterns are relative to the current working directory when
// protractor is called.
specs: ['example_spec.js'],
// Options to be passed to Jasmine.
jasmineNodeOpts: {
defaultTimeoutInterval: 30000
}
};
Example Spec.js
is there anything wrong with the code here should i add some type of wait time
describe('angularjs homepage', function() {
it('should greet the named user', function() {
browser.get('http://www.angularjs.org');
element(by.model('yourName')).sendKeys('Julie');
var greeting = element(by.binding('yourName'));
expect(greeting.getText()).toEqual('Hello Julie!');
});
describe('todo list', function() {
var todoList;
beforeEach(function() {
browser.get('http://www.angularjs.org');
todoList = element.all(by.repeater('todo in todoList.todos'));
});
it('should list todos', function() {
expect(todoList.count()).toEqual(2);
expect(todoList.get(1).getText()).toEqual('build an AngularJS app');
});
it('should add a todo', function() {
var addTodo = element(by.model('todoList.todoText'));
var addButton = element(by.css('[value="add"]'));
addTodo.sendKeys('write a protractor test');
addButton.click();
expect(todoList.count()).toEqual(3);
expect(todoList.get(2).getText()).toEqual('write a protractor test');
});
});
});
Error Code
this is the error I am getting
This seems to be a common issue with protractor if any one can assist me with a fix to get this test running please do
Thanks
Mike,
I'm bundling my javascript files using the task runner gulp js, during the development of an application I'm noticing a certain issue.
When I add the new feature(reveal a password) script refuses to work because of the form modal script which can be seen below.
'use strict';
var modal__button = document.getElementById("enquiry-form-trigger");
var close__button = document.getElementById("close");
modal__button.addEventListener("click", function (){
var modal = document.getElementById("modal-form");
modal.classList.add("fadeIn");
modal.style.visibility = "visible";
});
close__button.addEventListener("click", function (){
var modal = document.getElementById("modal-form");
modal.classList.remove("fadeIn");
modal.style.visibility = "hidden";
});
When the above script and this other script below
"use strict"
document.getElementById("password-reveal-modal").addEventListener("click", function (){
var x = document.getElementById("password-modal");
if (x.type === "password") {
x.type = "text";
} else {
x.type = "password";
}
});
The password reveal feature doesn't work, but when I paste it in chrome's dev tools works perfectly.
I'm not sure why it wont work without pasting it into the dev tools, it's baffling me, I'm not sure if its my setup or if its the custom javascript.
This is my gulp file setup in case anyone wants to check it, I don't see an issue but Ive only been using gulp for about 3 or 4 months.
var gulp = require("gulp"),
sass = require("gulp-sass"),
image = require("gulp-image"),
concat = require("gulp-concat"),
browserSync = require('browser-sync').create(),
reload = browserSync.reload,
minifyEjs = require("gulp-minify-ejs"),
stripEJSComments = require('gulp-strip-comments'),
nodemon = require('gulp-nodemon'),
plumber = require("gulp-plumber"),
ejs = require("ejs"),
uglify = require("gulp-uglify");
//Build task
gulp.task("build", ["ejs", "styles", "images", "javascript", "routes", "models", "middleware"], function () {
console.log("Build Success");
});
//start up sequence tasks
gulp.task('init', ["nodemon"], function () {
browserSync.init({
proxy: 'http://localhost:2000', //Index.js port number
port: 2128, // The port browser sync runs on
serveStatic: [ './public/', "./assets/"], // What files browser sync should have access to
reloadOnRestart: true, // Enable auto reload
ghostMode:false, //Stops session mirroring
open: "external", //Opens up on an external address (xxx.xxx.x.xx:3128)
});
});
//Starts the express server
gulp.task('nodemon', function (done) {
var running = false; //Default State
return nodemon({
script: 'index.js', //Index file for the JS project
watch: ["./assets/", "./public/"] //What nodemon has access to
})
.on('start', function () {
if (!running) {
done();
}
running = true;
})
//Minor Delay Of 500ms Upon Restart
.on('restart', function () {
setTimeout(function () {
reload();
}, 500);
});
});
//SCSS Task
gulp.task("styles", function () {
gulp.src("./assets/stylesheet/APP.scss")
.pipe(plumber())
.pipe(sass({
outputStyle: 'compressed'
}))
.pipe(gulp.dest("./public/stylesheet/"))
.pipe(browserSync.stream({ stream: true }));
});
//Compiles the express route/s
gulp.task("routes", function () {
gulp.src([
"./assets/routes/*.js"
])
.pipe(plumber())
.pipe(gulp.dest("./public/routes/"))
.pipe(browserSync.stream({ stream: true }));
});
//Compiles the express route/s
gulp.task("models", function () {
gulp.src("./assets/models/*.js")
.pipe(plumber())
.pipe(gulp.dest("./public/models/"))
.pipe(browserSync.stream({ stream: true }));
});
//Image minification
gulp.task("images", function () {
return gulp.src("./assets/images/*")
.pipe(image())
.pipe(gulp.dest("./public/images"))
.pipe(browserSync.stream({ stream: true }));
});
//Client javascript
gulp.task("javascript", function () {
gulp.src([
"./node_modules/jquery/dist/jquery.js",
"./node_modules/tether/dist/js/tether.js",
"./node_modules/bootstrap/dist/js/bootstrap.js",
"./assets/scripts/**/**/*.js"
])
.pipe(plumber())
.pipe(concat("main.js"))
.pipe(gulp.dest("./public/scripts/"))
.pipe(browserSync.stream({ stream: true }));
});
//Middleware task
gulp.task("middleware", function () {
gulp.src("./assets/middleware/*.js")
.pipe(plumber())
.pipe(concat("index.js"))
.pipe(gulp.dest("./public/middleware/"))
.pipe(browserSync.stream({ stream: true }));
});
//EJS task
gulp.task("ejs", function () {
gulp.src("./assets/views/**/*.ejs")
.pipe(plumber())
.pipe(stripEJSComments())
.pipe(minifyEjs({}))
.pipe(gulp.dest("./public/views"))
.pipe(browserSync.stream({ stream: true }));
});
//Default task array
gulp.task("default", ["init", "build"], function (done) {
gulp.watch("./assets/stylesheet/**/*.scss", ["styles"]);
gulp.watch("./assets/scripts/*", ["javascript"]);
gulp.watch("./assets/routes/*.js", ["routes"]);
gulp.watch("./assets/models/*.js",["models"]);
gulp.watch("./assets/images/*",["images"]);
gulp.watch("./assets/views/**/*.ejs",["ejs"]);
browserSync.reload();
done();
});
The two files which are causing the issue are the only files as I have tested each files and its only these two files weirdly so something is causing it to clash.
If you want me to upload my project to github just let me know and I will upload the latest version to my update branch.
I have exhausted all my knowledge into this problem and I am completely stuck now.
If anyone could help a fellow developer out it would be greatly appreciated.
cheers,
alex
I needed to ensure the DOM had fully loaded before the script could be executed.
I am currently trying to get unit testing working with Angular2 final and karma + jasmine.
I have the following problem:
TypeError: Cannot read property 'injector' of null if don't add: TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting())
.configureTestingModule({
declarations: [],
providers: [Stuff],
imports: [Stuff]
});
To my test.
But I can only call the initTestEnvironment and configureTestingModule once, so more than 1 test is not possible. And I'd like to prevent having an init test.
Here is my karma-test-shim.js
// #docregion
// /*global jasmine, __karma__, window*/
Error.stackTraceLimit = 0; // "No stacktrace"" is usually best for app testing.
// Uncomment to get full stacktrace output. Sometimes helpful, usually not.
// Error.stackTraceLimit = Infinity; //
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
var builtPath = '/base/app/';
__karma__.loaded = function () { };
function isJsFile(path) {
return path.slice(-3) == '.js';
}
function isSpecFile(path) {
return /\.spec\.(.*\.)?js$/.test(path);
}
function isBuiltFile(path) {
return isJsFile(path) && (path.substr(0, builtPath.length) == builtPath);
}
var allSpecFiles = Object.keys(window.__karma__.files)
.filter(isSpecFile)
.filter(isBuiltFile);
System.config({
baseURL: '/base',
// Extend usual application package list with test folder
packages: { 'testing': { main: 'index.js', defaultExtension: 'js' } },
// Assume npm: is set in `paths` in systemjs.config
// Map the angular testing umd bundles
map: {
'#angular/core/testing': 'npm:#angular/core/bundles/core-testing.umd.js',
'#angular/common/testing': 'npm:#angular/common/bundles/common-testing.umd.js',
'#angular/compiler/testing': 'npm:#angular/compiler/bundles/compiler-testing.umd.js',
'#angular/platform-browser/testing': 'npm:#angular/platform-browser/bundles/platform-browser-testing.umd.js',
'#angular/platform-browser-dynamic/testing': 'npm:#angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js',
'#angular/http/testing': 'npm:#angular/http/bundles/http-testing.umd.js',
'#angular/router/testing': 'npm:#angular/router/bundles/router-testing.umd.js',
'#angular/forms/testing': 'npm:#angular/forms/bundles/forms-testing.umd.js',
},
});
System.import('systemjs.config.js')
.then(importSystemJsExtras)
.then(initTestBed)
.then(initTesting);
/** Optional SystemJS configuration extras. Keep going w/o it */
function importSystemJsExtras(){
return System.import('systemjs.config.extras.js')
.catch(function(reason) {
console.log(
'WARNING: System.import could not load "systemjs.config.extras.js"; continuing without it.'
);
console.log(reason);
});
}
function initTestBed(){
return Promise.all([
System.import('#angular/core/testing'),
System.import('#angular/platform-browser-dynamic/testing')
])
.then(function (providers) {
var coreTesting = providers[0];
var browserTesting = providers[1];
console.log("call initTestEnvironment")
coreTesting.TestBed.initTestEnvironment(
browserTesting.BrowserDynamicTestingModule,
browserTesting.platformBrowserDynamicTesting());
console.log("call configure teting module")
coreTesting.TestBed.configureTestingModule({
declarations: [],
providers: [],
imports: []
})
})
}
// Import all spec files and start karma
function initTesting () {
return Promise.all(
allSpecFiles.map(function (moduleName) {
return System.import(moduleName);
})
)
.then(__karma__.start, __karma__.error);
}
I thought calling the initTestEnvironment in the test shim is enough. I am surprised that I the call in the karma-test-shim.js seems to have no effect.
package.json and code are in a related question: AsyncTestCompleter Browserify Angular2 HTTP Mock Test
Thank you so much for your help.
(I asked this question recently and accepted an answer but it's still not what I need.) I really need to create dynamic tests from data loaded from a module. Each item from the array will have it's own describe statement with certain protractor actions. My previous post has an answer that says to use an it statement, but I can't do that because there's too much going on.
My main problem is that the data doesn't get loaded in time for the describe. I had another suggestion to use VCR.js or something similar but I don't think those will work because I'm using a module. Is there a way I can save the data to a separate file and load it in? Would that be a good way to go?
var data = require('get-data'); //custom module here
describe('Test', function() {
var itemsArr;
beforeAll(function(done) {
data.get(function(err, result) {
itemsArr = result; //load data from module
done();
});
})
//error: Cannot read property 'forEach' of undefined
describe('check each item', function() {
itemsArr.forEach(function(item) {
checkItem(item);
});
});
function checkItem (item) {
var itemName = item.name;
describe(itemName, function() {
console.log('describe');
it('should work', function() {
console.log('it');
expect(true).toBeTruthy();
});
});
}
});
UPDATE:
I used Eugene's answer and came up with this. I can't test each individual study how I want because the it statement doesn't fire. Is this problem even solvable??
describe('check each item', function () {
it('should load data', function (done) {
browser.wait(itemsPromise, 5000);
itemsPromise.then(function(itemsArr) {
expect(itemsArr).toBeTruthy();
studyArr = itemsArr.filter(function (item) {
return item.enabled && _.contains(item.tags, 'study');
});
studyCount = studyArr.length;
expect(studies.count()).toEqual(studyCount);
checkItems(studyArr);
done();
});
});
function checkItems (itemsArr) {
itemsArr.forEach(function (item) {
describe(item.id, function () {
console.log('checkItems', item.id);
// doesn't work
it('should work', function (done) {
expect(false).toBeTruthy();
done();
});
});
});
}
});
You're trying to do something that Jasmine does not allow: generating tests after the test suite has started. See this comment on an issue of Jasmine:
Jasmine doesn't support adding specs once the suite has started running. Usually, when I've needed to do this, I've been able to know the list of options ahead of time and just loop through them to make the it calls. [...]
("adding specs" === "adding tests")
The point is that you can generate tests dynamically but only before the test suite has started executing tests. One corollary of this is that the test generation cannot be asynchronous.
Your second attempt does not work because it is trying to add tests to a suite that is already running.
Your first attempt is closer to what you need but it does not work either because describe calls its callback immediately, so beforeAll has not run by the time your describe tries to generate the tests.
Solutions
It all boils down to computing the value of itemsArr before the test suite start executing tests.
You could create a .getSync method that would return results synchronously. Your code would then be something like:
var data = require('get-data'); //custom module here
var itemsArr = data.getSync();
describe('Test', function() {
describe('check each item', function() {
itemsArr.forEach(function(item) {
checkItem(item);
});
});
[...]
If writing .getSync function is not possible, you could have an external process be responsible for producing a JSON output that you could then deserialize into itemsArr. You'd execute this external process with one of the ...Sync functions of child_process.
Here's an example of how the 2nd option could work. I've created a get-data.js file with the following code which uses setTimeout to simulate an asynchronous operation:
var Promise = require("bluebird"); // Bluebird is a promise library.
var get = exports.get = function () {
return new Promise(function (resolve, reject) {
var itemsArr = [
{
name: "one",
param: "2"
},
{
name: "two",
param: "2"
}
];
setTimeout(function () {
resolve(itemsArr);
}, 1000);
});
};
// This is what we run when were are running this module as a "script" instead
// of a "module".
function main() {
get().then(function (itemsArr) {
console.log(JSON.stringify(itemsArr));
});
};
// Check whether we are a script or a module...
if (require.main === module) {
main();
}
Then, inside the spec file:
var child_process = require('child_process');
var itemsArr = JSON.parse(child_process.execFileSync(
"/usr/bin/node", ["get-data.js"]));
describe('Test', function() {
itemsArr.forEach(function(item) {
checkItem(item);
});
function checkItem (item) {
var itemName = item.name;
describe(itemName, function() {
console.log('describe');
it('should work', function() {
console.log('it');
expect(true).toBeTruthy();
});
});
}
});
I've tested the code above using jasmine-node. And the following file structure:
.
├── data.js
├── get-data.js
└── test
└── foo.spec.js
./node_modules has bluebird and jasmine-node in it. This is what I get:
$ ./node_modules/.bin/jasmine-node --verbose test
describe
describe
it
it
Test - 5 ms
one - 4 ms
should work - 4 ms
two - 1 ms
should work - 1 ms
Finished in 0.007 seconds
2 tests, 2 assertions, 0 failures, 0 skipped
Try to use a promise, something like:
var deferred = protractor.promise.defer();
var itemsPromise = deferred.promise;
beforeAll(function() {
data.get(function(err, result) {
deferred.fulfill(result);
});
})
And then:
describe('check each item', function() {
itemsPromise.then(function(itemsArr) {
itemsArr.forEach(function(item) {
checkItem(item);
});
});
});
Another solution I can think of is to use browser.wait to wait until itemsArr becomes not empty.
Is your get-data module doing some browser things with protractor? If so, you will need to set/get itemsArr within the context of the controlFlow. Otherwise it will read all the code in the get-data module, but defer its execution and not wait for it to finish before moving right along to those expect statements.
var data = require('get-data'); //custom module here
var itemsArr;
describe('Test', function() {
beforeAll(function() {
// hook into the controlFlow and set the value of the variable
browser.controlFlow().execute(function() {
data.get(function(err, result) {
itemsArr = result; //load data from module
});
});
});
//error: Cannot read property 'forEach' of undefined
describe('check each item', function() {
// hook into the controlFlow and get the value of the variable (at that point in time)
browser.controlFlow().execute(function() {
itemsArr.forEach(function(item) {
checkItem(item);
});
});
});
function checkItem (item) {
var itemName = item.name;
describe(itemName, function() {
console.log('describe');
it('should work', function() {
console.log('it');
expect(true).toBeTruthy();
});
});
}
});
I've got a very simple yeoman generator, watchjs, that has speaker subgenerator. Below is hos it is used:
$ yo watchjs:speaker
You called the watch.js speaker subgenerator.
? Speaker file: data/speakers/speakers.json
? Speaker name: abc
{ file: 'data/speakers/speakers.json', name: 'abc' }
Generated slug is: abc
Trying to add: {
"id": "abc",
"name": "abc"
}
Mainly, there are two prompts: file - which defines the json file where data should be appended to and name - which defines actual data to be added to the file (slightly modified). I'm trying to write a simple yeoman test for this. I've been trying to follow the docs, but I'm failing all the time:
$ npm test
> generator-watchjs#0.0.2 test c:\Users\tomasz.ducin\Documents\GitHub\generator-watchjs
> mocha
Watchjs:speaker
{ file: 'speakers.json', name: 'John Doe' } // <- this is my console.log
1) "before all" hook
0 passing (59ms)
1 failing
1) Watchjs:speaker "before all" hook:
Uncaught Error: ENOENT, no such file or directory 'C:\Users\TOMASZ~1.DUC\AppData\Local\Temp\53dac48785ddecb6dabba402eeb04f91e322f844\speakers.json'
at Object.fs.openSync (fs.js:439:18)
at Object.fs.readFileSync (fs.js:290:15)
at module.exports.yeoman.generators.Base.extend.writing (c:\Users\tomasz.ducin\Documents\GitHub\generator-watchjs\speaker\index.js:43:33)
npm ERR! Test failed. See above for more details.
I can't understand where is the file actually created and where are the tests looking for it... There seems to be used a temporary windows location, but anyway, if all things work properly relative to the path, the file should have been found and it's not. Can't figure out what to do to make tests pass.
The best content of my test file is:
'use strict';
var path = require('path');
var assert = require('yeoman-generator').assert;
var helpers = require('yeoman-generator').test;
describe('watchjs:speaker', function () {
before(function (done) {
helpers.run(path.join(__dirname, '../speaker'))
.withOptions({ 'skip-install': true })
.withPrompts({ 'file': 'speakers.json', 'name': "John Doe" })
.on('end', done);
});
it('creates files', function () {
assert.file([
'speakers.json'
]);
});
});
I'm passing a specific name and file name via prompt.
I've found out that npm test call package.json's mocha command (and that's it). But I'm not an expert in mocha.
I'm using node v0.10.35 on Windows7.
First, you should use absolute paths in your test, so the location of the file is predictable.
My test would look something like this:
'use strict';
var fs = require('fs');
var path = require('path');
var assert = require('yeoman-generator').assert;
var helpers = require('yeoman-generator').test;
describe('watchjs:speaker', function () {
before(function (done) {
var self = this;
var name = 'John Doe';
var testPath = path.join(__dirname, 'temp');
// store in test obejct for later use
this.filePath = path.join(testPath, 'speaker.json');
helpers.run(path.join(__dirname, '../speaker'))
.inDir(testPath)
.withPrompts({ 'file': self.filePath, 'name': name })
.withOptions({ 'skip-install': true })
.on('end', done);
});
it('creates files', function () {
assert.file(this.filePath);
assert.fileContent(this.filePath, /\"id\":.*\"john-doe\"/);
assert.fileContent(this.filePath, /\"name\":.*\"John Doe\"/);
});
});
Second, and not directly related to your question, the test above will on the code in the repo you shared. Like I mentioned in my comment, it throws an error here if the file doesn't already exist.
I would change:
var content = JSON.parse(fs.readFileSync(this.options.file, 'utf8'));
to:
try {
var content = JSON.parse(fs.readFileSync(this.options.file, 'utf8'));
} catch(e) {
content = [];
}
With the change above, the test will pass.