Updated: I did not use any module loader, because this is old project, emm, so I just import all dependencies in my index.html via script tag
My AngularJS has a structure like this:
app.js
angular.module('app', ['LocalStorageModule', 'ngCookies', ...])
testController
angular.module('app').controller('testController', function(){})
now I want to test testController, so my jest UT code:
testController.spec.js
require('./testController.controller')
describe('TestController', () => {
beforeEach(angular.mock.module('app'));
})
but now I got an error:
Module 'app' is not available
that means I must import app.js, but if I import app.js, I also got
Failed to instantiate module LocalStorageModule due to: Module 'LocalStorageModule' is not available!
So I have to import all my dependencies(twenty an more installed by bower) in my every test file? I think it isn't a good way. How to handle this solution? import all my components installed by bower?
The problem seems to be the angular.js dependency injection. Because you have some dependencies declared in your app module. You need to mock your app module and then you need to inject the dependencies.
describe('TestController', () => {
beforeEach(
angular.mock.module('app')
);
let _localStorageModule;
let _ngCookies;
beforeEach(
inject((LocalStorageModule, ngCookies) => {
_localStorageModule = LocalStorageModule;
_ngCookies = ngCookies;
})
);
})
Related
I am trying to import a module from a file outside my /cypress directory into the /cypress/integration directory's test.spec.js file like so:
import { LAB_MODEL } from '../../models/search/lab-model';
But inside this imported module "LAB_MODEL" there are other files being imported using the "##" at the start of the file imports like
import ExperimentDetails from "##/components/experiment/ExperimentDetails";
and I think this is why Cypress isn't working and giving me this error:
Error: Webpack Compilation Error
./models/search/lab-model.js
Module not found: Error: Can't resolve '##/components/experiment/ExperimentDetails' in '/Users/hidden/models/search'
resolve '##/components/experiment/ExperimentDetails' in '/Users/hidden/models/search'
Parsed request is a module
using description file: /Users/hidden/package.json (relative path: ./models/search)
Field 'browser' doesn't contain a valid alias configuration
resolve as module
So I think this is the reason why my test won't run, but I have no idea how to make Cypress recognize "##" imports and can't find any documentation/stackoverflow answers, any help is appreciated, thanks!
##/ looks like something that gets translated into a full path during the Nuxt build.
(ref The alias Property).
Cypress has a separate build that doesn't know about this Nuxt feature. You could try to replicate it with some webpack config via a preprocessor, but another way is to have your Nuxt app put a reference to lab_model on the window object
// somewhere in the Nuxt app
if (window.Cypress) {
window.lab_model = LAB_MODEL;
}
then at the top of the test
const lab_model = cy.state('window').lab_model;
This has the benefit of giving you the exact same instance of lab_model, in case you wanted to stub something.
In a starter Nuxt app, I added the code window.lab_model = LAB_MODEL in /pages/index.vue, but you can add it in any component that imports it, right after the import statement.
In the spec add a .should() to test the property exists, to allow the app time to settle.
it('gets labModel from the Nuxt app', () => {
cy.visit('http://localhost:3000/')
cy.window()
.should('have.property', 'lab_model') // retries until property appears
.then(labModel => {
console.log(labModel)
// test with labModel here
})
})
I have a project that is using ng-metadata (https://github.com/ngParty/ng-metadata) to build a handful of Angular 1.5 modules. I have a test module/component that looks like this:
import { NgModule, Component, Inject, Input, Output, EventEmitter } from 'ng-metadata/core'
import { platformBrowserDynamic } from 'ng-metadata/platform-browser-dynamic'
#Component({
selector: 'test',
template: require('./test.template.html')
})
class TestComponent {
#Input() type: string;
constructor() {
console.log(`test: ${this.type}`)
}
}
#NgModule({
declarations: [TestComponent]
})
class HeroModule {}
platformBrowserDynamic().bootstrapModule(HeroModule)
Everything seems happy when compiled and I'm now attempting to use the module in another project (that is not using ng-metadata but has a compatible version of Angular).
I'm simply including the shims as directed by the ng-metadata docs and the JavaScript file that contains the module described above (built by webpack). I have a new module in this project that wants to list the HeroModule as a dependency. I've tried a few things:
// attempt 1:
angular.module('my-consuming-module', ['ui.router', 'hero'])
// attempt 2:
angular.module('my-consuming-module', ['ui.router', 'heroModule'])
// attempt 3:
angular.module('my-consuming-module', ['ui.router', 'hero-module'])
All always end up with the same Error: $injector:nomod Module Unavailable error from Angular.
If I'm using ng-metadata to build my modules, what are the names I use to list them as dependencies in another project?
Finally figured this out! It's amazing what happens when you carefully read documentation...
Found in the Manual Angular 1 Bootstrap section of ng-metadata's docs:
You can still leverage ng2 way of components registration without ng-metadata bootstrap, but you have to manually create your Angular 1 module from an ng-metadata #NgModule using the bundle helper function.
I ended up being able to do the following:
// REMOVED platformBrowserDynamic().bootstrapModule(HeroModule)
const Ng1AdminModule = bundle(HeroModule).name;
export const AppModule = angular.module('hero', [Ng1AdminModule]);
And then my hero module becomes accessible to the my-consuming-module just as I expected. The bundle helper function was the key to figuring this out.
You need to import those module from their respective locations and inject it inside your angular module
//ensure `angular`, `angular-ui-router` should be there in `map` of systemjs.config.js
import * as angular from 'angular';
import * as uiRouter from 'angular-ui-router';
import { heroModule} from './hero.module';
angular.module('app',[ uiRouter, heroModule]);
Check references here
I'm trying to start a new Angular 1 Application based on ES6. I use webpack and the babel-loader to convert the JS.
My problem now is to load an own config module. Please have a look at this:
// config/config.js
import angular from 'angular';
export default angular.module('config')
.factory('config', () => {
return {
url: {
products: 'https://....'
},
products: []
}
})
The corresponding app.js reads (I stripped some imports):
import angular from 'angular';
import config from './config/config';
import HomeCtrl from './controller/HomeController';
let app = () => {
return {
template: require('./app.html')
}
};
const MODULE_NAME = 'app';
angular.module(MODULE_NAME, [uiRouter, config])
.directive('myapp', app)
.config(['$stateProvider', '$urlRouterProvider', 'config', function ($stateProvider, $urlRouterProvider, config) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state("home", {
"url": "/",
"template": require('./views/home.html'),
"controller": HomeCtrl,
'controllerAs': 'app'
})
}]);
export default MODULE_NAME;
The error message says:
Uncaught Error: [$injector:nomod] Module 'config' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
What did I missed here? Is there a better way to load an application wide config object to use in certain services?
Thanks for help!
When you create a module, you have to call module() with two args: the name and the dependencies. If you call it with only one parameter, you get the existing module with that name.
Change your module config declaration to:
export default angular.module('config', [])
.factory('config', () => {
return {
url: {
products: 'https://....'
},
products: []
}
}).name
In addition, I always export only the .name of a new module. When you import the module, you just need its name.
Hope it helps.
When you declare your dependent modules in your main module definition you need to use the string identifier, not the actual angular.module (at least here it's specified as Array[String]: https://docs.angularjs.org/api/ng/function/angular.module
So, you should change to this:
angular.module(MODULE_NAME, ['config'])
Please check Angular documentation - module dependencies it's array of strings, but not dependent modules types or instances
angular.module(name, [requires], [configFn]);
when
requires (optional) Array<string>
So your solution to use next declaration
angular.module(MODULE_NAME, ['config'])
https://docs.angularjs.org/api/ng/function/angular.module
I'm trying to get lazy-loaded Angular modules working with Webpack, but I'm having some difficulties. Webpack appears to generate the split point correctly, because I see a 1.bundle.js getting created that contains the code for the child app, but I don't see any request for 1.bundle.js when I load the page, and the child app doesn't initialize. The console.log never seems to fire, and it doesn't even appear to get to the point where $oclazyload would initialize the module.
There are a few points where I am confused.
1) Will webpack make the request to the server, or do I have to load the second bundle manually? (I've tried both, and neither works)
2) If I do need to load the bundles manually, in what order should they be loaded?
3) The third argument to require.ensure supposedly lets you control the name of the bundle, but the bundle is named 1.bundle.js no matter what string I pass.
4) Why can't I step through the code inside the require.ensure block in the debugger? When I do so I end up looking at this in the Chrome source view:
undefined
/** WEBPACK FOOTER **
**
**/
(Code Below)
Main entry point code:
'use strict';
import angular from 'angular';
import 'angular-ui-router';
import 'oclazyload';
angular.module('parentApp', [
'ui.router',
])
.config(['$urlRouterProvider', '$locationProvider', ($urlRouterProvider, $locationProvider) => {
$urlRouterProvider
.otherwise('/');
$locationProvider.html5Mode(true);
}])
.config(['$stateProvider', ($stateProvider) => {
$stateProvider
.state('child-app', {
url: '/child-app',
template: '<child-app></child-app>',
resolve: {
loadAppModule: ($q, $ocLazyLoad) => {
return $q((resolve) => {
require.ensure(['./child-app/app.js'], (require) => {
let module = require('./child-app/app.js');
console.log(module);
$oclazyload.load({name: 'childApp'});
resolve(module.controller);
});
})
}
},
controller: function() {
}
})
}]);
Child app:
'use strict';
import childAppTemplateURL from '../templates/child-app.html';
import childAppController from './controllers/childAppController';
export default angular.module('parentApp.childApp', [])
.component('applicationListApp', {
templateUrl: childAppTemplateURL,
controller: childAppController
});
The problem was unrelated to the require.ensure implementation. It was caused by some weirdness in the way ocLazyLoad is packaged (https://github.com/ocombe/ocLazyLoad/issues/179). The fix in my case was simple, I just added 'oc.lazyLoad' to the module dependencies.
angular.module('parentApp', [
'ui.router',
'oc.lazyLoad'
])
To answer two of my own questions, Webpack does indeed make a request to the server for the bundle, and you do not have to manually load the bundle. One gotcha that really confused me: the resolve block will fail silently if it contains a promise that won't resolve. In my case $ocLazyLoad.load() was failing, but there was no indication of the failure. The only clue was that the state provider wasn't adding the <child-app></child-app> markup to the DOM, which meant that it was actually initializing the state.
I am brand new to typescript, typings. Vaguely understand type definition and trying to setup angular 1.5 project with typescript and angular material design.
I have angular material type definition in typings>globals>angular-material.
I don't know what module I can import and where to check. If i put like the following example, i get an exception: Module 'material' is not available!
import * as angular from 'angular';
import * as material from 'angular-material';
angular.module('app.services', []);
angular.module('app', ['app.services', 'material']);
The top portion of index.d.ts in typings>globals>angular-material is
declare module 'angular-material' {
var _: string;
export = _;
}
declare namespace angular.material { ...}
Thanks in advance.
It should be like this,
angular.module('app.services', []);
angular.module('app', ['app.services', 'ngMaterial']);
You should not import from 'angular-material'
just add a reference to typings :
/// <reference path="../typings/angular-material/angular-material.d.ts" />
(be sure to install typings for angular-materials first)
then use :
angular.module('app.services', []);
angular.module('app', ['app.services', 'ngMaterial']);
You should remove the single quotes when defining material provider for example
import * angular from 'angular';
import * as material from 'angular-material';
angular.module('app.services', []);
angular.module('app', ['app.services', material]);
This will solve your issue.
Also for importing material style sheet you can use following code.
import 'angular-material/angular-material.css';
For libraries that don't have type definitions you can use the following code.
import * as angular from 'angular';
require('../../../node_modules/angular-messages');
angular.module('app.services', []);
angular.module('app', ['app.services', 'ngMessages']);
Note : Here you should use string for defining provider
Small, I hope useful, addition (the experience I faced recently). If you need to use angular-material in TS unit tests, you will need to import it into spec file too (whereas in main code you only need to import it once and set as dependency in main module). Then it's sufficient to import 'angular-material'; at the top, and add variable for necessary service with corresponding typings, e.g. let dialog:ng.material.IDialogService;