I'm new to using Jasmine and Grunt (and StackOverflow). I'm trying to setup the most basic of projects. However, when I run my grunt task, I get an error that says:
Running "jasmine:testShared" (jasmine) task
Testing jasmine specs via PhantomJS
>> Error: notloaded: Module name "../" has not been loaded yet for context: _. Use require([])
>> http://requirejs.org/docs/errors.html#notloaded at
>> ..\..\C:\Tests\jasmine\_SpecRunner.html:21
>> ..\..\C:\Tests\jasmine\.grunt\grunt-contrib-jasmine\require.js:12 v
>> ..\..\C:\Tests\jasmine\.grunt\grunt-contrib-jasmine\require.js:26 h
>> ..\..\C:\Tests\jasmine\.grunt\grunt-contrib-jasmine\require.js:31
>> ..\..\C:\Tests\jasmine\node_modules\glob\examples\g.js:1
>> Error: notloaded: Module name "../" has not been loaded yet for context: _. Use require([])
>> http://requirejs.org/docs/errors.html#notloaded at
>> ..\..\C:\Tests\jasmine\_SpecRunner.html:21
>> ..\..\C:\Tests\jasmine\.grunt\grunt-contrib-jasmine\require.js:12 v
>> ..\..\C:\Tests\jasmine\.grunt\grunt-contrib-jasmine\require.js:26 h
>> ..\..\C:\Tests\jasmine\.grunt\grunt-contrib-jasmine\require.js:31
>> ..\..\C:\Tests\jasmine\node_modules\glob\examples\usr-local.js:1
>> ReferenceError: Can't find variable: module at
>> ..\..\C:\Tests\jasmine\node_modules\glob\glob.js:37
>> Error caught from PhantomJS. More info can be found by opening the Spec Runner in a browser.
Warning: SyntaxError: Parse error Use --force to continue.
Aborted due to warnings.
I can't figure out what I'm doing wrong. The code for this project can be found here. My file structure looks like this:
/jasmine
/node_modules
/shared
/modules
myModule.js
/tests
/unit
myModule.tests.js
/tasks
/options
jasmine.js
protractor.js
test_task.js
_SpecRunner.html
gruntfile.js
package.json
My package.json file looks like this:
{
"name": "MyApp",
"version": "1.0.0",
"dependencies": {
"async": "~0.9.0",
"glob":"~4.0.3",
"grunt":"~0.4.0",
"grunt-cli":"~0.1.13",
"grunt-contrib-connect":"0.7.1",
"grunt-contrib-jasmine":"~0.6.5",
"grunt-protractor-runner":"1.0.1",
"grunt-start-webdriver":"0.0.2",
"grunt-template-jasmine-requirejs":"0.2.0",
"jasmine-core":"2.0.0",
"jasmine-node":"2.0.0-beta3",
"load-grunt-tasks":"0.2.x",
"lodash":"~2.4.1",
"phantomjs": "1.9.7-14",
"selenium-webdriver":"2.42.1",
"time-grunt":"~0.3.2"
}
}
My gruntfile.js file looks like this:
'use strict';
module.exports = function (grunt) {
var config = {
name: 'MyApp',
pkg: grunt.file.readJSON('package.json'),
baseDir: '.'
};
// load all grunt task details
require('load-grunt-tasks')(grunt);
// show elapsed time at the end
require('time-grunt')(grunt);
// load task definitions
grunt.loadTasks('tasks');
// Utility function to load plugin settings into config
function loadConfig(config,path) {
require('glob').sync('*', {cwd: path}).forEach(function(option) {
var key = option.replace(/\.js$/,'');
// If key already exists, extend it. It is your responsibility to avoid naming collisions
config[key] = config[key] || {};
grunt.util._.extend(config[key], require(path + option)(config,grunt));
});
}
// Merge that object with what with whatever we have here
loadConfig(config,'./tasks/options/');
grunt.loadNpmTasks('grunt-start-webdriver');
grunt.loadNpmTasks('grunt-protractor-runner');
// pass the config to grunt
grunt.initConfig(config);
};
myModule.js looks like this:
'use strict';
var _ = require('lodash');
_.mixin({
'myFunction' : function(s) {
return 'Hello ' + s;
}
});
module.exports = _;
myModule.tests.js looks like this:
'use strict';
describe('myModule', function() {
it('should work', function() {
expect(true).toBe(true);
});
});
test_task.js looks like this:
module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-jasmine');
grunt.registerTask('test-shared', [
'jasmine:testShared'
]);
};
jasmine.js looks like this:
module.exports = function(config) {
return {
testShared: {
src: "shared/modules/*.js",
options: {
specs: "shared/tests/unit/**.tests.js",
vendor: "node_modules/**/*.js",
template: require('grunt-template-jasmine-requirejs')
}
}
};
};
protractor.js looks like this:
module.exports = function(config) {
return {
testShared: {
options: {
configFile: "shared/tests/unit/config.js"
}
}
};
};
I've spent two days on this. I have no idea what I'm doing wrong. I feel like I've followed the examples online. I feel like there is something stupid I'm overlooking. Can someone please show me what I'm doing wrong?
It's very hard to diagnose without a running example but that error is caused by RequireJS. It happens when you do require('something') without having loaded it. Loading is asynchronous so you either need to do require['lodash'], function(_) { do stuff }. Or the preferred define syntax:
define([
'lodash'
], function(_) {
'use strict';
_.mixin({
myFunction: function(s) {
return 'Hello ' + s;
}
});
return _;
});
With a test you would typically use the same structure and pull in the module you are testing in the define. You would set it up so the path to required files is the same as in the app.
To help more we need a running example.
My colleague made a good boilerplate using grunt/jasmine/requirejs maybe you can get some good practices from there: https://github.com/mderrick/backbone-boilerplate
Your directory structure is quite confusing have you considered simplifying it?
- Use a single Gruntfile.js - don't fragment unless your project is very big
index.html
Gruntfile.js
app/
test/
That is all you really need to get going. In app it could look something like:
app/app.js <-- entry point
app/modules/...
Related
I am trying to transfer an old node-express project over to be able to use es6. I have seen many posts about using gulp with es6. Most of them discuss using a syntax like this:
const gulp = require("gulp");
const babel = require("gulp-babel");
gulp.src('./index.js')
.pipe(
babel({
presets: [
["#babel/env", { modules: false }],
],
})
)
However my existing project's gulpfile does't use gulp.src at all. Instead, it uses gulp-develop-server. The gulpfile looks like this:
const gulp = require("gulp");
const devServer = require("gulp-develop-server");
const spawn = require("child_process").spawn;
const fs = require("fs");
const basedir = ".";
function serverRestart(done) {
// perform some cleanup code here
devServer.restart();
done();
}
function serverStart() {
devServer.listen({
path: basedir + "/index.js",
});
}
function serverWatch() {
serverStart();
gulp.watch(
[
basedir + "/paths/**/*",
// more directories to watch
],
serverRestart
);
}
function reload(done) {
serverWatch();
done();
}
function defaultTask() {
let p;
gulp.watch(["gulpfile.js"], killProcess);
spawnChild();
function killProcess(e) {
if (p && !p.killed) {
devServer.kill();
p.kill("SIGINT");
spawnChild();
}
}
function spawnChild() {
p = spawn("gulp", ["reload"], { stdio: "inherit" });
}
}
process.stdin.resume();
process.on("exit", handleExit.bind(null, { cleanup: true }));
process.on("SIGINT", handleExit.bind(null, { exit: true }));
process.on("uncaughtException", handleExit.bind(null, { exit: true }));
function handleExit(options, err) {
// perform some cleanup code here
if (options.cleanup) {
devServer.kill();
}
if (err) {
console.log(err.stack);
}
if (options.exit) {
process.exit();
}
}
gulp.task("serverRestart", serverRestart);
gulp.task("serverStart", serverStart);
gulp.task("serverWatch", serverWatch);
gulp.task("reload", reload);
gulp.task("default", defaultTask);
The existing flow is important because it executes needed code for setup and cleanup every time I hit save, which runs serverRestart. I've been trying a few different methods based on the other questions which recommended using gulp.src().pipe(), but I havne't had much luck integrating it with the existing pattern which uses gulp-develop-server. I am trying to not have to rewrite the whole gulpfile. Is there a simple way to integrate babel with my existing gulpfile such that I can use es6 in my source code?
There's an example with CoffeeScript in the gulp-develop-server documentation.
Using that as a model, try this:
function serverStart() {
devServer.listen({
path: "./dist/index.js",
});
}
function serverWatch() {
serverStart();
gulp.watch(
[
basedir + "/paths/**/*",
],
serverRestart
);
}
function serverRestart() {
gulp.src('./index.js')
.pipe(
babel({
presets: [
["#babel/env", { modules: false }],
],
})
)
.pipe( gulp.dest( './dist' ) )
.pipe( devServer() );
}
Other suggestions
That being said, your existing Gulp file doesn't actually really use Gulp. That is, everything is defined as a function and it doesn't leverage any of Gulp's useful features, like managing task dependencies. This is because (pre-es6), this was a very simple project. The Gulp tasks in that file are an over-elaborate way to watch files and run a server. The same could be done (with less code) using nodemon.
With the introduction of React and more complicated build processes, Gulp seems to have fallen out of favor with the community (and in my personal experience, Gulp was a time sinkhole anyhow).
If the main change you want to make is to use import, you can simply use a more recent Node version. You'll surely run into the error SyntaxError: Cannot use import statement outside a module. Simply rename the file to .mjs and it will work. This provides a way to incrementally migrate files to import syntax. Other features should automatically work (and are all backwards-compatible, anyhow). Once your project is mostly, or all, compliant, you can add "type": "module" to your package.json file, then rename all of your require-style js files to .cjs, and rename all of your .mjs files to .js, or leave them as .mjs. Read more about the rules of mixing CommonJS and Module imports in the Node.js blog post (note that some things may have changed since that article was written).
I'm trying to load test script according to custom argument passed through start command to start intern test.
To do this I'm trying to require the specific test script inside a test but i am getting Attempt to require unloaded module error.
This is my code set up. Can someone help on on this or sugest some alternative work around to make this work.
define(function (require) {
var intern = require('intern');
var AdvalentAutomationTestSuite = require('intern!object');
AdvalentAutomationTestSuite({
name: 'Advalent Automation Test',
'AdvalentTestSets': function () {
return this.remote
.then(function () {
var product = intern.args.product;
var script = 'Automation/TestScripts/FRG/' + product + '-Config';
require(script)
})
},
});
});
Update:
Including intern.js file:
define(function (require) {
var intern = require('intern');
console.log(intern)
return {
proxyPort: 9000,
proxyUrl: 'http://localhost:9000/',
defaultTimeout: 120000,
capabilities: {
'selenium_version': '2.48.2',
},
environments: [
{browserName: 'chrome', version: '48', platform: ['WINDOWS'], chromeOptions: {args: ['start-maximized']}},
],
maxConcurrency: 3,
tunnel: 'NullTunnel',
reporters: [
{id: 'JUnit', filename: 'test-reports/report.xml'},
{id: 'Runner'},
],
Loaders: {
'host-node': 'dojo/dojo',
'host-browser': 'node_modules/dojo/dojo.js'
},
loaderOptions: {
packages: [{name: 'intern-tutorial', location: '.'}]
},
functionalSuites: [
'Automation/TestScripts/FRG/FRG-Config',
],
defaultTimeout: 70000,
excludeInstrumentation: /^(?:tests|node_modules)\//
}
});
You should be fine with the default loader, although as #Troopers points out, it's loaders, not Loaders. The problem is that you're doing a dynamic require with a computed name:
var script = 'Automation/TestScripts/FRG/' + product + '-Config';
require(script)
AMD loaders don't completely support the require(script) syntax since they don't load modules synchronously. When a module is written in CJS-compatibility mode, the loader fakes it by scanning the module code for require calls and then preloading and caching the modules before executing the module code. When the require(script) call is eventually executed, the preloaded module is returned.
When you use a computed module name, the loader can't preload the module being required, so the synchronous require call will fail. To load a module with a computed name you'll need to use the require([ dependency ]) syntax, like:
var script = 'Automation/TestScripts/FRG/' + product + '-Config';
return new Promise(function (resolve) {
require([ script ], resolve);
});
At a higher level, though, it seems odd to be doing this in a test in the first place. It seems like something that should be handled at the module or config levels. For example, assuming 'Automation/TestScripts/FRG/' + product + '-Config' is a functional test suite, the config could simply add that suite to the functionalSuites list if the required command line argument were provided.
You need to specify a loader in your configuration file :
loaders: {
"host-node": "requirejs",
"host-browser": "node_modules/requirejs/require.js"
},
And install the npm package requirejs
The documentation is here
After few hit and trial I managed to make it work by changing my intern.js as follows:
define(function (require) {
var intern = require('intern');
var product = intern.args.product
return {
functionalSuites: [
'Automation/TestScripts/FRG/' + product + '-Config.js',
],
// rest of config code ...
}
});
Please suggest if there's any better way to do this.
Is there any way to import a requirejs config in to my grunt config file? Right now I have to keep two identical versions, one in app/main.js and one in my Gruntfile.js:
module.exports = function(grunt) {
// can I import app/main.js requireConfig here?
var requireConfig = {
paths: {
jquery: 'lib/jquery'
// etc...
}
};
});
My main.js looks something like this:
requirejs.config({
paths: {
jquery: 'lib/jquery'
// etc...
}
});
define(['app'], function(app){
app.start();
});
You can use standard module pattern which supports different type of module system like following.
Your requirejs config file like this
amd-config.js
(function(factory) {
if (typeof define === 'function' && define.amd) {
// Register as an AMD module if available...
define('amd-config', [], factory());
} else if (typeof exports === 'object') {
// Next for Node.js, CommonJS, browserify...
module.exports = factory();
} else {
// setting browser global when none of the above are available
window.amdConfig = factory();
}
}
(function() {
var amdConfig = {
baseUrl: 'scripts',
paths: {
//Paths here
}
};
return amdConfig;
}));
In gruntfile you can just require like any other module.
var requireConfig = require('amd-config');
Include it normally like you do in index.html with script tag before app.js
and then in app.js use it like following.
requirejs.config(window.amdConfig);
define(['app'], function(app){
app.start();
});
PS: There are cleaner way of including it in app.js.
More cleaner than second, create global variable require and include the script before requirejs script. requirejs checks if there is global variable with name require containing object. If its there, it is used as a config object. So you dont have to call requirejs.config yourself.
You can require the file like you require other files. In that case it will be treated as a require module and you will receive the object in require callback. call your requirejs.config like following.
```
require(['amd-config'], function(amdConfig){
requirejs.config(amdConfig);
require(['app'], function(app){
app.start();
});
});
```
A simpler approach you could use, if you are using grunt to build the project. You can simply use:
options:{
mainConfigFile: "path/to/Config.js"
}
granted you need to use:
https://github.com/gruntjs/grunt-contrib-requirejs
You can try something like this:
function getRequireConfig(requireFilePath) {
var config;
var configFileContent,
_require;
_require = require;
require = {
data: {},
config : function (configParam) {
this.data = configParam;
},
get : function () {
return this.data;
}
};
configFileContent = readFileSync(requireFilePath);
eval(configFileContent);
config = require.get();
require = _require;
return config;
}
What it is doing is:
Override require definition to a custom implementation
Load require config file
Eval it so that the config function of custom implementation will be
called Get the config object from data
I want to build a quick nodejs script to package a Typescript app as SystemJS modules, a lot like what Angular2 bundles look like.
I tried different configurations but I can't seem to put my finger on it, and haven't found clear enough documentation as of yet.
Note that for this "test", I am not using Gulp or Jspm at all, just systemjs-builder for the time being (and don't plan on using jspm at all either)
Here's what my "project" looks like:
---- Project's Root
-------- index.ts // export * from './modules/index' and eventually more
-------- modules
------------ index.ts // export * from './menu/index'
------------ menu
---------------- menu.component.ts // export class
---------------- menu.service.ts // export class
I want to package this under a single file, where I will have multiple SystemRegister modules that can be consumed in an app thereafter
I tried the following without success:
var Builder = require('systemjs-builder');
// optional constructor options
// sets the baseURL and loads the configuration file
var builder = new Builder('./modules');
builder.bundle('./modules/index.ts', {
/* SystemJS Configuration Here */
baseURL: './modules',
transpiler: 'typescript',
typescriptOptions: {
"module": "system",
"emitDecoratorMetadata": true,
"experimentalDecorators": true
},
defaultExtension: 'ts',
packages: {
'modules': {
defaultExtension: 'ts'
}
}
}, 'infrastructure.js')
.then(function() {
console.log('Build complete');
})
.catch(function(err) {
console.error(err);
})
First of all, the defaultExtension options doesn't seem to work at all
So when I do import {something} from 'filePath'; (without extension), it tries to load filePath, instead of filePath.ts;
Second, if I try adding the .ts extension in my imports (which I don't want to do), it complains that the code is invalid (unexpected token #, unexpected token menuItem and so forth)
Anyone have a good example or some explanations on how this is supposed to work?
Thank you
here you have an example: angular typescript skeleton
build task looks like this:
const path = require('path');
const Builder = require('jspm').Builder;
const builder = new Builder();
const packageJson = require(path.join(config.projectDir, 'package.json'));
return beginBuild()
.then(buildSFX)
.catch((err) => console.log('Build Failed', err));
function beginBuild() {
builder.reset();
return builder.loadConfig(path.join(config.projectDir, packageJson.jspm.configFile))
}
function buildSFX() {
const appName = packageJson.name;
const distFileName = `${appName}.min.js`;
const outFile = path.join(config.distDir, distFileName);
const moduleName = 'app';
const buildConfig = {
format: 'global',
minify: true,
sourceMaps: true
};
return builder.buildStatic(moduleName, outFile, buildConfig);
}
and jspm conf looks like this:
System.config({
defaultJSExtensions: true,
transpiler: "typescript",
typescriptOptions: {
"tsconfig": "src/tsconfig.json"
},
paths: {
"github:*": "vendor/jspm_packages/github/*",
"npm:*": "vendor/jspm_packages/npm/*",
"app": "src/index"
}
/// ...
}
Why do you want to bundle typescript? Bundling is a method used for optimizing the delivery of source code to the browser. The browser doesn't know typescript, it only knows javascript (unless you do on the fly transpiling).
I've got a config for requirejs that is shared with multiple applications. When the app is loaded, the app receives an object with the layout of the app (and various other params). I want to use that object to define modules/packages for the app.
siteMap = { modules: { "foo": { … }, "bar": { … }, … }, other: "stuff" }
/shared/
libs/
bootstrap.js
jquery.js
…
app.js
/$appName/
foo/
index.html
edit.html
main.js
bar/
index.html
stuff.html
main.js
…
I imagine there must be a way to add packages that goes something like this:
// /shared/app.js
require.config({
paths: {
"bootstrap": ["//hosted.bootstrap.js","/shared/libs/bootstrap.js"],
"jquery": ["//hosted.jquery.js","/shared/libs/jquery.js"],
"siteMap": "//appName.example.com/api/siteMap"
},
…
});
require(['jquery','siteMap','bootstrap'], function($,siteMap) {
for ( var module in siteMap.modules )
{
require.config.packages[module] = siteMap.modules[module];
// OR
require.addPackage(siteMap.modules[module]);
}
});
You could nest another require() call to load the additional modules after siteMap tells you what should be loaded. Something like this:
require(['jquery','siteMap','bootstrap'], function($,siteMap) {
// .map assumes modern browser or use of something like es5-shim
var modulesToLoad = siteMap.modules.map(function(m) { return m + '/main'; } );
require(modulesToLoad, function() {
// kick off the rest of app logic
});
});