I currently have a Hybrid Angular app (2.4.9 and 1.5.0) using angular-cli. Currently, when running our application, we are able to bootstrap the 1.5 app correctly:
// main.ts
import ...
platformBrowserDynamic().bootstrapModule(AppModule).then(platformRef => {
angular.element(document).ready(() => {
const upgrade = platformRef.injector.get(UpgradeModule) as UpgradeModule;
upgrade.bootstrap(document.body, ['myApp'], {strictDi: true});
});
});
However, in our test.ts file:
// test.ts
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import ...;
declare var __karma__: any;
declare var require: any;
__karma__.loaded = function () {};
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
// I'm assuming that I need to call 'boostrapModule()' somehow here...
platformBrowserDynamicTesting()
);
const context = require.context('./', true, /\.spec\.ts$/);
context.keys().map(context);
__karma__.start();
I'm not exactly sure how to bootstrap our 1.5 application into the test environment, all I've gotten is Module 'myApp' is not available!, and my Google skills have failed trying to find an example.
I was hoping the bounty I added last night would mean I could log on this morning to a nice solution laid out for me. Alas, it did not. So instead I spent the day cruising around many SO answers and github issues getting it to work. I'm sorry I did not keep track of everything that helped me to credit them, but here is my solution. It is probably not ideal, but it is working so far so I hope it is a good start.
This github issue indicates that downgradeComponent isn't going to work for now, so I went with what I assume is an older technique using UpgradeAdapter. Note that this technique does not use initTestEnvironment. Here are the relevant snippets, with some explanations below:
// downgrade.ts:
export const componentsToDowngrade = {
heroDetail: HeroDetailComponent,
...
};
export function downgradeForApp() {
forOwn(componentsToDowngrade, (component, name) => {
app.directive(name!, downgradeComponent({ component }));
});
}
// main.ts:
downgradeForApp();
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory).then((platformRef) => {
...
});
// test.ts:
require("../src/polyfills.ts");
require("zone.js/dist/proxy");
require('zone.js/dist/sync-test');
require("zone.js/dist/mocha-patch");
// test-helper.ts
let upgradeAdapterRef: UpgradeAdapterRef;
const upgradeAdapter = new UpgradeAdapter(AppModule);
forEach(componentsToDowngrade, (component, selectorName) => {
angular.module("app").directive(
selectorName!,
upgradeAdapter.downgradeNg2Component(component) as any,
);
});
export function useAdaptedModule() {
beforeEach(() => {
upgradeAdapterRef = upgradeAdapter.registerForNg1Tests(["app"]);
});
}
export function it(expectation: string, callback: () => void) {
test(expectation, (done) => {
inject(() => { }); // triggers some kind of needed initialization
upgradeAdapterRef.ready(() => {
try {
callback();
done();
} catch (ex) { done(ex); }
});
});
}
// hero-detail.component.spec.ts
import { it, useAdaptedModule } from "test-helpers/sd-app-helpers";
describe("", () => {
useAdaptedModule();
it("behaves as expected", () => { ... });
});
A few of the highlights from that code:
I downgrade components differently for tests than for the app, so I made a DRY list of them in downgrade.ts
I downgrade components for the main app from main.ts by calling downgradeForApp() as shown above (used with AOT for a production bundle), and also main-jit.ts, not shown above (used for development)
I showed the imports I needed to add to start integrating Angular components into my AngularJS tests. You may need more/different ones depending e.g. on whether your tests are asynchronous, or you use Jasmine instead of Mocha.
At the beginning of each test that needs to use downgraded components, I "bootstrap" things with useAdaptedModule() instead of beforeEach(angular.mock.module("app"));
I import an alternative it from my helpers, which wraps the it provided by Mocha. None of my tests are asynchronous; if you have some that are it may require tweaking. I do not know how it may need to be adapted for Jasmine.
A caveat: Instantiating the component must happen within an it callback so that it happens within upgradeAdapterRef.ready(...). Trying to do it within a beforeEach is too soon.
Related
After runned my test with this artist-detail.spec.ts
import { HttpClientModule } from '#angular/common/http';
import { ComponentFixture, TestBed } from '#angular/core/testing';
import { RouterModule } from '#angular/router';
import { RouterTestingModule } from '#angular/router/testing';
import { ArtistDetailComponent } from './artist-detail.component';
describe('ArtistDetailComponent', () => {
let component: ArtistDetailComponent;
let fixture: ComponentFixture<ArtistDetailComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
RouterTestingModule,
RouterModule,
HttpClientModule
],
declarations: [ ArtistDetailComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ArtistDetailComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
I have got this output
27 04 2022 18:23:06.651:INFO [launcher]: Starting browser ChromeHeadless
27 04 2022 18:23:09.795:INFO [Chrome Headless 100.0.4896.127 (Linux x86_64)]: Connected on socket iATQ5FHffdjRWSTWAAAB with id 92608559
Chrome Headless 100.0.4896.127 (Linux x86_64) ERROR: 'DEPRECATION: describe with no children (describe() or it()) is deprecated and will be removed in a future version of Jasmine. Please either remove the describe or add children to it.
Error:
at
at Env.jasmineEnv. [as describe] (http://localhost:9876/karma_webpack/webpack:/node_modules/zone.js/fesm2015/zone-testing.js:454:1)
at
at Object.4911 (http://localhost:9876/karma_webpack/webpack:/src/app/modules/artist/artist-detail/artist-detail.component.spec.ts:8:1)
at webpack_require (http://localhost:9876/karma_webpack/webpack:/webpack/bootstrap:19:1)
Note: This message will be shown only once. Set the verboseDeprecations config property to true to see every occurrence.'
Chrome Headless 100.0.4896.127 (Linux x86_64) ERROR: 'DEPRECATION: describe with no children (describe() or it()) is deprecated and will be removed in a future version of Jasmine. Please either remove the describe or add children to it.
Error:
at
at Env.jasmineEnv. [as describe] (http://localhost:9876/karma_webpack/webpack:/node_modules/zone.js/fesm2015/zone-testing.js:454:1)
at
at Object.4911 (http://localhost:9876/karma_webpack/webpack:/src/app/modules/artist/artist-detail/artist-detail.component.spec.ts:8:1)
at webpack_require (http://localhost:9876/karma_webpack/webpack:/webpack/bootstrap:19:1)
Note: This message will be shown only once. Set the verboseDeprecations config property to true to see every occurrence.'
Chrome Headless 100.0.4896.127 (Linux x86_64) ERROR: 'DEPRECATION: describe with no children (describe() or it()) is deprecated and will be removed in a future version of Jasmine. Please either remove the describe or add children to it.
Error:
at
at Env.jasmineEnv. [as describe] (http://localhost:9876/karma_webpack/webpack:/node_modules/zone.js/fesm2015/zone-testing.js:454:1)
at
at Object.4911 (http://localhost:9876/karma_webpack/webpack:/src/app/modules/artist/artist-detail/artist-detail.component.spec.ts:8:1)
at webpack_require (http://localhost:9876/karma_webpack/webpack:/webpack/bootstrap:19:1)
Note: This message will be shown only once. Set the verboseDeprecations config property to true to see every occurrence.'
ExhibitionDetailComponent
✔ should create
How can I fix the issue ERROR: 'DEPRECATION'
Thanks a lot
This answer doesn't directly answer the question above, but it may help others that encounter the error, since I kept ending up here when searching for a solution.
The error:
ERROR: 'DEPRECATION: describe with no children (describe() or it())
This can happen if an error occurs before the code hits the inner describe or it functions. So the test code never reaches the inner functions.
In my case, I was upgrading from Jasmine v1.3.1 to Jasmine v4 and I had the below code. Note that I had the incorrect Jasmine API call for andReturn on a spied object. So the test failed at that point before it could reach the inner describe function.
define(function(require) {
'use strict';
return describe('Sample test', function () {
var saveMock = jasmine.createSpy('save').andReturn($.Deferred().resolve());
describe('test', function() {
it('should be true', function() {
expect(true).toEqual(true);
});
});
});
});
Fixing the API call, then fixed the actual issue and stopped the error describe with no children from being thrown, since it could reach the inner describe
var saveMock = jasmine.createSpy('save').and.returnValue($.Deferred().resolve());
The above deprecation warning does not tell you which test has the problem. It is just like a summary warning saying that one of the test(s) have this problem.
To found out which tests need to be fixed please add verboseDeprecations: true for jasmine in karma.conf.js config file (see below)
client: {
jasmine: {
....
verboseDeprecations: true,
...
},
...
},
Then run all or required unit tests and see the name of each of the tests that has describe with no children problem and fix those (add children or remove those describe).
Also had same error. Solved by specifying function ìt. Since you have 2x beforeEach functions I'd suggest writing it like
it('should create', () => {
const fixture = TestBed.createComponent(ArtistDetailComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
For me, I had a beforeEach and it blocks inside of the main beforeEach.
Similar to below:
beforeEach(() => {
TestBed.configureTestingModule({});
beforeEach(() => {});
it('abc', () => {});
it('def', () => {});
});
I changed it to:
beforeEach(() => {
TestBed.configureTestingModule({});
});
beforeEach(() => {});
it('abc', () => {});
it('def', () => {});
And the issue went away.
WCT is great for testing your custom web components, but it requires spinning up a browser to execute the tests.
I am looking for a way to test my components outside of a browser. I looked into jsdom, but it does not currently support Polymer. Any other suggestions?
In case anyone else is following this question, I have made some progress with jsdom.
The trick thus far has been in the created() method:
created: function (error, window) {
window.document.createRange = function () { }
window.getSelection = function () { }
window.Range = function () { }
window.Selection = function () { }
window.CanvasRenderingContext2D = function () { } // Object.getPrototypeOf(require("canvas")(0,0).getContext("2d")) might be better
window.SVGUseElement = window.HTMLUnknownElement
},
I'm working on building an extensible automated test suite with Protractor (angularJS/Jasmine framework).
As long as all of my variables and functions and jasmine are in the same file, it runs semi-okay.
But every effort I make to break it into export/require is a nightmare.
Is there a tool that will just find the parts of my test and automatically reformat it and break it into individual files and folders, so that the thing will actually run?
Thanks!
I don´t know a tool for what you want. However if I were you, I would keep working with node's way of sharing files (export/require). Once you understand it, if you keep it clean and tidy, you could grow your app in a "clean" way.
EDIT:
As #MBielski said, Page Objects Model is also helpful when maintaining you test code.
Definition from the Selenium team:
Page Object is a Design Pattern which has become popular in test
automation for enhancing test maintenance and reducing code
duplication. A page object is an object-oriented class that serves as
an interface to a page of your AUT. The tests then use the methods of
this page object class whenever they need to interact with that page
of the UI. The benefit is that if the UI changes for the page, the
tests themselves don’t need to change, only the code within the page
object needs to change. Subsequently all changes to support that new
UI are located in one place.
And now an example without using page objects and then one using it.
Without:
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!');
});
});
With:
var AngularHomepage = function() {
var nameInput = element(by.model('yourName'));
var greeting = element(by.binding('yourName'));
this.get = function() {
browser.get('http://www.angularjs.org');
};
this.setName = function(name) {
nameInput.sendKeys(name);
};
this.getGreeting = function() {
return greeting.getText();
};
};
describe('angularjs homepage', function() {
it('should greet the named user', function() {
var angularHomepage = new AngularHomepage();
angularHomepage.get();
angularHomepage.setName('Julie');
expect(angularHomepage.getGreeting()).toEqual('Hello Julie!');
});
});
You can also define various test suites. Take a look at this config file:
exports.config = {
seleniumAddress: 'http://localhost:4444/wd/hub',
capabilities: {
'browserName': 'chrome'
},
// Spec patterns are relative to the location of the spec file. They may
// include glob patterns.
suites: {
homepage: 'tests/e2e/homepage/**/*Spec.js',
search: ['tests/e2e/contact_search/**/*Spec.js',
'tests/e2e/venue_search/**/*Spec.js']
},
jasmineNodeOpts: {
showColors: true, // Use colors in the command line report.
}
};
As ReactJS is only view layer and works by his own, which additional libraries to use for for full stack ReactJS suite - data layer, comunication with server (AJAX calls, REST), etc. when building SPA (single page application)?
Are they any ReactJS full stack frameworks (something like AngularJS) available?
ReactJS alone gives you DOM rendering, but Facebook also created Flux which gives you an architecture in which to work. By following the rules set out by Flux, you now have a SPA with DOM rendering, data models, and communication between the two.
Of course, the SPA that you'll build with Flux is self-contained. Flux does not go so far as to provide you with tools to perform AJAX requests. You will need another library for that. However, the NodeJS community is so full of AJAX implementations, I might actually prefer it.
superagent is a pretty popular one. (It's what I use.) You might notice it doesn't support promises, so you might also check out superagent-bluebird-promise, which wraps superagent with the bluebird promise library.
Another note, if you're going to use Flux, I recommend also pulling in one of the growing number of wrapper libraries that will help you reduce the boilerplate. Check out Reflux.
A full cycle might look like this...
RecordList.jsx
const React = require('react');
const Reflux = require('reflux');
const RecordStore = require('../stores/RecordStore');
const RecordActions = require('../actions/RecordActions');
const RecordList = React.createClass({
mixins: [
// auto-magically create event listeners to state change to re-render
Reflux.connect(RecordStore)
],
// There is no `getInitialState()` here, but the one in `RecordStore` is inherited.
// load the initial data
componentDidMount: function () {
RecordActions.load();
},
// render records as a list
render: function () {
return (
<li>
{
this.state.records.map(function (record) {
return <ul>{record.name}</ul>;
})
}
</li>
);
}
});
module.exports = RecordList;
RecordActions.js
const Reflux = require('reflux');
const request = require('superagent-bluebird-promise');
const RecordActions = Reflux.createActions({
// create an action called 'load' and create child actions of 'completed' and 'failed'
load: {asyncResult: true}
});
// set up promise for loading records
RecordActions.load.listenAndPromise(() =>
request.get('/records')
.type('application/json')
.then(res => res.body)
);
module.exports = RecordActions;
RecordStore.js
const Reflux = require('reflux');
const RecordActions = require('../actions/RecordActions');
/**
* storage for record data
*/
const RecordStore = Reflux.createStore({
// listen for events from RecordActions (Reflux)
listenables: RecordActions,
init: function () {
this.data = {
records: []
};
},
// facilitate initializing component state with store data
getInitialState: function () {
return this.data;
},
/*
* all records
*/
getRecords: function () {
return this.data.records;
},
// handle successful load of records
onLoadCompleted: function (response) {
this.data.records = response;
this.trigger(this.data);
},
// handle failure to load records
onLoadFailed: function (err) {
console.error('Failed to load records', err.toString());
}
});
module.exports = RecordStore;
U could look at
http://martyjs.org/ which is an implementation of the Flux Application Architecture.
(es6 support/React native support/ Higher order components (containers: https://medium.com/#dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750))
You may want to search on GitHub for "react starter kits". A popular technology stack for building SPAs with React consists of:
a Flux implementation -> you can choose one from here https://github.com/voronianski/flux-comparison
React Router https://github.com/rackt/react-router
Webpack + Webpack Dev Server, Babel, Gulp, ESLint + CSSLint, LESS / Sass etc.
Jest / Mocha + Chai + Sinon
Netflix Falcor https://github.com/Netflix/falcor (developer preview)
some React components http://material-ui.com / https://react-bootstrap.github.io / http://react-components.com
As for starter kits, here's an interesting list of React boilerplates http://habd.as/awesome-react-boilerplates
You could also check MERN (MongoDB, Express, ReactJS, NodeJs) full stack at mern.io. I have been using it and it has been awesome stack. It comes with Webpack, Redux and React-Router and other basic frameworks.
I'm getting weird weird behaviour when rendering a component using an AMD-loaded React, when a global React already exists on the page. Click events on components are getting fired when they should not be.
A look at the DOM implies that this stems from multiple React instances (one global, one AMD in my case) not being aware of each other, but this poses a problem when loading an AMD module at runtime that depends on React, into a page that also includes React.
How can I resolve this clash?
Reproduction
I can make a component like this:
var ButtonComponent = React.createClass({
onButtonClick: function(){
alert(this.props.data + ' click event fired');
},
render: function() {
return React.DOM.button({onClick: this.onButtonClick}, this.props.data);
}
});
(function(){ // create vanilla
var ButtonList = React.createClass({
render: function() {
return React.DOM.div({}, React.createElement(ButtonComponent, {data: this.props.data}));
}
});
React.render(React.createElement(ButtonList, {data: 'button that was loaded by the page'}), document.getElementById('page-load-target'));
})();
jsbin
But as soon as I add another component using another instance of React then click the first button, then it calls the click event on the second loaded button:
// .... as above ....
(function(){ // create using amd
require.config({
paths: {
'react': '//fb.me/react-with-addons-0.12.2.min'
}
});
window.setTimeout(function(){
require(['react'], function(ReactFromAmd){
ReactFromAmd.render(ReactFromAmd.createElement(ButtonComponent, {data: 'button that was loaded by AMD'}), document.getElementById('amd-load-target'));
});
}, 1000)
})();
jsbin
If I use the existing, global version of React in this call (rather than ReactFromAmd, then it works as expected. jsbin
The ancestors (React instance) of ButtonComponent and the component created with ReactFromAmd.createElement are different, and yet they are in the same virtual DOM -- That's not allowed.
If you don't mind replacing AMD with browserify, I just figured out a way to let isolated/remotely-loaded React components co-exist nicely.
(To be continued if someone needs it)
This has been fixed in version 0.14.2: http://jsbin.com/tesodoxape/1/edit?html,js,output