I have create an Angular2 app (+Webpack). I added a component which shows an Openlayers3 map.It also adds a few markers and a polygon. I am reasonable happy with the functionality, but writing a test for it nearly proves impossible.
This is the test source code: ( I have not added any expect statements yet. The test seems to break when Openlayers tries to render the map.
/* tslint:disable:no-unused-variable */
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {DebugElement, NO_ERRORS_SCHEMA} from '@angular/core';
import * as ol from 'openlayers';
import {OlComponent} from './ol.component';
let comp: OlComponent;
describe('OlComponent', () => {
let fixture: ComponentFixture<OlComponent>;
let component: OlComponent;
let element: any;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
OlComponent
],
schemas: [NO_ERRORS_SCHEMA],
})
}))
it('should display the map', () => {
TestBed.overrideComponent(OlComponent, {
set: {
template: '<div id="map" style="width:10px;height:10px"></div>'
}
});
fixture = TestBed.createComponent(OlComponent);
console.log(fixture);
component = fixture.componentInstance;
element = fixture.nativeElement;
console.log(element);
let map = element.querySelector('#map');
map.style.width = "100px";
map.style.height = "100px";
component.ngOnInit();
fixture.detectChanges
});
});
All my efforts so far have only produced this error message when running the test (see source code(s) below.
OlComponent
✖ should display the map
Chrome 53.0.2785 (Linux 0.0.0)
TypeError: Cannot read property 'length' of undefined
at Kc (webpack:///~/openlayers/dist/ol.js:43:372 <- karma-shim.js:70246:398)
at Object.fromLonLat (webpack:///~/openlayers/dist/ol.js:768:188 <- karma-shim.js:70971:196)
at OlComponent.createMap (webpack:///src/app/ol-maps/ol.component.ts:9:1914 <- karma-shim.js:70159:1929)
at OlComponent.ngOnInit (webpack:///src/app/ol-maps/ol.component.ts:9:6364 <- karma-shim.js:70159:6369)
at Object.<anonymous> (webpack:///src/app/ol-maps/ol.component.spec.ts:33:0 <- karma-shim.js:53653:19)
at ZoneDelegate.invoke (webpack:///~/zone.js/dist/zone.js:232:0 <- karma-shim.js:40990:26)
at ProxyZoneSpec.onInvoke (webpack:///~/zone.js/dist/proxy.js:79:0 <- karma-shim.js:40654:39)
at ZoneDelegate.invoke (webpack:///~/zone.js/dist/zone.js:231:0 <- karma-shim.js:40989:32)
at Zone.run (webpack:///~/zone.js/dist/zone.js:114:0 <- karma-shim.js:40872:43)
at Object.<anonymous> (webpack:///~/zone.js/dist/jasmine-patch.js:102:0 <- karma-shim.js:40369:34)
at webpack:///~/@angular/core/bundles/core-testing.umd.js:91:0 <- karma-shim.js:3519:21
at ZoneDelegate.invoke (webpack:///~/zone.js/dist/zone.js:232:0 <- karma-shim.js:40990:26)
at AsyncTestZoneSpec.onInvoke (webpack:///~/zone.js/dist/async-test.js:49:0 <- karma-shim.js:39959:39)
at ProxyZoneSpec.onInvoke (webpack:///~/zone.js/dist/proxy.js:76:0 <- karma-shim.js:40651:39)
at ZoneDelegate.invoke (webpack:///~/zone.js/dist/zone.js:231:0 <- karma-shim.js:40989:32)
at Zone.run (webpack:///~/zone.js/dist/zone.js:114:0 <- karma-shim.js:40872:43)
at AsyncTestZoneSpec._finishCallback (webpack:///~/@angular/core/bundles/core-testing.umd.js:86:0 <- karma-shim.js:3514:29)
at webpack:///~/zone.js/dist/async-test.js:38:0 <- karma-shim.js:39948:31
at ZoneDelegate.invokeTask (webpack:///~/zone.js/dist/zone.js:265:0 <- karma-shim.js:41023:35)
at Zone.runTask (webpack:///~/zone.js/dist/zone.js:154:0 <- karma-shim.js:40912:47)
at ZoneTask.invoke (webpack:///~/zone.js/dist/zone.js:335:0 <- karma-shim.js:41093:33)
at data.args.(anonymous function) (webpack:///~/zone.js/dist/zone.js:970:0 <- karma-shim.js:41728:25)
OK, here is the source code of the files involved: I am starting of with a service providing Openlayers3 to the component
import {Injectable} from '@angular/core';
import * as ol from 'openlayers';
@Injectable()
export class OlService {
get(): any {
return ol;
}
}
Moving on to the component. (I removed the layers URLs)
import {Component, OnInit, Input} from '@angular/core';
import {OlService} from './ol.service';
@Component({
selector: 'my-map',
templateUrl: './ol.component.html',
styleUrls: ['./ol.component.scss'],
providers: [OlService]
})
export class OlComponent implements OnInit {
@Input() lnglat: [number, number];
@Input() zoom: number;
private map;
public layers = [];
private vectorSource;
constructor(private olService: OlService) {
}
createMap = () => {
let ol = this.olService.get();
this.vectorSource = new ol.source.Vector({});
// define layers
let OSM = new ol.layer.Tile({
source: new ol.source.OSM()
});
OSM.set('name', 'Openstreetmap');
let geography = new ol.layer.Tile({
source: new ol.source.TileJSON({
url: '',
crossOrigin: '',
}),
visible: false
});
geography.set('name', 'Geography');
let boundaries = new ol.layer.Tile({
opacity: 0.5,
source: new ol.source.TileWMS({
url: '',
params: {
'LAYERS': 'fwsys:fwsys_region',
'TILED': true,
'transparent': 'true',
'format': 'image/png'
},
serverType: 'geoserver',
projection: ol.proj.get('EPSG:3857')
})
});
boundaries.set('name', 'Boundaries');
let vector = new ol.layer.Vector({
source: this.vectorSource
});
this.map = new ol.Map({
target: 'map',
layers: [OSM, geography, vector, boundaries],
view: new ol.View({
center: ol.proj.fromLonLat(this.lnglat),
zoom: this.zoom,
projection: ol.proj.get('EPSG:3857')
})
});
let select_interaction = new ol.interaction.Select();
this.map.addInteraction(select_interaction);
// add popup for all features
let container = document.getElementById('popup');
let content = document.getElementById('popup-content');
let closer = document.getElementById('popup-closer');
let popup = new ol.Overlay({
element: container,
autoPan: true,
positioning: 'bottom-center',
stopEvent: false,
offset: [0, -5]
});
closer.onclick = function () {
popup.setPosition(undefined);
closer.blur();
return false;
};
this.map.addOverlay(popup);
this.map.on('click', (evt) => {
let feature = this.map.forEachFeatureAtPixel(evt.pixel, (feat) => {
return feat;
});
if (feature) {
let coordinate = evt.coordinate;
content.innerHTML = feature.get('name');
popup.setPosition(coordinate);
}
});
this.addLayerSwitcher([OSM, geography, boundaries]);
this.addMarker([174.76, -37.10], 'Close to Auckland', 'akl1');
this.addMarker([173.76, -37.10], 'Out in in waters', 'pacific1');
this.addPolygon([[174.76, -37.18], [176.76, -37.18], [176.76, -38.18], [174.76, -38.18]], 'Hamilton', 'id_hamilton');
};
addPolygon = (polygon: [[number, number]], name: string, id: string) => {
let ol = this.olService.get();
let projectedPolygon = [];
for (let poly of polygon) {
projectedPolygon.push(ol.proj.transform(poly, 'EPSG:4326', 'EPSG:3857'));
}
let p = new ol.geom.Polygon([projectedPolygon]);
let featurething = new ol.Feature({
name: name,
id: id,
geometry: p
});
this.vectorSource.addFeature(featurething);
};
addMarker = (coords: [number, number], name: string, id: string) => {
let ol = this.olService.get();
let iconFeature = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.transform(coords, 'EPSG:4326', 'EPSG:3857')),
name: name,
id: id,
});
let iconStyle = new ol.style.Style({
image: new ol.style.Icon(/** @type {olx.style.IconOptions} */ ({
opacity: 0.75,
anchor: [0.5, 1],
src: '//cdn4.iconfinder.com/data/icons/pictype-free-vector-icons/16/location-alt-32.png'
}))
});
iconFeature.setStyle(iconStyle);
this.vectorSource.addFeature(iconFeature);
}
addLayerSwitcher = (layers: [any]) => {
this.layers = layers;
}
toggleLayer = (layer, evt) => {
evt.target.blur();
if (layer.getVisible()) {
layer.setVisible(false);
} else {
layer.setVisible(true);
}
}
ngOnInit() {
this.createMap();
}
}