Organize tests

This commit is contained in:
Tim Schaub
2021-04-27 15:41:14 -07:00
parent 278e355795
commit 490cfabe91
599 changed files with 12374 additions and 1603 deletions
@@ -0,0 +1,94 @@
import {
getMapboxPath,
normalizeSourceUrl,
normalizeSpriteUrl,
normalizeStyleUrl,
} from '../../../../../src/ol/layer/MapboxVector.js';
describe('ol/layer/MapboxVector', () => {
describe('getMapboxPath()', () => {
const cases = [
{
url: 'mapbox://path/to/resource',
expected: 'path/to/resource',
},
{
url: 'mapbox://path/to/resource?query',
expected: 'path/to/resource?query',
},
{
url: 'https://example.com/resource',
expected: '',
},
];
for (const c of cases) {
it(`works for ${c.url}`, () => {
expect(getMapboxPath(c.url)).to.be(c.expected);
});
}
});
describe('normalizeStyleUrl()', () => {
const cases = [
{
url: 'mapbox://styles/mapbox/bright-v9',
expected:
'https://api.mapbox.com/styles/v1/mapbox/bright-v9?&access_token=test-token',
},
{
url: 'https://example.com/style',
expected: 'https://example.com/style',
},
];
const token = 'test-token';
for (const c of cases) {
it(`works for ${c.url}`, () => {
expect(normalizeStyleUrl(c.url, token)).to.be(c.expected);
});
}
});
describe('normalizeSpriteUrl()', () => {
const cases = [
{
url: 'mapbox://sprites/mapbox/bright-v9',
expected:
'https://api.mapbox.com/styles/v1/mapbox/bright-v9/sprite?access_token=test-token',
},
{
url: 'https://example.com/sprite',
expected: 'https://example.com/sprite',
},
];
const token = 'test-token';
for (const c of cases) {
it(`works for ${c.url}`, () => {
expect(normalizeSpriteUrl(c.url, token)).to.be(c.expected);
});
}
});
describe('normalizeSourceUrl()', () => {
const cases = [
{
url: 'mapbox://mapbox.mapbox-streets-v7',
expected:
'https://{a-d}.tiles.mapbox.com/v4/mapbox.mapbox-streets-v7/{z}/{x}/{y}.vector.pbf?access_token=test-token',
},
{
url: 'https://example.com/source/{z}/{x}/{y}.pbf',
expected: 'https://example.com/source/{z}/{x}/{y}.pbf',
},
];
const token = 'test-token';
for (const c of cases) {
it(`works for ${c.url}`, () => {
expect(normalizeSourceUrl(c.url, token)).to.be(c.expected);
});
}
});
});
+502
View File
@@ -0,0 +1,502 @@
import Collection from '../../../../../src/ol/Collection.js';
import Layer from '../../../../../src/ol/layer/Layer.js';
import LayerGroup from '../../../../../src/ol/layer/Group.js';
import Source from '../../../../../src/ol/source/Source.js';
import {assign} from '../../../../../src/ol/obj.js';
import {getIntersection} from '../../../../../src/ol/extent.js';
import {getUid} from '../../../../../src/ol/util.js';
describe('ol.layer.Group', function () {
describe('constructor (defaults)', function () {
let layerGroup;
beforeEach(function () {
layerGroup = new LayerGroup();
});
afterEach(function () {
layerGroup.dispose();
});
it('creates an instance', function () {
expect(layerGroup).to.be.a(LayerGroup);
});
it('provides default opacity', function () {
expect(layerGroup.getOpacity()).to.be(1);
});
it('provides default visibility', function () {
expect(layerGroup.getVisible()).to.be(true);
});
it('provides default layerState', function () {
expect(layerGroup.getLayerState()).to.eql({
layer: layerGroup,
opacity: 1,
visible: true,
managed: true,
sourceState: 'ready',
extent: undefined,
zIndex: 0,
maxResolution: Infinity,
minResolution: 0,
minZoom: -Infinity,
maxZoom: Infinity,
});
});
it('provides default empty layers collection', function () {
expect(layerGroup.getLayers()).to.be.a(Collection);
expect(layerGroup.getLayers().getLength()).to.be(0);
});
});
describe('generic change event', function () {
let layer, group, listener;
beforeEach(function () {
layer = new Layer({
source: new Source({
projection: 'EPSG:4326',
}),
});
group = new LayerGroup({
layers: [layer],
});
listener = sinon.spy();
});
afterEach(function () {
group.dispose();
layer.dispose();
});
it('is dispatched by the group when layer opacity changes', function () {
group.on('change', listener);
layer.setOpacity(0.5);
expect(listener.calledOnce).to.be(true);
});
it('is dispatched by the group when layer visibility changes', function () {
group.on('change', listener);
layer.setVisible(false);
expect(listener.callCount).to.be(1);
layer.setVisible(true);
expect(listener.callCount).to.be(2);
});
});
describe('property change event', function () {
let layer, group, listener;
beforeEach(function () {
layer = new Layer({
source: new Source({
projection: 'EPSG:4326',
}),
});
group = new LayerGroup({
layers: [layer],
});
listener = sinon.spy();
});
afterEach(function () {
group.dispose();
layer.dispose();
});
it('is dispatched by the group when group opacity changes', function () {
group.on('propertychange', listener);
group.setOpacity(0.5);
expect(listener.calledOnce).to.be(true);
});
it('is dispatched by the group when group visibility changes', function () {
group.on('propertychange', listener);
group.setVisible(false);
expect(listener.callCount).to.be(1);
group.setVisible(true);
expect(listener.callCount).to.be(2);
});
});
describe('constructor (options)', function () {
it('accepts options', function () {
const layer = new Layer({
source: new Source({
projection: 'EPSG:4326',
}),
});
const layerGroup = new LayerGroup({
layers: [layer],
opacity: 0.5,
visible: false,
zIndex: 10,
maxResolution: 500,
minResolution: 0.25,
minZoom: 1,
maxZoom: 10,
});
expect(layerGroup.getOpacity()).to.be(0.5);
expect(layerGroup.getVisible()).to.be(false);
expect(layerGroup.getMaxResolution()).to.be(500);
expect(layerGroup.getMinResolution()).to.be(0.25);
expect(layerGroup.getMinZoom()).to.be(1);
expect(layerGroup.getMaxZoom()).to.be(10);
expect(layerGroup.getLayerState()).to.eql({
layer: layerGroup,
opacity: 0.5,
visible: false,
managed: true,
sourceState: 'ready',
extent: undefined,
zIndex: 10,
maxResolution: 500,
minResolution: 0.25,
minZoom: 1,
maxZoom: 10,
});
expect(layerGroup.getLayers()).to.be.a(Collection);
expect(layerGroup.getLayers().getLength()).to.be(1);
expect(layerGroup.getLayers().item(0)).to.be(layer);
layer.dispose();
layerGroup.dispose();
});
it('accepts an extent option', function () {
const layer = new Layer({
source: new Source({
projection: 'EPSG:4326',
}),
});
const groupExtent = [-10, -5, 10, 5];
const layerGroup = new LayerGroup({
layers: [layer],
opacity: 0.5,
visible: false,
extent: groupExtent,
maxResolution: 500,
minResolution: 0.25,
});
expect(layerGroup.getOpacity()).to.be(0.5);
expect(layerGroup.getVisible()).to.be(false);
expect(layerGroup.getExtent()).to.eql(groupExtent);
expect(layerGroup.getMaxResolution()).to.be(500);
expect(layerGroup.getMinResolution()).to.be(0.25);
expect(layerGroup.getLayerState()).to.eql({
layer: layerGroup,
opacity: 0.5,
visible: false,
managed: true,
sourceState: 'ready',
extent: groupExtent,
zIndex: 0,
maxResolution: 500,
minResolution: 0.25,
minZoom: -Infinity,
maxZoom: Infinity,
});
expect(layerGroup.getLayers()).to.be.a(Collection);
expect(layerGroup.getLayers().getLength()).to.be(1);
expect(layerGroup.getLayers().item(0)).to.be(layer);
layer.dispose();
layerGroup.dispose();
});
});
describe('#getLayerState', function () {
let layerGroup;
beforeEach(function () {
layerGroup = new LayerGroup();
});
afterEach(function () {
layerGroup.dispose();
});
it('returns a layerState from the properties values', function () {
layerGroup.setOpacity(0.3);
layerGroup.setVisible(false);
layerGroup.setZIndex(10);
const groupExtent = [-100, 50, 100, 50];
layerGroup.setExtent(groupExtent);
layerGroup.setMaxResolution(500);
layerGroup.setMinResolution(0.25);
layerGroup.setMinZoom(5);
layerGroup.setMaxZoom(10);
expect(layerGroup.getLayerState()).to.eql({
layer: layerGroup,
opacity: 0.3,
visible: false,
managed: true,
sourceState: 'ready',
extent: groupExtent,
zIndex: 10,
maxResolution: 500,
minResolution: 0.25,
minZoom: 5,
maxZoom: 10,
});
});
it('returns a layerState with clamped values', function () {
layerGroup.setOpacity(-1.5);
layerGroup.setVisible(false);
expect(layerGroup.getLayerState()).to.eql({
layer: layerGroup,
opacity: 0,
visible: false,
managed: true,
sourceState: 'ready',
extent: undefined,
zIndex: 0,
maxResolution: Infinity,
minResolution: 0,
minZoom: -Infinity,
maxZoom: Infinity,
});
layerGroup.setOpacity(3);
layerGroup.setVisible(true);
expect(layerGroup.getLayerState()).to.eql({
layer: layerGroup,
opacity: 1,
visible: true,
managed: true,
sourceState: 'ready',
extent: undefined,
zIndex: 0,
maxResolution: Infinity,
minResolution: 0,
minZoom: -Infinity,
maxZoom: Infinity,
});
});
});
describe('layers events', function () {
it('listen / unlisten for layers added to the collection', function () {
const layers = new Collection();
const layerGroup = new LayerGroup({
layers: layers,
});
expect(Object.keys(layerGroup.listenerKeys_).length).to.eql(0);
const layer = new Layer({});
layers.push(layer);
expect(Object.keys(layerGroup.listenerKeys_).length).to.eql(1);
const listeners = layerGroup.listenerKeys_[getUid(layer)];
expect(listeners.length).to.eql(2);
expect(typeof listeners[0]).to.be('object');
expect(typeof listeners[1]).to.be('object');
// remove the layer from the group
layers.pop();
expect(Object.keys(layerGroup.listenerKeys_).length).to.eql(0);
expect(listeners[0].listener).to.be(undefined);
expect(listeners[1].listener).to.be(undefined);
});
});
describe('#setLayers', function () {
it('sets layers property', function () {
const layer = new Layer({
source: new Source({
projection: 'EPSG:4326',
}),
});
const layers = new Collection([layer]);
const layerGroup = new LayerGroup();
layerGroup.setLayers(layers);
expect(layerGroup.getLayers()).to.be(layers);
layerGroup.dispose();
layer.dispose();
layers.dispose();
});
});
describe('#getLayerStatesArray', function () {
let layer1, layer2, layer3;
beforeEach(function () {
layer1 = new Layer({
source: new Source({
projection: 'EPSG:4326',
}),
});
layer2 = new Layer({
source: new Source({
projection: 'EPSG:4326',
}),
opacity: 0.5,
visible: false,
maxResolution: 500,
minResolution: 0.25,
});
layer3 = new Layer({
source: new Source({
projection: 'EPSG:4326',
}),
extent: [-5, -2, 5, 2],
});
});
afterEach(function () {
layer1.dispose();
layer2.dispose();
layer3.dispose();
});
it('returns an empty array if no layer', function () {
const layerGroup = new LayerGroup();
const layerStatesArray = layerGroup.getLayerStatesArray();
expect(layerStatesArray).to.be.a(Array);
expect(layerStatesArray.length).to.be(0);
layerGroup.dispose();
});
it('does not transform layerStates by default', function () {
const layerGroup = new LayerGroup({
layers: [layer1, layer2],
});
const layerStatesArray = layerGroup.getLayerStatesArray();
expect(layerStatesArray).to.be.a(Array);
expect(layerStatesArray.length).to.be(2);
expect(layerStatesArray[0]).to.eql(layer1.getLayerState());
// layer state should match except for layer reference
const layerState = assign({}, layerStatesArray[0]);
delete layerState.layer;
const groupState = assign({}, layerGroup.getLayerState());
delete groupState.layer;
expect(layerState).to.eql(groupState);
expect(layerStatesArray[1]).to.eql(layer2.getLayerState());
layerGroup.dispose();
});
it('uses the layer group extent if layer has no extent', function () {
const groupExtent = [-10, -5, 10, 5];
const layerGroup = new LayerGroup({
extent: groupExtent,
layers: [layer1],
});
const layerStatesArray = layerGroup.getLayerStatesArray();
expect(layerStatesArray[0].extent).to.eql(groupExtent);
layerGroup.dispose();
});
it('uses the intersection of group and child extent', function () {
const groupExtent = [-10, -5, 10, 5];
const layerGroup = new LayerGroup({
extent: groupExtent,
layers: [layer3],
});
const layerStatesArray = layerGroup.getLayerStatesArray();
expect(layerStatesArray[0].extent).to.eql(
getIntersection(layer3.getExtent(), groupExtent)
);
layerGroup.dispose();
});
it('transforms layerStates correctly', function () {
const layerGroup = new LayerGroup({
layers: [layer1, layer2],
opacity: 0.5,
visible: false,
maxResolution: 150,
minResolution: 0.2,
});
const layerStatesArray = layerGroup.getLayerStatesArray();
// compare layer state to group state
// layer state should match except for layer reference
let layerState = assign({}, layerStatesArray[0]);
delete layerState.layer;
const groupState = assign({}, layerGroup.getLayerState());
delete groupState.layer;
expect(layerState).to.eql(groupState);
// layer state should be transformed (and we ignore layer reference)
layerState = assign({}, layerStatesArray[1]);
delete layerState.layer;
expect(layerState).to.eql({
opacity: 0.25,
visible: false,
managed: true,
sourceState: 'ready',
extent: undefined,
zIndex: 0,
maxResolution: 150,
minResolution: 0.25,
minZoom: -Infinity,
maxZoom: Infinity,
});
layerGroup.dispose();
});
it('returns max minZoom', function () {
const group = new LayerGroup({
minZoom: 5,
layers: [
new Layer({
source: new Source({
projection: 'EPSG:4326',
}),
}),
new Layer({
source: new Source({
projection: 'EPSG:4326',
}),
minZoom: 10,
}),
],
});
expect(group.getLayerStatesArray()[0].minZoom).to.be(5);
expect(group.getLayerStatesArray()[1].minZoom).to.be(10);
});
it('returns min maxZoom of layers', function () {
const group = new LayerGroup({
maxZoom: 5,
layers: [
new Layer({
source: new Source({
projection: 'EPSG:4326',
}),
}),
new Layer({
source: new Source({
projection: 'EPSG:4326',
}),
maxZoom: 2,
}),
],
});
expect(group.getLayerStatesArray()[0].maxZoom).to.be(5);
expect(group.getLayerStatesArray()[1].maxZoom).to.be(2);
});
});
});
@@ -0,0 +1,97 @@
import Feature from '../../../../../src/ol/Feature.js';
import HeatmapLayer from '../../../../../src/ol/layer/Heatmap.js';
import Map from '../../../../../src/ol/Map.js';
import Point from '../../../../../src/ol/geom/Point.js';
import VectorSource from '../../../../../src/ol/source/Vector.js';
import View from '../../../../../src/ol/View.js';
describe('ol.layer.Heatmap', function () {
describe('constructor', function () {
it('can be constructed without arguments', function () {
const instance = new HeatmapLayer();
expect(instance).to.be.an(HeatmapLayer);
});
it('has a default className', function () {
const layer = new HeatmapLayer({
source: new VectorSource(),
});
const canvas = layer.getRenderer().helper.getCanvas();
expect(canvas.className).to.eql('ol-layer');
});
it('accepts a custom className', function () {
const layer = new HeatmapLayer({
source: new VectorSource(),
className: 'a-class-name',
});
const canvas = layer.getRenderer().helper.getCanvas();
expect(canvas.className).to.eql('a-class-name');
});
});
describe('hit detection', function () {
it('hit detects two distinct features', function (done) {
const target = document.createElement('div');
target.style.width = '300px';
target.style.height = '300px';
document.body.appendChild(target);
const feature = new Feature({
geometry: new Point([0, 0]),
id: 1,
weight: 10,
});
const feature2 = new Feature({
geometry: new Point([14, 14]),
id: 2,
weight: 10,
});
const source = new VectorSource({
features: [feature, feature2],
});
const layer = new HeatmapLayer({
source: source,
blur: 10,
radius: 10,
});
const map = new Map({
layers: [layer],
view: new View({
center: [0, 0],
resolution: 0.1,
}),
target: target,
});
map.render();
function hitTest(coordinate) {
const features = map.getFeaturesAtPixel(
map.getPixelFromCoordinate(coordinate)
);
return features.length ? features[0] : null;
}
const renderer = layer.getRenderer();
renderer.worker_.addEventListener('message', function (event) {
if (!renderer.hitRenderInstructions_) {
return;
}
map.renderSync();
let res;
res = hitTest([0, 0]);
expect(res).to.be(feature);
res = hitTest([20, 0]);
expect(res).to.be(null);
res = hitTest([14, 14]);
expect(res).to.be(feature2);
res = hitTest([0, 14]);
expect(res).to.be(null);
document.body.removeChild(target);
done();
});
});
});
});
+692
View File
@@ -0,0 +1,692 @@
import Layer, {inView} from '../../../../../src/ol/layer/Layer.js';
import Map from '../../../../../src/ol/Map.js';
import RenderEvent from '../../../../../src/ol/render/Event.js';
import Source from '../../../../../src/ol/source/Source.js';
import {get as getProjection} from '../../../../../src/ol/proj.js';
describe('ol.layer.Layer', function () {
describe('constructor (defaults)', function () {
let layer;
beforeEach(function () {
layer = new Layer({
source: new Source({
projection: getProjection('EPSG:4326'),
}),
});
});
afterEach(function () {
layer.dispose();
});
it('creates an instance', function () {
expect(layer).to.be.a(Layer);
});
it('provides default opacity', function () {
expect(layer.getOpacity()).to.be(1);
});
it('provides default visibility', function () {
expect(layer.getVisible()).to.be(true);
});
it('provides default max resolution', function () {
expect(layer.getMaxResolution()).to.be(Infinity);
});
it('provides default min resolution', function () {
expect(layer.getMinResolution()).to.be(0);
});
it('provides default min zoom', function () {
expect(layer.getMinZoom()).to.be(-Infinity);
});
it('provides default max zoom', function () {
expect(layer.getMaxZoom()).to.be(Infinity);
});
it('provides default layerState', function () {
expect(layer.getLayerState()).to.eql({
layer: layer,
opacity: 1,
visible: true,
managed: true,
sourceState: 'ready',
extent: undefined,
zIndex: 0,
maxResolution: Infinity,
minResolution: 0,
minZoom: -Infinity,
maxZoom: Infinity,
});
});
});
describe('constructor (options)', function () {
it('accepts options', function () {
const layer = new Layer({
source: new Source({
projection: getProjection('EPSG:4326'),
}),
opacity: 0.5,
visible: false,
zIndex: 10,
maxResolution: 500,
minResolution: 0.25,
minZoom: 1,
maxZoom: 10,
foo: 42,
});
expect(layer.getOpacity()).to.be(0.5);
expect(layer.getVisible()).to.be(false);
expect(layer.getMaxResolution()).to.be(500);
expect(layer.getMinResolution()).to.be(0.25);
expect(layer.getMinZoom()).to.be(1);
expect(layer.getMaxZoom()).to.be(10);
expect(layer.get('foo')).to.be(42);
expect(layer.getLayerState()).to.eql({
layer: layer,
opacity: 0.5,
visible: false,
managed: true,
sourceState: 'ready',
extent: undefined,
zIndex: 10,
maxResolution: 500,
minResolution: 0.25,
minZoom: 1,
maxZoom: 10,
});
layer.dispose();
});
it('throws on non-numeric opacity', function () {
function create() {
new Layer({
source: new Source({
projection: 'EPSG:4326',
}),
opacity: 'foo',
});
}
expect(create).to.throwException();
});
it('accepts a custom render function', function () {
let called = false;
const layer = new Layer({
render: function () {
called = true;
},
});
layer.render();
expect(called).to.eql(true);
});
});
describe('inView', function () {
let layer;
beforeEach(function () {
layer = new Layer({
source: new Source({
projection: getProjection('EPSG:4326'),
}),
});
});
afterEach(function () {
layer.dispose();
});
const cases = [
{
when: 'layer is not visible',
visible: false,
view: {
resolution: 4,
zoom: 4,
},
inView: false,
},
{
when: 'layer is not visible (with min/max zoom and resolution)',
visible: false,
minZoom: 2,
maxZoom: 6,
minResolution: 2,
maxResolution: 6,
view: {
resolution: 4,
zoom: 4,
},
inView: false,
},
{
when: 'view zoom is less than minZoom',
minZoom: 2,
view: {
resolution: 1,
zoom: 1,
},
inView: false,
},
{
when: 'view zoom is less than minZoom (with maxZoom)',
minZoom: 2,
maxZoom: 4,
view: {
resolution: 1,
zoom: 1,
},
inView: false,
},
{
when: 'view zoom is equal to minZoom',
minZoom: 2,
view: {
resolution: 2,
zoom: 2,
},
inView: false,
},
{
when: 'view zoom is equal to minZoom (with maxZoom)',
minZoom: 2,
maxZoom: 4,
view: {
resolution: 2,
zoom: 2,
},
inView: false,
},
{
when: 'view zoom is greater than minZoom',
minZoom: 2,
view: {
resolution: 3,
zoom: 3,
},
inView: true,
},
{
when: 'view zoom is greater than minZoom (with maxZoom)',
minZoom: 2,
maxZoom: 4,
view: {
resolution: 3,
zoom: 3,
},
inView: true,
},
{
when: 'view zoom is equal to maxZoom',
maxZoom: 4,
view: {
resolution: 4,
zoom: 4,
},
inView: true,
},
{
when: 'view zoom is equal to maxZoom (with minZoom)',
minZoom: 2,
maxZoom: 4,
view: {
resolution: 4,
zoom: 4,
},
inView: true,
},
{
when: 'view zoom is greater than maxZoom',
maxZoom: 4,
view: {
resolution: 5,
zoom: 5,
},
inView: false,
},
{
when: 'view zoom is greater than maxZoom (with minZoom)',
minZoom: 2,
maxZoom: 4,
view: {
resolution: 5,
zoom: 5,
},
inView: false,
},
{
when: 'view resolution is less than minResolution',
minResolution: 2,
view: {
resolution: 1,
zoom: 1,
},
inView: false,
},
{
when: 'view resolution is less than minResolution (with maxResolution)',
minResolution: 2,
maxResolution: 4,
view: {
resolution: 1,
zoom: 1,
},
inView: false,
},
{
when: 'view resolution is equal to minResolution',
minResolution: 2,
view: {
resolution: 2,
zoom: 2,
},
inView: true,
},
{
when: 'view resolution is equal to minResolution (with maxResolution)',
minResolution: 2,
maxResolution: 4,
view: {
resolution: 2,
zoom: 2,
},
inView: true,
},
{
when: 'view resolution is greater than minResolution',
minResolution: 2,
view: {
resolution: 3,
zoom: 3,
},
inView: true,
},
{
when:
'view resolution is greater than minResolution (with maxResolution)',
minResolution: 2,
maxResolution: 4,
view: {
resolution: 3,
zoom: 3,
},
inView: true,
},
{
when: 'view resolution is equal to maxResolution',
maxResolution: 4,
view: {
resolution: 4,
zoom: 4,
},
inView: false,
},
{
when: 'view resolution is equal to maxResolution (with minResolution)',
minResolution: 2,
maxResolution: 4,
view: {
resolution: 4,
zoom: 4,
},
inView: false,
},
{
when: 'view resolution is greater than maxResolution',
maxResolution: 4,
view: {
resolution: 5,
zoom: 5,
},
inView: false,
},
{
when:
'view resolution is greater than maxResolution (with minResolution)',
minResolution: 2,
maxResolution: 4,
view: {
resolution: 5,
zoom: 5,
},
inView: false,
},
];
cases.forEach(function (c, i) {
it('returns ' + c.inView + ' when ' + c.when, function () {
if ('visible' in c) {
layer.setVisible(c.visible);
}
if ('minZoom' in c) {
layer.setMinZoom(c.minZoom);
}
if ('maxZoom' in c) {
layer.setMaxZoom(c.maxZoom);
}
if ('minResolution' in c) {
layer.setMinResolution(c.minResolution);
}
if ('maxResolution' in c) {
layer.setMaxResolution(c.maxResolution);
}
const layerState = layer.getLayerState();
expect(inView(layerState, c.view)).to.be(c.inView);
});
});
});
describe('#getLayerState', function () {
let layer;
beforeEach(function () {
layer = new Layer({
source: new Source({
projection: getProjection('EPSG:4326'),
}),
});
});
afterEach(function () {
layer.dispose();
});
it('returns a layerState from the properties values', function () {
layer.setOpacity(1 / 3);
layer.setVisible(false);
layer.setMaxResolution(500);
layer.setMinResolution(0.25);
layer.setZIndex(10);
expect(layer.getLayerState()).to.eql({
layer: layer,
opacity: 0.33,
visible: false,
managed: true,
sourceState: 'ready',
extent: undefined,
zIndex: 10,
maxResolution: 500,
minResolution: 0.25,
minZoom: -Infinity,
maxZoom: Infinity,
});
});
it('returns a layerState with clamped values', function () {
layer.setOpacity(-1.5);
layer.setVisible(false);
let state = layer.getLayerState();
expect(state.opacity).to.be(0);
expect(state.visible).to.be(false);
layer.setOpacity(3);
layer.setVisible(true);
state = layer.getLayerState();
expect(state.opacity).to.be(1);
expect(state.visible).to.be(true);
});
});
describe('#getSource', function () {
it('gets the layer source', function () {
const source = new Source({projection: getProjection('EPSG:4326')});
const layer = new Layer({source: source});
expect(layer.getSource()).to.be(source);
});
});
describe('#set("source", source)', function () {
const projection = getProjection('EPSG:4326');
it('sets the layer source', function () {
const layer = new Layer({
source: new Source({projection: projection}),
});
const source = new Source({projection: projection});
layer.set('source', source);
expect(layer.getSource()).to.be(source);
});
it('calls changed', function () {
const layer = new Layer({
source: new Source({projection: projection}),
});
sinon.spy(layer, 'changed');
const source = new Source({projection: projection});
layer.set('source', source);
expect(layer.changed.calledOnce).to.be(true);
});
it('sets up event listeners', function () {
sinon.spy(Layer.prototype, 'handleSourceChange_');
const first = new Source({projection: projection});
const layer = new Layer({source: first});
first.setState('ready');
expect(layer.handleSourceChange_.calledOnce).to.be(true);
const second = new Source({projection: projection});
layer.set('source', second);
expect(layer.handleSourceChange_.calledOnce).to.be(true);
second.setState('ready');
expect(layer.handleSourceChange_.callCount).to.be(2);
// remove spy
Layer.prototype.handleSourceChange_.restore();
});
});
describe('#setSource()', function () {
const projection = getProjection('EPSG:4326');
it('sets the layer source', function () {
const layer = new Layer({
source: new Source({projection: projection}),
});
const source = new Source({projection: projection});
layer.setSource(source);
expect(layer.getSource()).to.be(source);
});
it('calls changed', function () {
const layer = new Layer({
source: new Source({projection: projection}),
});
sinon.spy(layer, 'changed');
const source = new Source({projection: projection});
layer.setSource(source);
expect(layer.changed.calledOnce).to.be(true);
});
it('sets up event listeners', function () {
sinon.spy(Layer.prototype, 'handleSourceChange_');
const first = new Source({projection: projection});
const layer = new Layer({source: first});
first.setState('ready');
expect(layer.handleSourceChange_.calledOnce).to.be(true);
const second = new Source({projection: projection});
layer.setSource(second);
expect(layer.handleSourceChange_.calledOnce).to.be(true);
second.setState('ready');
expect(layer.handleSourceChange_.callCount).to.be(2);
// remove spy
Layer.prototype.handleSourceChange_.restore();
});
});
describe('#setOpacity', function () {
let layer;
beforeEach(function () {
layer = new Layer({
source: new Source({
projection: getProjection('EPSG:4326'),
}),
});
});
afterEach(function () {
layer.dispose();
});
it('accepts a positive number', function () {
layer.setOpacity(0.3);
expect(layer.getOpacity()).to.be(0.3);
});
it('throws on types other than number', function () {
function set() {
layer.setOpacity('foo');
}
expect(set).to.throwException();
});
it('triggers a change event', function () {
const listener = sinon.spy();
layer.on('propertychange', listener);
layer.setOpacity(0.4);
expect(listener.calledOnce).to.be(true);
});
});
describe('#setVisible', function () {
let layer;
beforeEach(function () {
layer = new Layer({
source: new Source({
projection: getProjection('EPSG:4326'),
}),
});
});
afterEach(function () {
layer.dispose();
});
it('sets visible property', function () {
layer.setVisible(false);
expect(layer.getVisible()).to.be(false);
layer.setVisible(true);
expect(layer.getVisible()).to.be(true);
});
it('fires a change event', function () {
const listener = sinon.spy();
layer.on('propertychange', listener);
layer.setVisible(false);
expect(listener.callCount).to.be(1);
layer.setVisible(true);
expect(listener.callCount).to.be(2);
});
});
describe('#setMap (unmanaged layer)', function () {
let map;
beforeEach(function () {
map = new Map({});
});
describe('with map in constructor options', function () {
it('renders the layer', function () {
const layer = new Layer({
map: map,
});
const frameState = {
layerStatesArray: [],
};
map.dispatchEvent(
new RenderEvent('precompose', null, frameState, null)
);
expect(frameState.layerStatesArray.length).to.be(1);
const layerState = frameState.layerStatesArray[0];
expect(layerState.layer).to.equal(layer);
});
});
describe('setMap sequences', function () {
let mapRenderSpy;
beforeEach(function () {
mapRenderSpy = sinon.spy(map, 'render');
});
afterEach(function () {
mapRenderSpy.restore();
});
it('requests a render frame', function () {
const layer = new Layer({});
layer.setMap(map);
expect(mapRenderSpy.callCount).to.be(1);
layer.setMap(null);
expect(mapRenderSpy.callCount).to.be(2);
layer.setMap(map);
expect(mapRenderSpy.callCount).to.be(3);
});
});
describe('zIndex for unmanaged layers', function () {
let frameState, layer;
beforeEach(function () {
layer = new Layer({
map: map,
});
frameState = {
layerStatesArray: [],
};
});
afterEach(function () {
layer.setMap(null);
});
it('has Infinity as zIndex when not configured otherwise', function () {
map.dispatchEvent(
new RenderEvent('precompose', null, frameState, null)
);
const layerState = frameState.layerStatesArray[0];
expect(layerState.zIndex).to.be(Infinity);
});
it('respects the configured zIndex', function () {
[-5, 0, 42].forEach((index) => {
layer.setZIndex(index);
map.dispatchEvent(
new RenderEvent('precompose', null, frameState, null)
);
const layerState = frameState.layerStatesArray[0];
frameState.layerStatesArray.length = 0;
expect(layerState.zIndex).to.be(index);
});
});
});
});
});
+101
View File
@@ -0,0 +1,101 @@
import TileLayer from '../../../../../src/ol/layer/Tile.js';
import {Map, View} from '../../../../../src/ol/index.js';
import {OSM, XYZ} from '../../../../../src/ol/source.js';
describe('ol.layer.Tile', function () {
describe('constructor (defaults)', function () {
let layer;
beforeEach(function () {
layer = new TileLayer({
source: new OSM(),
});
});
afterEach(function () {
layer.dispose();
});
it('creates an instance', function () {
expect(layer).to.be.a(TileLayer);
});
it('provides default preload', function () {
expect(layer.getPreload()).to.be(0);
});
it('provides default useInterimTilesOnError', function () {
expect(layer.getUseInterimTilesOnError()).to.be(true);
});
});
describe('frameState.animate after tile transition with layer opacity', function () {
let target, map;
beforeEach(function (done) {
target = document.createElement('div');
Object.assign(target.style, {
position: 'absolute',
left: '-1000px',
top: '-1000px',
width: '256px',
height: '256px',
});
document.body.appendChild(target);
map = new Map({
target: target,
view: new View({center: [0, 0], zoom: 1}),
});
map.once('rendercomplete', function () {
done();
});
});
afterEach(function () {
map.dispose();
document.body.removeChild(target);
});
it('sets frameState.animate to false when opacity is 1', function (done) {
let lastFrameState;
const layer = new TileLayer({
opacity: 1,
source: new XYZ({
url: 'spec/ol/data/osm-0-0-0.png',
}),
});
layer.on('postrender', function (event) {
lastFrameState = event.frameState;
});
map.once('rendercomplete', function () {
expect(lastFrameState.animate).to.be(false);
done();
});
map.addLayer(layer);
});
it('sets frameState.animate to false when opacity is 0.5', function (done) {
let lastFrameState;
const layer = new TileLayer({
opacity: 0.5,
source: new XYZ({
url: 'spec/ol/data/osm-0-0-0.png',
}),
});
layer.on('postrender', function (event) {
lastFrameState = event.frameState;
});
map.once('rendercomplete', function () {
expect(lastFrameState.animate).to.be(false);
done();
});
map.addLayer(layer);
});
});
});
+229
View File
@@ -0,0 +1,229 @@
import Feature from '../../../../../src/ol/Feature.js';
import ImageStyle from '../../../../../src/ol/style/Image.js';
import Layer from '../../../../../src/ol/layer/Layer.js';
import LineString from '../../../../../src/ol/geom/LineString.js';
import Map from '../../../../../src/ol/Map.js';
import Point from '../../../../../src/ol/geom/Point.js';
import Stroke from '../../../../../src/ol/style/Stroke.js';
import Style, {createDefaultStyle} from '../../../../../src/ol/style/Style.js';
import VectorLayer from '../../../../../src/ol/layer/Vector.js';
import VectorSource from '../../../../../src/ol/source/Vector.js';
import View from '../../../../../src/ol/View.js';
describe('ol.layer.Vector', function () {
describe('constructor', function () {
const source = new VectorSource();
const style = new Style();
it('creates a new layer', function () {
const layer = new VectorLayer({source: source});
expect(layer).to.be.a(VectorLayer);
expect(layer).to.be.a(Layer);
});
it('accepts a style option with a single style', function () {
const layer = new VectorLayer({
source: source,
style: style,
});
const styleFunction = layer.getStyleFunction();
expect(styleFunction()).to.eql([style]);
});
it('accepts a style option with an array of styles', function () {
const layer = new VectorLayer({
source: source,
style: [style],
});
const styleFunction = layer.getStyleFunction();
expect(styleFunction()).to.eql([style]);
});
it('accepts a style option with a style function', function () {
const layer = new VectorLayer({
source: source,
style: function (feature, resolution) {
return [style];
},
});
const styleFunction = layer.getStyleFunction();
expect(styleFunction()).to.eql([style]);
});
});
describe('#setStyle()', function () {
let layer, style;
beforeEach(function () {
layer = new VectorLayer({
source: new VectorSource(),
});
style = new Style();
});
it('allows the style to be set after construction', function () {
layer.setStyle(style);
expect(layer.getStyle()).to.be(style);
});
it('dispatches the change event', function (done) {
layer.on('change', function () {
done();
});
layer.setStyle(style);
});
it('updates the internal style function', function () {
expect(layer.getStyleFunction()).to.be(createDefaultStyle);
layer.setStyle(style);
expect(layer.getStyleFunction()).not.to.be(createDefaultStyle);
});
it('allows setting an null style', function () {
layer.setStyle(null);
expect(layer.getStyle()).to.be(null);
expect(layer.getStyleFunction()).to.be(undefined);
});
it('sets the default style when passing undefined', function () {
layer.setStyle(style);
layer.setStyle(undefined);
expect(layer.getStyle()).to.be(createDefaultStyle);
expect(layer.getStyleFunction()).to.be(createDefaultStyle);
});
});
describe('#getStyle()', function () {
const source = new VectorSource();
const style = new Style();
it('returns what is provided to setStyle', function () {
const layer = new VectorLayer({
source: source,
});
expect(layer.getStyle()).to.be(createDefaultStyle);
layer.setStyle(style);
expect(layer.getStyle()).to.be(style);
layer.setStyle([style]);
expect(layer.getStyle()).to.eql([style]);
const styleFunction = function (feature, resolution) {
return [style];
};
layer.setStyle(styleFunction);
expect(layer.getStyle()).to.be(styleFunction);
});
});
describe('#getFeatures()', function () {
let map;
beforeEach(function () {
const container = document.createElement('div');
container.style.width = '256px';
container.style.height = '256px';
document.body.appendChild(container);
map = new Map({
target: container,
view: new View({
zoom: 2,
center: [0, 0],
}),
});
});
afterEach(function () {
document.body.removeChild(map.getTargetElement());
map.setTarget(null);
});
it('detects features properly', function (done) {
const source = new VectorSource({
features: [
new Feature({
geometry: new Point([-1000000, 0]),
name: 'feature1',
}),
new Feature({
geometry: new Point([1000000, 0]),
name: 'feature2',
}),
],
});
const feature = new Feature({
geometry: new Point([-1000000, 0]),
name: 'feature with no size',
});
const testImage = new ImageStyle({
opacity: 1,
displacement: [],
});
testImage.getImageState = () => {};
testImage.listenImageChange = () => {};
testImage.getImageSize = () => {};
feature.setStyle([
new Style({
image: testImage,
}),
]);
source.addFeature(feature);
const layer = new VectorLayer({
source,
});
map.addLayer(layer);
map.renderSync();
const pixel = map.getPixelFromCoordinate([-1000000, 0]);
layer.getFeatures(pixel).then(function (features) {
expect(features.length).to.equal(1);
expect(features[0].get('name')).to.be('feature1');
done();
});
});
it('hits lines even if they are dashed', function (done) {
const geometry = new LineString([
[-1e6, 0],
[1e6, 0],
]);
const feature = new Feature(geometry);
const layer = new VectorLayer({
source: new VectorSource({
features: [feature],
}),
style: new Style({
stroke: new Stroke({
color: 'black',
width: 8,
lineDash: [10, 20],
}),
}),
});
map.addLayer(layer);
map.renderSync();
const pixel = map.getPixelFromCoordinate([0, 0]);
layer
.getFeatures(pixel)
.then(function (features) {
expect(features.length).to.equal(1);
expect(features[0]).to.be(feature);
done();
}, done)
.catch(done);
});
});
});
@@ -0,0 +1,55 @@
import Feature from '../../../../../src/ol/Feature.js';
import Map from '../../../../../src/ol/Map.js';
import Point from '../../../../../src/ol/geom/Point.js';
import VectorImageLayer from '../../../../../src/ol/layer/VectorImage.js';
import VectorSource from '../../../../../src/ol/source/Vector.js';
import View from '../../../../../src/ol/View.js';
describe('ol/layer/VectorImage', function () {
describe('#getFeatures()', function () {
let map, layer;
beforeEach(function () {
layer = new VectorImageLayer({
source: new VectorSource({
features: [
new Feature({
geometry: new Point([-1000000, 0]),
name: 'feature1',
}),
new Feature({
geometry: new Point([1000000, 0]),
name: 'feture2',
}),
],
}),
});
const container = document.createElement('div');
container.style.width = '256px';
container.style.height = '256px';
document.body.appendChild(container);
map = new Map({
target: container,
layers: [layer],
view: new View({
zoom: 2,
center: [0, 0],
}),
});
});
afterEach(function () {
document.body.removeChild(map.getTargetElement());
map.setTarget(null);
});
it('detects features properly', function (done) {
map.renderSync();
const pixel = map.getPixelFromCoordinate([-1000000, 0]);
layer.getFeatures(pixel).then(function (features) {
expect(features[0].get('name')).to.be('feature1');
done();
});
});
});
});
@@ -0,0 +1,153 @@
import GeoJSON from '../../../../../src/ol/format/GeoJSON.js';
import Map from '../../../../../src/ol/Map.js';
import VectorTileLayer from '../../../../../src/ol/layer/VectorTile.js';
import VectorTileSource from '../../../../../src/ol/source/VectorTile.js';
import View from '../../../../../src/ol/View.js';
import {fromLonLat} from '../../../../../src/ol/proj.js';
describe('ol.layer.VectorTile', function () {
describe('constructor (defaults)', function () {
let layer;
beforeEach(function () {
layer = new VectorTileLayer({
source: new VectorTileSource({}),
});
});
afterEach(function () {
layer.dispose();
});
it('creates an instance', function () {
expect(layer).to.be.a(VectorTileLayer);
});
it('provides default preload', function () {
expect(layer.getPreload()).to.be(0);
});
it('provides default useInterimTilesOnError', function () {
expect(layer.getUseInterimTilesOnError()).to.be(true);
});
it('provides default renderMode', function () {
expect(layer.getRenderMode()).to.be('hybrid');
});
});
describe('constructor (options)', function () {
it('works with options', function () {
let layer = new VectorTileLayer({
renderMode: 'hybrid',
source: new VectorTileSource({}),
});
expect(layer.getRenderMode()).to.be('hybrid');
expect(function () {
layer = new VectorTileLayer({
renderMode: 'foo',
source: new VectorTileSource({}),
});
}).to.throwException();
});
});
describe('#getFeatures()', function () {
let map, layer;
beforeEach(function () {
layer = new VectorTileLayer({
source: new VectorTileSource({
format: new GeoJSON(),
url: `data:application/json;charset=utf-8,
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-36, 0]
},
"properties": {
"name": "feature1"
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [36, 0]
},
"properties": {
"name": "feature2"
}
}
]
}
`,
}),
});
const container = document.createElement('div');
container.style.width = '256px';
container.style.height = '256px';
document.body.appendChild(container);
map = new Map({
target: container,
layers: [layer],
view: new View({
zoom: 0,
center: [0, 0],
}),
});
});
afterEach(function () {
document.body.removeChild(map.getTargetElement());
map.setTarget(null);
});
it('detects features properly', function (done) {
map.once('rendercomplete', function () {
const pixel = map.getPixelFromCoordinate(fromLonLat([-36, 0]));
layer
.getFeatures(pixel)
.then(function (features) {
expect(features[0].get('name')).to.be('feature1');
done();
})
.catch(done);
});
});
it('does not give false positives', function (done) {
map.once('rendercomplete', function () {
const pixel = map.getPixelFromCoordinate(fromLonLat([0, 0]));
layer
.getFeatures(pixel)
.then(function (features) {
expect(features.length).to.be(0);
done();
})
.catch(done);
});
});
it('stores separate hit detection data for each layer that uses the source', function (done) {
const layer2 = new VectorTileLayer({
source: layer.getSource(),
});
map.addLayer(layer2);
map.once('rendercomplete', function () {
const pixel = map.getPixelFromCoordinate(fromLonLat([-36, 0]));
Promise.all([layer.getFeatures(pixel), layer2.getFeatures(pixel)])
.then(function (result) {
const tile = layer.getSource().tileCache.get('0/0/0');
expect(Object.keys(tile.hitDetectionImageData).length).to.be(2);
done();
})
.catch(done);
});
});
});
});